Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access Provider providers in Dialogs in Flutter

The Provider package makes use of InheritedWidget. This is a problem when I want to access a provider when I'm in a Dialog. If I load a dialog using

 showDialog(... builder: (context) => MyDialog);

I can't access anything using InheritedWidget because my dialog isn't part of the main widget tree. This also means that I can't access my Provider providers, correct?

My question is: How can I access my providers in a dialog if it's not part of the main app widget tree?

final firebaseAuth = Provider.of<FirebaseAuth>(context);

I have the same problem with using BLoCs. If I try to retrieve them in a dialog via InheritedWidget, they fail. I've gotten around this by passing the BLoC in the constructor but this seems to defeat the purpose of InheritedWidgets.

like image 919
JustLearningAgain Avatar asked Sep 17 '19 06:09

JustLearningAgain


2 Answers

Instead of passing the BLoC in the constructor, you can make use of BlocProvider.value.

https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/BlocProvider/BlocProvider.value.html

This will allow you to provide your existing BLoC instance to your new route (the dialog). And you still get all the benefits of InheritedWidget

  // Get the BLoC using the provider
  MyBloc myBloc = BlocProvider.of<MyBloc>(context);

  showDialog(
    context: context,
    builder: (BuildContext context) {
      Widget dialog = SimpleDialog(
        children: <Widget>[
          ... // Now you can call BlocProvider.of<MyBloc>(context); and it will work
        ],
      );

      // Provide the existing BLoC instance to the new route (the dialog)
      return BlocProvider<MyBloc>.value(
        value: myBloc, //
        child: dialog,
      );
    },
  );

.value() also exists for ChangeNotifierProvider, ListenableProvider, etc. https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider/ChangeNotifierProvider.value.html

https://pub.dev/documentation/provider/latest/provider/ListenableProvider/ListenableProvider.value.html

like image 71
SteveM Avatar answered Sep 30 '22 22:09

SteveM


I got stuck at this part for a while. I honestly didn't want to pass the provider, also unpacking the widget code to grab the parent context is hard when you are dealing with a complex widget (And it doesn't seem like the best approach).

This made more sense

  handleFileViewerClicked(context) async {
    var reportState = Provider.of<ReportState>(context, listen: false);
    /**
     *The dialog will live in a new context and requires a new provider to be created for the report state
     * For more information read the Provider.Consumer documentation and showDialog function signature.
     */
    showDialog(
      context: context,
      //Notice the use of ChangeNotifierProvider<ReportState>.value
      builder: (_) => ChangeNotifierProvider<ReportState>.value(
        value: reportState,
        child: FileViewer(),
      ),
    );
}

Your child widget which is FileViewer in that case can make use of

class FileViewer extends StatelessWidget {
.
.
Widget build(BuildContext context) {
    //you can enable or disable listen if you logic require so 
    var reportState = Provider.of<ReportState>(context); 
    return Text('${reportState.files.length}');
 }
}
like image 42
Kareem Avatar answered Sep 30 '22 22:09

Kareem