Bloc: BlocProvider.of is not working when Bloc has been provided through MultiBlocProvider and BlocProvider.value()

Created on 22 Feb 2020  路  2Comments  路  Source: felangel/bloc

Hi,

I found that using _BlocProvider.value()_ inside a _MultiBlocProvider_ instance doesn't work when children objects try to call _BlocProvider.of<>_ to access inherited blocs.

The sample app below contains 3 pages.
PageOne => contains 2 buttons to call PageTwo and PageThree respectively
PageTwo => contains 1 button to call PageThree
PageThree => contains a text

From the PageOne, if you click on the buttons called "Page 2" or "Page 3", you can see that there is no issue printed on the console because both _MaterialPageRoute_ have been created with a fresh _BlocProvider_ and doesn't matter if this one is the parent of the page or if it's located inside a _MultiBlocProvider_ instance.

Now, if you come back on PageOne and you click on "Page 2" button and then you click on "Page 3" button, you will see the following error:

BlocProvider.of() called with a context that does not contain a Bloc of type SomeBloc.

You can see that the onPressed() function of the button on PageThree contains:

BlocProvider.value(value: BlocProvider.of<SomeBloc>(context)),
//BlocProvider<SomeBloc>(create: (_) => BlocProvider.of<SomeBloc>(context)),

If you comment the first line and remove it on the second line, now scenario from PageTwo works.

//BlocProvider.value(value: BlocProvider.of<SomeBloc>(context)),
BlocProvider<SomeBloc>(create: (_) => BlocProvider.of<SomeBloc>(context)),

Can you confirm that it should work in all cases?

Find below the full code of the sample app:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class SomeBloc extends Bloc<BlocEvent, BlocState> {
  @override
  BlocState get initialState => null;

  @override
  Stream<BlocState> mapEventToState(BlocEvent event) {
    return null;
  }
}

class BlocEvent {}
class BlocState {}

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

class SomeApp extends StatefulWidget {
  @override
  _SomeAppState createState() => _SomeAppState();
}

class _SomeAppState extends State<SomeApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: PageOne(),
    );
  }
}

class PageOne extends StatefulWidget {
  @override
  _PageOneState createState() => _PageOneState();
}

class _PageOneState extends State<PageOne> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[

          RaisedButton(
            child: Text("Page 2"),
            onPressed: () => Navigator.of(context).push(
              MaterialPageRoute(
                builder: (_) => BlocProvider<SomeBloc>(
                  create: (_) => SomeBloc(),
                  child: PageTwo(),
                ),
              ),
            ),
          ),

          RaisedButton(
            child: Text("Page 3"),
            onPressed: () => Navigator.of(context).push(
              MaterialPageRoute(
                builder: (_) => MultiBlocProvider(
                  providers: <BlocProvider>[
                    BlocProvider<SomeBloc>(create: (_) => SomeBloc()),
                  ],
                  child: PageThree(),
                ),
              ),
            ),
          ),

        ],
      ),
    );
  }
}

class PageTwo extends StatefulWidget {
  @override
  _PageTwoState createState() => _PageTwoState();
}

class _PageTwoState extends State<PageTwo> {
  @override
  void initState() {
    super.initState();

    try {
      BlocProvider.of<SomeBloc>(context);
    }
    catch (e) {
      print("Error $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text("Page 3"),
          onPressed: () => Navigator.of(context).push(
            MaterialPageRoute(
              builder: (_) => MultiBlocProvider(
                providers: <BlocProvider>[
                  BlocProvider.value(value: BlocProvider.of<SomeBloc>(context)),
                  //BlocProvider<SomeBloc>(create: (_) => BlocProvider.of<SomeBloc>(context)),
                ],
                child: PageThree(),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class PageThree extends StatefulWidget {
  @override
  _PageThreeState createState() => _PageThreeState();
}

class _PageThreeState extends State<PageThree> {
  @override
  void initState() {
    super.initState();

    try {
      BlocProvider.of<SomeBloc>(context);
    }
    catch (e) {
      print("Error $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Here we are!"),
      ),
    );
  }
}
question

Most helpful comment

Hi felangel,

Thank you for the reply, you put the finger where I missed to look because it make sense now I see that.

I was following sample from another blog and because I try to use BlocProvider.value for the first time I looked immediately the implementation to understand what is doing this method.

BlocProvider.value({
    Key key,
    @required T value,
    Widget child,
  }) : this._(
          key: key,
          create: (_) => value,
          child: child,
        );

What I forgot on the moment because I was probably too much confident with the sample I found, is that it's not a static access to a method but a constructor which is by definition linked to the class declaration.

And here there is no way to discuss:

class BlocProvider<T extends Bloc<dynamic, dynamic>>
    extends SingleChildStatelessWidget with BlocProviderSingleChildWidget {

Thank you for clarification, it works as expected.

All 2 comments

Hi @Cyrus964 馃憢
Thanks for opening an issue!

The problem is you're not providing the bloc type when you use BlocProvider.value.

Everything will work as expected if you provide the bloc type to the value constructor just as you're doing with the default constructor (create).

BlocProvider<SomeBloc>.value(value: BlocProvider.of<SomeBloc>(context)),

Full Working Example:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class SomeBloc extends Bloc<BlocEvent, BlocState> {
  @override
  BlocState get initialState => null;

  @override
  Stream<BlocState> mapEventToState(BlocEvent event) {
    return null;
  }
}

class BlocEvent {}

class BlocState {}

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

class SomeApp extends StatefulWidget {
  @override
  _SomeAppState createState() => _SomeAppState();
}

class _SomeAppState extends State<SomeApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: PageOne(),
    );
  }
}

class PageOne extends StatefulWidget {
  @override
  _PageOneState createState() => _PageOneState();
}

class _PageOneState extends State<PageOne> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          RaisedButton(
            child: Text("Page 2"),
            onPressed: () => Navigator.of(context).push(
              MaterialPageRoute(
                builder: (_) => BlocProvider<SomeBloc>(
                  create: (_) => SomeBloc(),
                  child: PageTwo(),
                ),
              ),
            ),
          ),
          RaisedButton(
            child: Text("Page 3"),
            onPressed: () => Navigator.of(context).push(
              MaterialPageRoute(
                builder: (_) => MultiBlocProvider(
                  providers: <BlocProvider>[
                    BlocProvider<SomeBloc>(create: (_) => SomeBloc()),
                  ],
                  child: PageThree(),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class PageTwo extends StatefulWidget {
  @override
  _PageTwoState createState() => _PageTwoState();
}

class _PageTwoState extends State<PageTwo> {
  @override
  void initState() {
    super.initState();

    try {
      BlocProvider.of<SomeBloc>(context);
    } catch (e) {
      print("Error $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text("Page 3"),
          onPressed: () => Navigator.of(context).push(
            MaterialPageRoute(
              builder: (_) => MultiBlocProvider(
                providers: <BlocProvider>[
                  BlocProvider<SomeBloc>.value(value: BlocProvider.of<SomeBloc>(context)),
                  //BlocProvider<SomeBloc>(create: (_) => BlocProvider.of<SomeBloc>(context)),
                ],
                child: PageThree(),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class PageThree extends StatefulWidget {
  @override
  _PageThreeState createState() => _PageThreeState();
}

class _PageThreeState extends State<PageThree> {
  @override
  void initState() {
    super.initState();

    try {
      BlocProvider.of<SomeBloc>(context);
    } catch (e) {
      print("Error $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("Here we are!"),
      ),
    );
  }
}

Hi felangel,

Thank you for the reply, you put the finger where I missed to look because it make sense now I see that.

I was following sample from another blog and because I try to use BlocProvider.value for the first time I looked immediately the implementation to understand what is doing this method.

BlocProvider.value({
    Key key,
    @required T value,
    Widget child,
  }) : this._(
          key: key,
          create: (_) => value,
          child: child,
        );

What I forgot on the moment because I was probably too much confident with the sample I found, is that it's not a static access to a method but a constructor which is by definition linked to the class declaration.

And here there is no way to discuss:

class BlocProvider<T extends Bloc<dynamic, dynamic>>
    extends SingleChildStatelessWidget with BlocProviderSingleChildWidget {

Thank you for clarification, it works as expected.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shawnchan2014 picture shawnchan2014  路  3Comments

wheel1992 picture wheel1992  路  3Comments

nerder picture nerder  路  3Comments

nhwilly picture nhwilly  路  3Comments

rsnider19 picture rsnider19  路  3Comments