Difference between List.add and manually adding item to a Riverpod StateNotifier<List<String>>

Viewed 910

I'd like to learn how to use Riverpod, so to this end I'm implementing a small app that shows a list of items and a button that adds a dummy item to the list when tapped.

Problem context

Pressing the button in the following app works as expected (a dummy item is added, and the change is immediately reflected in the UI):

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



void main() => runApp(const ProviderScope(child: MyApp()));

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => MaterialApp(home: HomePage());
}



final itemsProvider = StateNotifierProvider((ref) => ItemsList());

class ItemsList extends StateNotifier<List<String>> {
  ItemsList([List<String> items]) : super(items ?? []);

  void add(String item) => state = [...state, item];
}



class HomePage extends ConsumerWidget {
  const HomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final items = watch(itemsProvider.state);
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: items.map((e) => Text(e)).toList(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read(itemsProvider).add('Other item'),
        tooltip: 'Add item',
        child: Icon(Icons.add),
      ),
    );
  }
}

Problem

Inside the add method of the ItemsList class, I thought that changing the statement state = [...state, item] to state.add(item) would not make any difference. However, doing so makes the app not responsive to state changes anymore: tapping the button doesn't trigger a change to the UI, and I don't see any new item in the UI until I hot-reload my app.

So just to be clear: changing void add(String item) => state = [...state, item]; to void add(String item) => state.add(item); breaks the link between app state and UI.

Why is manually adding an item with list destructuring different from using List.add? Thank you very much in advance.

1 Answers

The state must change in a way that oldValue == newValue is false. And by default, adding elements to a list with .add mutates the list in place, so equality is preserved. That's why's all the examples have something like state = [...state, foo], so that an entirely new list is in state, and that's not equal to the old list.

Related