Search code examples
flutterdartflutter-provider

Consumer Provider doesn't seem to notify listeners?


The minimal reproducible code below aims to have a loading icon when a button is pressed(to simulate loading when asynchronous computation happen).

For some reason, the Consumer Provider doesn't rebuild the widget when during the callback.


My view:

class HomeView extends StatefulWidget {

  const HomeView();

  @override
  _HomeViewState createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChangeNotifierProvider(
        create: (_) => HomeViewModel(99),
        child: Consumer<HomeViewModel>(
          builder: (_, myModel, __) => Center(
            child: ButtonsAtBottom(
              addEventAction: () => myModel.increment(context),
              busy: myModel.busy
            ),
          ),
        ),
      ),
    );
  }
}

My model where I simulate to do business logic:

class HomeViewModel extends LoadableModel {
  late int integer;
  HomeViewModel(this.integer);
  
  void increment(BuildContext context) {
    super.setBusy(true);
    Future.delayed(Duration(seconds: 3), () => print(integer++));
    super.setBusy(false);
    //Passed in context just to try to simulate my real app
    //print(context);
  }
}

class LoadableModel extends ChangeNotifier {
  bool _busy = false;
  bool get busy => _busy;

  void setBusy(bool value) {
    _busy = value;
    notifyListeners();
  }
}

PROBLEM: When the callback executes, and setBusy() methods within it are executed, they should notify the listeners and update the busy field passed in it. Subsequently, either a text or loader should be displayed.

BUT, busy field is never updated and remains as false.

class ButtonsAtBottom extends StatelessWidget {
  final bool busy;
  final void Function() addEventAction;
  const ButtonsAtBottom({required this.busy, required this.addEventAction});

  @override
  Widget build(BuildContext context) {
    print("busy value: $busy");
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        IconButton(
          onPressed: () => Navigator.pop(context),
          icon: Icon(Icons.clear_rounded),
        ),
        ElevatedButton.icon(
            onPressed: addEventAction,
            icon: Icon(Icons.save_alt_rounded),
            label: busy
                ? CircularProgressIndicator.adaptive(
                    backgroundColor: Colors.white,
                  )
                : Text("Save")),
      ],
    );
  }
}

Solution

  • did you try to await the future?