Search code examples
flutterdartflutter-providerstate-management

Flutter Provider select - How do handle complex returned type like List or Map


I am trying to use flutter provider and I am having issues with context.select() when the returned type is a complex object (like Map or List) with an unchanged reference. Even when I notify the listeners of my model, my widget won't rebuild and I suspect it is because I'm modifying my objects in place.

Here is a simple code to reproduce

class MyModel extends ChangeNotifier {
  final list = <int>[0, 1, 2, 3, 4, 5];

  void push() {
    // Here I modify the list in place, and even when I call `notifyListeners()`, `MyWidget` won't rebuild
    list.add(list.length); 
    notifyListeners();
  }
}

class MyScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MyModel>(
      create: (_) => MyModel(),
      child: MyWidget(),
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    // It is not rebuilt when the list is modified, the widget is rebuilt if I add
    // context.select<MyModel, int>((myModel) => myModel.list.length);
    // for example
    // I suspect it is because the reference of `myModel.list` didn't change
    final list = context.select<MyModel, List<int>>((myModel) => myModel.list);

    return Container(
      child: Column(
        children: [
          Text(list.join(', ')),
          TextButton(
            child: const Text('Add'),
            onPressed: () {
              context.read<MyModel>().push();
              // setState(() {}); <- I could force the rebuild here too
            },
          ),
        ],
      ),
    );
  }
}

enter image description here

When I click on the button Add, the list is updated but my widget is not rebuilt. I could, for example, call setState(() {}) at the end of my onPressed to force a rebuild but I would rather not do that.


What is the best way to handle this ?


Solution

  • Your list should be immutable:

    class MyModel extends ChangeNotifier {
      List<int> list = <int>[0, 1, 2, 3, 4, 5];
    
      void push() {
        list = [...list, list.length];
        notifyListeners();
      }
    }
    

    Otherwise, the object reference is the same and the Widget does not rebuild.