Search code examples
flutterprovider

Flutter - Provider


I am trying to create following widget, in which pressing a button the user should be redirected to MainGameScreen().

Widget quitGame(BuildContext context) {
showDialog(
    context: context,
    builder: (BuildContext context) {
      return Dialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10),
        ),
        elevation: 0,
        backgroundColor: Colors.transparent,
        child: Stack(
          children: <Widget>[
            Container(
              padding:
                  EdgeInsets.only(left: 20, top: 30, right: 20, bottom: 20),
              margin: EdgeInsets.only(top: 45),
              decoration: BoxDecoration(
                  shape: BoxShape.rectangle,
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(20),
                  boxShadow: [
                    BoxShadow(
                        color: Colors.black,
                        offset: Offset(0, 10),
                        blurRadius: 10),
                  ]),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Text(
                    "QUIT GAME",
                    textScaleFactor: 1,
                    style: TextStyle(
                        fontSize: 40, fontWeight: FontWeight.w600),
                  ),
                  SizedBox(
                    height: 15,
                  ),
                  Text(
                    "Are you sure you want to leave the game?",
                    textScaleFactor: 1,
                    style: TextStyle(fontSize: 14),
                    textAlign: TextAlign.center,
                  ),
                  SizedBox(
                    height: 10,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Align(
                        alignment: Alignment.bottomCenter,
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              primary: Color(0xFFec4688),
                            ),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                            child: Text("NO",
                                textScaleFactor: 1,
                                style: TextStyle(fontSize: 20))),
                      ),
                      Align(
                        alignment: Alignment.bottomCenter,
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              primary: Color(0xFF1fd281),
                            ),
                            onPressed: () {
                              roundTimer.cancel();
                              Navigator.push(
                                context,
                                MaterialPageRoute(
                                  builder: (context) => MainGameScreen(),
                                ),
                              );
                            },
                            child: Text("YES",
                                textScaleFactor: 1,
                                style: TextStyle(fontSize: 20))),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      );
    });
}

But when I do this I get the following error:

════════ Exception caught by widgets library ═══════════════════════════════════
The following ProviderNotFoundException was thrown building Builder:
Error: Could not find the correct Provider<AuthBloc> above this MainGameScreen Widget

Unfortunately I don't have good understanding of provider, so I would be grateful if someone could explain what is going wrong and how I can fix it. Thanks


Solution

  • The MainGameScreen Widget must be a descendant of Provider Widget. But here, you used MaterialPageRoute to push a new route to your app, so MainGameScreen will be in a new seperated branch of the widget tree, it's only parent is the Material App Widget. So there is two solutions for this:

    1. Place the ProviderWidget above the MaterialApp Widget. Ex:
          Provider<AuthBloc>(   
            create: (context) => AuthBloc(),  
            dispose: (context, bloc) => bloc.dispose(), // remove it if there is no dispose method in your BLoC
            child: MaterialApp(),  
            ),
    
    
    
    2. A trick, which is very useful but really hard to test and manage: Add a `static` method which look like this :
        
            class MainGameScreen extends StatefulWidget {
                      static Widget create(BuildContext context) {
                        return Provider<AuthBloc>(
                          create: (_) => AuthBloc(),
                          child: MainGameScreen(),
                        );
                      } 
    

    and when we use MaterialPageRoute to push, use:

     MaterialPageRoute(
          builder: (context) => MainGameScreen.create(context),
        ),
    

    And with this solution, we had a Provider above the MainGameScreen ;). And remember this is very hard to add widget test so think twice before using it.

    Sorry for my previous stupid explanation!