How to use bloc initialised in one screen in a different screen - flutter

I have two screens in my flutter application Screen1 and Screen2. Screen1 is the home screen. I navigate from Screen1 to Screen2 via
Navigator.of(context).push(PageRouteBuilder<void>(pageBuilder: (context, animation, secondaryAnimation) => Screen2());
and Screen2 to Screen1 via
Navigator.pop(context);
Screen1 is statelesswidget:
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<BlocA>(create: (_) => BlocA()),
BlocProvider<BlocB>(create: (_) => BlocB()),
]
child: RaisedButton(
child: Text('Goto Screen 2'),
onPressed: Navigator.of(context).push(PageRouteBuilder<void>(pageBuilder: (context, animation, secondaryAnimation) => Screen2());
),
)
}
}
I would appreciate anyone can provide an answer that will satisfy the following :
Want to access the two bloc initialised in the Screen1 from Screen2 using
BlocProvider.value(value: BlocProvider.of(context), child: ...)
without bringing the initialisation of blocs upto the MaterialApp widget. Cannot make the MultiBlocProvider the parent of MaterialApp. I want the blocs only accessed in Screen1 and Screen2. It should not be accessed by other screens.
Also when popped from Screen2 to Screen1, the blocs should not be disposed. Hence, continue to maintain state when popped from Screen2
Should not pass the bloc via constructor or as arguments in Navigator
Currently getting following error:
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building Screen2(dirty):
flutter: BlocProvider.of() called with a context that does not contain a BlocA.
flutter: No ancestor could be found starting from the context that was passed to
flutter: BlocProvider.of<BlocA>().
flutter:
flutter: This can happen if the context you used comes from a widget above the BlocProvider.
flutter:
flutter: The context used was: Screen2(dirty)

The use the already created bloc instance on new page, you can use BlocProvider.value.
Like passing BlocX to next route will be like
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<BlocX>(context),
child: Screen2(),
),
),
);
I might go for repository provider on your case. But to pass multiple instance, you can wrap BlocProvider two times on route.
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: BlocProvider.value(
value: BlocProvider.of<BlocB>(context),
child: Screen2(),
),
),
),
);
Currently, I cannot remember any better option, let me know if you've got any.
Now, your second route Screen2 can access both BlocB and BlocB instance.
You can get the instance it like, depend on your code structure.
BlocConsumer<BlocA, BlocAState>(
builder: (context, state) {
if (state is BlocAInitial) {
return Text(state.name);
}
return Text("un impleneted");
},
listener: (context, state) {},
),
When you create bloc, and like to pass it with BlocProvider.value(value: BlocProvider.of<BlocA>(context),, you need to use separate context.
More about blocprovider.
Check the demo, It will clarify, I am using Builder instead of creating new widget for context.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen1(),
);
}
}
class Screen1 extends StatelessWidget {
const Screen1({super.key});
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<BlocA>(create: (_) => BlocA()),
BlocProvider<BlocB>(create: (_) => BlocB()),
],
child: Builder(builder: (context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: BlocProvider.value(
value: BlocProvider.of<BlocB>(context),
child: Screen2(),
),
),
),
);
},
),
);
}),
);
}
}
class Screen2 extends StatelessWidget {
const Screen2({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
BlocConsumer<BlocA, BlocAState>(
builder: (context, state) {
if (state is BlocAInitial) {
return Text(state.name);
}
return Text("un impleneted");
},
listener: (context, state) {},
),
BlocConsumer<BlocB, BlocBState>(
builder: (context, state) {
if (state is BlocBInitial) {
return Text(state.name);
}
return Text("un impleneted");
},
listener: (context, state) {},
),
],
),
);
}
}
Find more about flutterbloccoreconcepts

you have to elevate MultiBlocProvider in the widget tree so that it wraps both screens, e.g. make it a parent of MaterialApp

You can pass bloc elements as a parameter to Screen2
final blocAObject = BlocProvider.of<BlocA>(context);
Navigator.of(context).push(PageRouteBuilder<void>(pageBuilder: (context, animation, secondaryAnimation) => Screen2(bloca:blocAObject));

If you're ok with initializing in MaterialApp while only having the blocs accessible from the two screens, try the following:
final blocA = BlocA(); // shared bloc instance
final blocB = BlocB(); // shared bloc instance
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'screen1': (_) => MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => blocA,
),
BlocProvider(
create: (context) => blocB,
),
],
child: Screen1(),
),
'screen2': (_) => MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => blocA,
),
BlocProvider(
create: (context) => blocB,
),
],
child: Screen2(),
),
},
);
}

Related

How to use MultiBlocProvider in specific level in flutter tree widget?

I want to use MultiBlocProvider as shown below.
How to use MultiBlocProvider in specific level in flutter tree widget ?
In other words, when we use MultiBlocProvideron top of MaterialApp, there is no problem. But according to the code below, this item gets an error.
example:
void main() {
runApp(MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case "/":
return MaterialPageRoute(
builder: (_) => MultiBlocProvider(providers: [
BlocProvider(
create: (_) => CounterBloc(),
)
], child: const GroupA()),
settings: settings);
case "/ScopeA":
return MaterialPageRoute(
builder: (_) => const ScopeA(), settings: settings);
default:
return MaterialPageRoute(
builder: (_) => const Text("ERROR"), settings: settings);
}
},
));
}
class GroupA extends StatelessWidget {
const GroupA({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Group A:')),
body: Center(
child: MaterialButton(
onPressed: () => Navigator.pushNamed(context, '/ScopeA'),
child: const Text("Go To Scope A")),
),
);
}
}
class ScopeA extends StatelessWidget {
const ScopeA({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Scope A:')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: Theme.of(context).textTheme.displayLarge,
);
},
),
),
);
}
}
abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}
class CounterDecrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
on<CounterDecrementPressed>((event, emit) => emit(state - 1));
}
}
i using below version :
flutter_bloc : 8.1.2
bloc : 8.1.1
error:
Error: Cannot hit test a render box that has never been laid out.
You're misleading the concept behind passing blocs through sub-tree and through Navigator widgets.
Basically, the MultiBlocProvideror BlocProvider make a bloc accessible in all the subtrees, so the bloc will be available only in GroupA's subtree of widgets, by calling Navigator.pushNamed(), what does happen is that another separated sub-tree will be put in the Navigator child, so at this point the GroupA and ScopeA will not be in the same widget-tree, even if it seems to when you see a page route is set on top of other's on the Flutter UI, I can represent it like this:
-> MultiBlocProvider -> GroupA
Navigator => |
-> ScopeA
and as you conclude, the bloc that is available inside the GroupA will not be available in ScopeA, until you pass it in somehow, like using BlocProvider.value():
case "/ScopeA":
return MaterialPageRoute(
builder: (context) {
return BlocProvider.value(
value: context.read<CounterBloc>(),
child: const ScopeA(),
);
},
settings: settings,
);
or by making the bloc accessible through the whole app, so you will have a Flutter tree like this:
-> GroupA
MultiBlocProvider -> Navigator => |
-> ScopeA

Some Providers not working in modal bottom sheet

Problem: Some of my providers cannot be found in the context above the modal bottom sheet. Example:
Error: Could not find the correct Provider above
this ModalEnterTask Widget
This happens because you used a BuildContext that does not include
the provider of your choice.
All Providers are definetly above the widget opening the modal sheet. One provider is actually working. That one is created above the material app. The ones not working are created in the build method of my tabs screen. I've been using them sucesfuly all throughout the app. Why can they not be found in the modal sheet?
My theory: The context used for the modal sheet is dependend on the Material app context; not on the context from where it is opened in the widget tree. Correct?
Solution: I don't know. I can't move the providers up to where the working Provider sits. They need context information (edit: MediaQuery information, not context), so they cannot be initialized before the material app.
Code:
MyApp State...this is where I initialize the provider that works:
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MainElementList(widget.mainElementList),
),
ChangeNotifierProvider(
create: (context) => ScrollControllerToggles(),
)
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Three Things',
initialRoute: '/',
routes: {
'/': (ctx) => TabsScreen(),
},
),
);
}
}
The TabsScreen(), here I initialize the Providers that do not work in the modal sheet:
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => CustomColors(customColorScheme),
),
//
ChangeNotifierProvider(
create: (ctx) => DimensionsProvider(
appBarHeight: appBarHeight,
availableHeight: availableHeight,
availableWidth: availableWidth),
),
//
ChangeNotifierProvider(
create: (ctx) => CustomTextStyle(availableHeight, customTextTheme),
),
],
child: Scaffold(//body: function picking pages)
Calling the modal sheet:
return GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
builder: (bctx) => ModalEnterTask(positionTask: positionTask),
),
//
child: Center(//container with an icon)
The widget called in the builder of the the modal sheet:
class ModalEnterTask extends StatelessWidget {
late String positionTask;
ModalEnterTask({required String this.positionTask, Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
//RESPONSIVENESS
final availableHeight =
Provider.of<DimensionsProvider>(context).availableHeight;
return Column(
children: [
SizedBox(
height: 10,
),
//
IconButton(
icon: Icon(Icons.plus_one),
onPressed: () {
Provider.of<MainElementList>(context, listen: false)
.changeSingleTaskPhase(0, positionTask);
Navigator.of(context).pop();
},
),
],
);
}
}
DimensionProvider > doesn't work
MainElementList > works
As I can see you are getting error because your provider is not in the tree which you are calling so its better to include all providers in the main and you will be able to resolve this issue. Here is how you do that
void main() async {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CustomColors()),
ChangeNotifierProvider(create: (context) => DimensionsProvider()),
ChangeNotifierProvider(create: (context) => MainElementList()),
ChangeNotifierProvider(create: (context) => ScrollControllerToggles()),
],
child: MyApp(),
),
);
}
I think this solution will work in your case... Have a try and let me know
Note: I can see that you are passing some parameters in providers so just change a structure little bit and assign values to providers where you are initializing them
I got same error when using method showModalBottomSheet.
I try to use BlocBuilder in the widget that was open as a modal bottom sheet, then i got
BlocProvider.of() called with a context that does not contain a MyBloc
My solutions is:
pass the value as a parameter of widget. In my case is:
showModalBottomSheet(
context: context,
builder: (BuildContext btsContext) {
return CartBottomSheetPage(
cartBloc: BlocProvider.of<MyBloc>(context),
);
},
);
NOTE: you must get the value from parent context like this:
BlocProvider.of<MyBloc>(context)
not like this:
BlocProvider.of<MyBloc>(btsContext)

How to use multiple Blocs for different UIs?

I have a Landing UI that doesn't have any Blocs, a Register UI with it's Bloc, a Verification UI with it's Bloc, and a Home UI with it's Bloc.
In each one I defined the BlocProvider.of.
In the main I defined at the Home of Material App a Multiple Bloc Provider with each has it's child and the main child of the provider is the landing Page like this :
home: MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (context) => UserBloc(UsRepoImp()),
child: RegisterUi(),
),
BlocProvider<VerificationBloc>(
create: (context) => VerificationBloc(VerRepoImp()),
child: VerificationUi(),
),
BlocProvider<HomeBloc>(
create: (context) => HomeBloc(HomeRepoImp()),
child: HomeUi(),
),
],
child: LandingUi(),
),
and one more thing the Verification UI is returned from a Register Bloc state like so :
BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserLoading) {
return CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(Color(0xff7b68ee)),
);
} else if (state is UserRegistered) {
return VerifyAccount();
} else if (state is UserError) {
return Text('Error');
}
return SizedBox(
height: 10.0,
);
},
),
But when I run I have an error that the Bloc shouldn't have an ancestor.
How am I supposed to make these Blocs to communicate with UI changings correctly?
I think you are using MultiBlocProvider in a wrong way. you should not provide child there, instead only provide the argument of the create function there, and then in your widget tree below this MultiBlocProvider you can use BlocBuilder to listen to any of the provided blocs above in the tree, and if you need to listen to multiple blocs in the same widget, you need to nest BlocBuilders.
example:
#override
Widget build(BuildContext context) => MultiBlocProvider(
providers: [
BlocProvider<RecorderBloc>(
create: (context) => myFirstBloc(),
),
BlocProvider<PermissionBloc>(
create: (context) => mySecondBloc(),
)
],
child:myChild()
);
Then inside my_child.dart :
#override
Widget build(BuildContext context) {
return BlocBuilder<MyFirstBloc, MyFirstBlocState>(
builder: (context, myFirstBlocState) =>
BlocBuilder<MySecondBloc, MySecondBlocState>(
builder: (context, secondBlocState) {
//return widget based on the states of both blocs...
},
),
);
}

Can't access bloc in showModal context in Flutter

I wrap my scafflod with dart BlocProvider<ABloc,AState>
but when I use showDialog func and showDialog I want to access ABloc from context or BlocBuilder
doesn't contain bloc and throw error
is there a way to access bloc in this situatuion
(in my dialog I show text filed to get user name so I want to access bloc)
class MyHomePageState extends StateLessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
lazy: false,
create: (context) => EditColorBloc(context.bloc<RetrieveColorBloc>()),
child: MainScafold());
}
}
class MainScafold extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("sina"),
),
floatingActionButton:FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => showDialog(
context: context,
builder: (ctx) {
// show dialog and use
// context o BlocBuilder to access
// EditColorBloc
// throw error BlocProvider.of() called with a context that does
// not contain a Cubit of type EditColorBloc
}
)
}
}
UPDATE v6.1.0
(Credit to #Sebastian Dennis)
context.bloc was deprecated in favor of provider's context.read, context.watch, and context.select. In the below code, just change context.bloc<TestBloc> to context.read<TestBloc>
You shouldn't use a BlocBuilder to wrap the Dialog. Reason being is that you're (most likely) not rebuilding the whole Dialog whenever a change occurs. Another, more important, reason is that the context in which your Dialog is being built doesn't contain your bloc, so you need to inject your bloc instance into the Dialog's context.
Instead, use BlocProvider.value and set the value parameter to BlocProvider.of(context) (aka context.bloc() ), and make sure the context is not the showDailog 's method context, but the context in the original build method.
Here is a working example. You just have to change the TestBloc or TestState etc to your own Bloc instances:
#override
Widget build(BuildContext context) {
return BlocProvider<TestBloc>(
create: (context) => TestBloc(InitTestState()),
child: Scaffold(
body: BlocBuilder<TestBloc, TestState>(
builder: (context, state) => Center(
child: FlatButton(
child: Text("Show Dialog"),
onPressed: () => showDialog(
context: context,
// Relavent code change here
builder: (ctx) => BlocProvider<TestBloc>.value(
value: context.bloc<TestBloc>(),
child: Dialog(
child: FlatButton(
child: Text("Notify bloc"),
onPressed: () => context
.bloc<TestBloc>()
.add(TestEvent()),
),
),
)),
),
),
)));

BlocProvider.of() called with a context that does not contain a Bloc - even that it does

First of, I do know how BLoC suppose to work, the idea behind it and I know the difference between BlocProvider() and BlocProvider.value() constructors.
For simplicity, my application has 3 pages with a widget tree like this:
App() => LoginPage() => HomePage() => UserTokensPage()
I want my LoginPage() to have access to UserBloc because i need to log in user etc. To do that, I wrap LoginPage() builder at App() widget like this:
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
),
);
}
}
That obviously works just fine. Then, if User logs in successfully, he is navigated to HomePage. Now, I need to have access to two different blocs at my HomePage so I use MultiBlocProvider to pass existing UserBloc further and create a brand new one named DataBloc. I do it like this:
#override
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
),
BlocProvider<DataBloc>(
create: (_) => DataBloc(DataRepository()),
),
],
child: HomePage(),
),
),
);
}
},
[...]
This also works. Problem happens when from HomePage user navigates to UserTokensPage. At UserTokensPage I need my already existing UserBloc that I want to pass with BlocProvider.value() constructor. I do it like this:
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text('My App'),
actions: <Widget>[
CustomPopupButton(),
],
),
[...]
class CustomPopupButton extends StatelessWidget {
const CustomPopupButton({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
icon: Icon(Icons.more_horiz),
onSelected: (String choice) {
switch (choice) {
case PopupState.myTokens:
{
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
}
break;
case PopupState.signOut:
{
BlocProvider.of<UserBloc>(context).add(SignOut());
Navigator.of(context).pop();
}
}
},
[...]
When I press button to navigate to MyTokensPage i get error with message:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Builder(dirty):
BlocProvider.of() called with a context that does not contain a Bloc of type UserBloc.
No ancestor could be found starting from the context that was passed to BlocProvider.of<UserBloc>().
This can happen if:
1. The context you used comes from a widget above the BlocProvider.
2. You used MultiBlocProvider and didn't explicity provide the BlocProvider types.
Good: BlocProvider<UserBloc>(create: (context) => UserBloc())
Bad: BlocProvider(create: (context) => UserBloc()).
The context used was: CustomPopupButton
What am I doing wrong? Is it because i have extracted PopupMenuButton widget that somehow loses blocs? I don't understand what I can be doing wrong.
You can just wrap the Blocs you need to access through out the app by wrapping it at the entry point of the app like this
runApp(
MultiBlocProvider(
providers: [
BlocProvider<UserBloc>(
create: (context) =>
UserBloc(UserRepository()),
),
],
child: App()
)
);
}
and you can access this bloc at anywhere of your app by
BlocProvider.of<UserBloc>(context).add(event of user bloc());
EDIT 10/03/2022
Since this thread became very popular I feel I need to add some comments.
This is valid solution if your goal is to use blocs that are not provided above your MaterialApp widget, but instead being declared somewhere down the widget tree by wrapping your widget (eg. some page) with BlocProvider making it possible for that widget to access the bloc.
It is easier to avoid problems by declaring all your blocs in MultiBlocProvider somewhere up the widget tree (like I said before), but this topic was not created with that in mind. Feel free to upvote and use this aproach described in Amesh Fernando response but do that knowing the difference.
I fixed it. Inside App widget i create LoginPage with
home: BlocProvider<UserBloc>(
create: (context) => UserBloc(UserRepository()),
child: LoginPage(),
At LoginPage I simply wrap BlocBuilders one into another
Widget build(BuildContext context) {
return BlocListener<UserBloc, UserState>(
listener: (context, state) {
if (state is UserAuthenticated) {
Navigator.of(context).push(
MaterialPageRoute<HomePage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: BlocProvider<NewRelicBloc>(
create: (_) => NewRelicBloc(NewRelicRepository()),
child: HomePage(),
),
),
),
);
}
},
[...]
PopupMenuButton navigates User to TokenPage with
Navigator.of(context).push(
MaterialPageRoute<UserTokensPage>(
builder: (_) => BlocProvider.value(
value: BlocProvider.of<UserBloc>(context),
child: UserTokensPage(),
),
),
);
And that solved all my problems.
Solution
Method A: Access UserBloc provider instance directly without passing it
I prefer this solution since it requires less code.
A.1 Wrap CustomPopupButton instance with provider Consumer so it rebuilds itself whenever UserBloc notifies listeners of value changes.
Change this:
actions: <Widget>[
CustomPopupButton(),
],
To:
actions: <Widget>[
Consumer<UserBloc>(builder: (BuildContext context, UserBloc userBloc, Widget child) {
return CustomPopupButton(),
});
],
A.2 Change Provider instance invocation inside the stateless widget to disable listening to value changes -- "listening" and resulting "rebuilds" are already done by Consumer.
A.2.1 Change this:
value: BlocProvider.of<UserBloc>(context),
To:
value: BlocProvider.of<UserBloc>(context, listen: false),
A.2.2 And change this:
BlocProvider.of<UserBloc>(context).add(SignOut());
To:
BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
Method B: pass UserBloc provider instance
Same thing as Method A, but:
In A.1 you'd pass userBloc like this: return CustomPopupButton(userBloc: userBloc),.
You'd declare final UserBloc userBloc; member property inside CustomPopupButton.
In A.2 you'd do this: userBloc.add(SignOut()); instead of BlocProvider.of<UserBloc>(context, listen: false).add(SignOut());
Explanation
flutter_bloc is using Provider, to be aware what's going on it's better understand Provider. Please refer to my answer here to understand my answer to your question, and to understand Provider and listen flag better.
Change name of context in builder whether in bottomSheet or materialPageRoute.
So that bloc can access parent context through context
unless it's going to take context from builder (bottom sheet). This can lead
to an error which you can't reach the instance of bloc .
showModalBottomSheet(
context: context,
builder: (context2) { ===> change here to context2
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: widgetA(),
),
}
You need to either decompose your widget into two widgets (which I recommend for testability reasons) or use a Builder widget to get a child context.
class MyHomePage extends StatelessWidget { #override Widget build(BuildContext context) { return BlocProvider( create: (_) => TestCubit(), child: MyHomeView(), ); } } class MyHomeView extends StatelessWidget { #override Widget build(BuildContext context) { return Scaffold( body: Center( child: RaisedButton(onPressed: () => BlocProvider.of<TestCubit>(context)...) ), ); } }
source: solved by Felix Angelov, https://github.com/felangel/bloc/issues/2064
you don't have to use BlocProvider.value() to navigate to another screen, you can just wrap MaterialApp into BlocProvider as a child of it