Search code examples
flutterdialog

How to open a Dialog in flutter when a value changes in a ChangeNotifierProvider


I have a screen that is using values from a ChangeNotifierProvider to display the content of the screen. As expected when these values change the screen build method is triggered and the screen content is updated.

I would like that if one of those values changes from false to true, a Dialog is opened on that screen.

The only way I figured out to open this Dialog is by launching it from the build method when the value has changed and a new build is called.

I know that the showDialog is an async method while build is sync and it is an antipattern to manage these side effects from inside the build method. I cannot know when the build method will be called and this could lead to having several dialogs opened everytime the build is called.

So, so far I can only open the dialog from the build method and using a boolean flag in memory to control if the Dialog is opened or not. Like this:

class MyScreen extends StatefulWidget {
  const MyScreen();

  @override
  State<StatefulWidget> createState() => _MyScreenState();
}

class _MyScreenState extends State<MyScreen> {
  late final myModel = Provider.of<MyModel?>(context);
   bool _isDialogShowing = false;

  @override
  void initState() {
    super.initState();
    ...
  }
  
  void _showMyDialog(BuildContext ctx) {
    showDialog(
      context: ctx,
      barrierDismissible: false,
      builder: (context) => AlertDialog(
        content: const Text("Hello I am a dialog"),
      ),
    ).then((val) {
      // The dialog has been closed
      _isDialogShowing = true;

    });
  }

  @override
  Widget build(BuildContext context) {
    // Open dialog if value from `ChangeNotifierProvider` has changed
    if (myModel?.hasToShowDialog == true && _isDialogShowing == false) {
      _isDialogShowing = true;
      Future.delayed(
        Duration.zero,
        () {
          _showMyDialog(context);
        },
      );
    }

    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Column(
          children: [
           ..... 
          ],
        ),
      ],
    );
  }
}

Do you know how to properly trigger the dialog opening event when a value changes in the ChangeNotifierProvider? Thank you very much!!!


Solution

  • What you can do is to add a listener to your ChangeNotifier:

      late final myModel = Provider.of<MyModel?>(context);
      bool _isDialogShowing = false;
    
      @override
      void initState() {
        super.initState();
        myModel.addListener(_showDialog);
      }
    
      @override
      void dipose() {
        myModel.removeListener(_showDialog);
        super.dispose();
      }
    
      Future<void> _showDialog() async {
       if (_isDialogShowing || !mounted) return;
       // `hasToShowDialog` could be a getter and not a variable.
       if (myModel?.hasToShowDialog != true) return;
       _isDialogShowing = true;
       await showDialog(
         context: context,
         barrierDismissible: false,
         builder: (context) => AlertDialog(
           content: const Text("Hello I am a dialog"),
         ),
       );
       _isDialogShowing = false;
      }
    

    I don't know what your MyModel looks like so it is hard to know what else you could fo. hasToShowDialog could be a getter and not a variable as you suggested in your question.