BlocProvider.of(context) returns null

Compatibility
Last Reviewed
Mar 30, 2023
Published on
Apr 08, 2019
Flutter
v 3.13.x
Dart
v 3.1.x

Question

I am trying to retrieve the reference of a BLoC, a Model... via BlocProvider.of(context) or ScopedModel.of(context) and it returns null. Why is this?

Answer

Most of the time, this comes from the fact that you switched from one Page (= Route) to another, using the Navigator.

Typical use case:



class HomePage extends StatefulWidget {
  
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  late MyBloc bloc;
  
  
  void initState(){
      super.initState();
      bloc = MyBloc();
  }

  
  void dispose(){
      bloc.dispose();
      super.dispose();
  }
  
  Widget build(BuildContext context){
      return BlocProvider<MyBloc>(
          bloc: bloc,
          child: Scaffold(
              appBar: AppBar(
                  title: Text('Home Page'),
                  actions: <Widget>[
                      IconButton(
                          icon: Icon(Icons.search),
                          onPress:(){
                              Navigator.of(context).pushNamed('/searchPage');
                          }
                      ),
                  ]
              ),
              body: Container(),
          ),
      );
  }
}

class SearchPage extends StatelessWidget {
  
  Widget build(BuildContext context){
      MyBloc? bloc = BlocProvider.of<MyBloc>(context);

      return Container();
  }
}

So, when you click the Search icon on the HomePage, you are redirected to the SearchPage and the line: "MyBloc bloc = BlocProvider.of<MyBloc>(context)" returns null...

Why is this?

In order to understand, you need to keep in mind how Flutter handles the Pages (= Route)...

Routes = Stack of OverlayEntry()

When you are instantiating a MaterialApp, in fact, Flutter creates a series of Widgets you usually do not need to worry about and one of them is an Overlay.

This Overlay has a Stack as a descendant. This Stack will be used to "stack" the Pages (something like to put them one on top of the other).

The following schema shows a very simplified view of the hierarchy, which results from the code here above:



The MOST important here is to remember that both stacked Pages DOT NOT SHARE ANYTHING. The only thing they have a common is the same ancestors: the Stack and upward.

In other words, Widgets which are descendant of one page ARE NOT VISIBLE FROM ANOTHER PAGE (without doing any trick...).

When, from the SearchPage, you are using BlocProvider.of<MyBloc>(context), the system looks for a Widget of a certain type in the list of all its ANCESTORS (= ancestors of the SearchPage Widget).

In our case (see simplified schema), it will never find the requested Widget as the latter is not in its ancestors' list.


How to solve this?

There are several possibilities, let's see 3 of them:

1. Put on "Top of Everything"

If the BLoC is global and unique for the whole application, nothing prevents from injecting it as an ancestor of the MaterialApp, like this:



void main(){
  runApp(Application());
}

class Application extends StatelessWidget {
  
  Widget build(BuildContext context){
      return BlocProvider<MyBloc>(
          bloc: MyBloc(),
          child: MaterialApp(
              home: HomePage(),
          ),
      );
  }
}

This way, the BLoC will be available from EVERYWHERE in the application since it will be an ancestor of almost all widgets.

2. Make it a "Singleton"

If the BLoC is global and unique for the whole application, nothing prevents from using a "Singleton", as follows:



class MyBloc implements BlocBase {

  
  void dispose(){
      //...
  }

  // -------  SINGLETON ------
  static final MyBloc _instance = MyBloc._internal();
  factory MyBloc() => _instance;
  MyBloc._internal();
}
MyBloc myBloc = MyBloc();

To use the BLoC, it becomes very easy: simply make a reference to the "myBloc" global variable.

3. Pass it as an argument

Another possibility is to pass the instance of the BLoC in argument to another page.

I personally do not favor this solution as it creates a tight coupling but sometimes, it is very difficult to avoid it.


Conclusions

This problem is very common and faced by every new Flutter developer, one day or another.

I really hope it is now a bit clearer why this happens.

0 Comments
Be the first to give a comment...
© 2024 - Flutteris
email: info@flutteris.com

Flutteris



Where excellence meets innovation
"your satisfaction is our priority"

© 2024 - Flutteris
email: info@flutteris.com