Riverpod: List provider is not rebuilding

Issue

Flutter riverpod is not notifying the Consumer on the state change when the StateNotifier‘s type is List, while the same implementation works just fine for other types.

here, I provided a minimal reproducable example:

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      child: MaterialApp(
        home: MyHomePage(),
      ),
    );
  }
}

class CounterState extends StateNotifier<List<int>> {
  static final provider = StateProvider(
    (ref) => CounterState(),
  );

  int get last {
    print('last');
    return state.last;
  }

  int get length {
    print('len');
    return state.length;
  }

  // the body of this will be provided below
  add(int p) {}

  CounterState() : super(<int>[0]);
}

class MyHomePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, watch) {
    void _incrementCounter() {
      final _count = Random.secure().nextInt(100);

      context.read(CounterState.provider.notifier).state.add(_count);
    }

    var count = watch(CounterState.provider.notifier).state.length;

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text(
          'You have pushed the button this many times: $count',
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: Icon(Icons.add),
      ),
    );
  }
}

as for the add method, I tried implementing it in a lot of ways, but neither works.

here is what I tried:

1: just add it straight away:

add(int p) {
  state.add(p);
}

2: I also tried the solution suggested in this answer:

add(int p) {
  state = [...state, p];
}

3: I tried to destroy the list entirely, and reassign it:

add(int p) {
  final _state = [];

  // copy [state] to [_state]
  for (var item in state) {
    _state.add(item);
  }

  // empty the state
  state = [];

  // add the new element
  _state.add(p);

  // refill [state] from [_state]
  for (var item in _state) {
    state.add(item);
  }


  print(state.length); // it continues until here and prints
}

Solution

Firstly, you are not creating the correct provider to listen to a StateNotifier. You need to change this:

static final provider = StateProvider(
  (ref) => CounterState(),
);

to this:

static final provider = StateNotifierProvider<CounterState, List<int>>(
  (ref) => CounterState(),
);

Please refer to the Riverpod documentation about the different types of providers.

Secondly, you are not actually watching for state changes, but you are just getting the state object from the notifier.

Change this line:

var count = watch(CounterState.provider.notifier).state.length;

to this:

final count = watch(CounterState.provider).length;

also, your increment method is not correct for StateNotifier providers. Please change this:

context.read(CounterState.provider.notifier).state.add(_count);

to this:

context.read(CounterState.provider.notifier).add(_count);

It should rebuild now when the state changes. However, you do need an implementation of your add method that actually changes the state object itself. I would suggest the second variant you mentioned, that is in my opinion the nicest way to do this:

add(int p) {
  state = [...state, p];
}

Answered By – TmKVU

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *