Search code examples
flutterflutter-provider

Consumer not updating the state?


I am trying to create an Icon with a number indicator on top of it and the number indicator receives its data via a Consumer provider. The problem is that the state is not being updated by the consumer function and I don't understand why (if I update the state with a hot reload everything works just fine).

Here is the code for my main file:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => TestData())
          // I use more providers but deleted them here for brevity
        ],
        child: TestScreen3(),
      ),
    );
  }
}

The test screen 3 widget

class TestScreen3 extends StatefulWidget {
  @override
  _TestScreen3State createState() => _TestScreen3State();
}

class _TestScreen3State extends State<TestScreen3> {
  @override
  Widget build(BuildContext context) {
    final testData = Provider.of<TestData>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Test app 3'),
        actions: [
          Consumer<TestData>(builder: (_, data, __) {
            return IconButton(
                icon: Badge(num: data.items.length.toString()),
                onPressed: () => print(data.items.length));
          })
        ],
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Increase'),
          onPressed: () {
            testData.addItem();
          },
        ),
      ),
    );
  }
}

The badge widget

class Badge extends StatelessWidget {
  Badge({@required this.num});
  final String num;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Icon(Icons.assessment),
        Positioned(
          child: Container(
            padding: EdgeInsets.all(2),
            child: Text(
              num,
              style: TextStyle(fontSize: 8),
              textAlign: TextAlign.center,
            ),
            constraints: BoxConstraints(
              minHeight: 12,
              minWidth: 12,
            ),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(10),
              color: Colors.red,
            ),
          ),
        ),
      ],
    );
  }
}

and the data model I am using

class Item {
  Item(this.id);
  final String id;
}

class TestData with ChangeNotifier {
  List<Item> _items = [];

  List<Item> get items => [..._items];

  void addItem() {
    _items.add(Item(DateTime.now().toString()));
  }

  notifyListeners();
}

The imports work just fine, I left them out for brevity. I followed along a this tutorial: https://www.udemy.com/course/learn-flutter-dart-to-build-ios-android-apps/ and it uses a key argument for the badge that looks like this:

class Badge extends StatelessWidget {
  const Badge({
    Key key,
    @required this.child,
    @required this.value,
    this.color,
  }) : super(key: key);

  final Widget child;
  final String value;
  final Color color;

However, the use of key or super is not explained in the tutorial and when I add these parameters to my code they don't seem to make a change.

Many thanks in advance, I probably missed something super obvious...


Solution

  • Add notifyListeners(); inside addItem() method

     void addItem() {
        _items.add(Item(DateTime.now().toString()));
            notifyListeners();
          }