'how to propagate changes to another branch of the widget tree?

I have a flutter app (full code below) that displays a list, whose items are selectable independently of each other, and a status bar that displays the number of items selected.

Whenever an item in the list is tapped, its selected property is toggled, the setter invokes notifyListeners, and the check mark also toggles because the list tile is rebuilt thanks to provider.

But the item counter in the statusbar doesn't change. This is understandable since nobody's invoking notifyListeners on the global app state.

So my question is: how can I propagate a change in a part of the app state upwards if this change has to trigger the rebuild of other widgets?

I could write a method in AppState that toggles the item itself and then calls notifyListeners on AppState, but this doesn't sound like a good solution.

Full source code:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Item extends ChangeNotifier {
  bool _selected = false;
  final String label;

  Item(this.label);

  set selected(bool s) {
    _selected = s;
    notifyListeners();
  }

  bool get selected => _selected;
}

class AppState extends ChangeNotifier {
  List<Item> items = [
    Item("Apple"),
    Item("Banana"),
    Item("Peach"),
    Item("Strawberry")
  ];
}

void main() {
  runApp(ChangeNotifierProvider(
      create: (context) => AppState(), child: const MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Consumer<AppState>(
        builder: (context, appState, _) => Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: ListView(
                  children: appState.items
                      .map((e) => ChangeNotifierProvider.value(
                          value: e,
                          builder: (context, _) => const ItemWidget()))
                      .toList(),
                ),
              ),
              bottomSheet: BottomSheet(
                builder: (context) => Container(
                  padding: const EdgeInsets.all(20),
                  child: Text(
                      "${appState.items.where((element) => element.selected).length}"
                      " items selected"),
                ),
                onClosing: () {},
              ),
            ));
  }
}

class ItemWidget extends StatelessWidget {
  const ItemWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Consumer<Item>(
        builder: (context, item, _) => ListTile(
          leading: Icon(item.selected ? Icons.check_circle : Icons.circle),
          title: Text(item.label),
          onTap: () => item.selected = !item.selected,
        ),
      ),
    );
  }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source