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!"),
),
);
}
}
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.
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.
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:
Thank you for clarification, it works as expected.