Search code examples
flutterriverpod

How can I watch a change in the list size with Riverpod on a Flutter app?


I have a list of todo elements managed by:

@Riverpod(keepAlive: true)
class TodosNotifier extends _$TodosNotifier {
  @override
  List<Todo> build() {
    return [.....some elements here.....];
  }

  void addTodo(Map<String, Object?> todoJson) {
    Todo todo = Todo.fromJson(todoJson);
    state = [...state, todo];
  }

  ...other methods here that change the state for different reasons...
}

and I have another Notifier which is meant to change its value only when the size of the TodosNotifier list changes (via addTodo(...)).

@Riverpod(keepAlive: true)
class TodosLengthNotifier extends _$TodosLengthNotifier {

  @override
  int build() => 0;

  void update() {
    // it never gets called here
    List<Todo> todos = ref.watch(todosNotifierProvider);
    if (state != todos.length) {
      state = todos.length;
    }
  }

}

The problem is that the method TodosLengthNotifier.update() never gets called every time I add a new "todo" element with TodosNotifier.addTodo(). How can I fix that?

UPDATE

Based on the given answer I am now just using:

@Riverpod(keepAlive: true)
int todosLength(TodosLengthRef ref) => ref.watch(todosNotifierProvider).length;

Solution

  • Any custom methods you define inside a notifier won't get called unless you explicitly call it. For example, to call the update method, you would do:

    ref.read(todosNotifierProvider.notifier).update();
    

    But what you want here is that the value exposed by todosLengthNotifierProvider to be reactive to the changes from TodosNotifier. In order to do that, you should watch TodosNotifier inside the build method of TodosLengthNotifier. The returned value should be the value that you want this notifier to expose.

    @Riverpod(keepAlive: true)
    class TodosLengthNotifier extends _$TodosLengthNotifier {
      @override
      int build() {
        List<Todo> todos = ref.watch(todosNotifierProvider);
        return todos.length;
      }
    }
    

    Now when the list from TodosNotifier changes, the build method of TodosLengthNotifier will be executed again so the value exposed by todosLengthNotifierProvider will change as well.

    Furthermore, if you're not going to introduce side-effect methods to TodosLengthNotifier (and probably makes more sense that way, since it's just exposing a "computed" property from another provider), you'd better off using a regular Provider instead.