When using flutter bloc what is the recommendation, is it recomended for each page to have its own bloc or can i reuse one block for multiple pages, if so how?
3 Answers
I think that the best solution is to have one BLoC per page. It helps you to always know in which state each screen is just by looking at its BLoC. If you want to show the state of each of your tabs independently you should create one BLoC for each tab, and create one Repository which will handle fetching the data. But if the state of every tab will be the same, (for example you fetch data only once for all of the screens, so you don't show loading screen on every tab) then I think that you could create just one BLoC for all of this tabs.
It is also worth to add, that BLoCs can communicate with each other. So you can get state of one BLoC from another, or listen to its state changes. That could be helpful when you decide to create separate BLoCs for tabs.
I have addressed this topic in my latest article. You can check it out if you want to dive deeper.
There are no hard-set rules about this. It depends on what you want to accomplish.
An example: if each page is "radically" from each other, then yes, a BLoC per page makes sense. You can still share an "application-wide" BLoC between those pages if some kind of sharing or interaction is required between the pages.
In general, I've noticed that usually a BLoC "per page" is useful as there are always specific things related for each page that you handle within their BLoC. You can the use a general BLoC to share data or some other common thing between them.
You can combine the BLoC pattern with RxDart to handle somewhat more complex interaction scenarios between a BLoC and the UI.
Sharing a BLoC is fairly simple, just nest them or use a MultiProvider (from the provider package):
runApp(
BlocProvider(
builder: (_) => SettingsBloc(),
child: BlocProvider(
builder: (_) => ApplicationBloc(),
child: MyApp()
)
)
);
and then you can just retrieve them via the Provider:
class MyApp extends ... {
@override
Widget build(BuildContext context) {
final settingsBloc = Provider.of<SettingsBloc>(context);
final appBloc = Provider.of<ApplicationBloc>(context);
// do something with the above BLoCs
}
}
You can share different bloc's in different pages using BlocProvider.
Let's define some RootWidget that will be responsible for holding all Bloc's.
class RootPage extends StatefulWidget {
@override
_RootPageState createState() => _RootPageState();
}
class _RootPageState extends State<RootPage> {
NavigationBloc _navigationBloc;
ProfileBloc _profileBloc;
ThingBloc _thingBloc;
@override
void initState(){
_navigationBloc = NavigationBloc();
_thingBloc = ThingBloc();
_profileBloc = ProfileBloc();
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<NavigationBloc>(
builder: (BuildContext context) => _navigationBloc
),
BlocProvider<ProfileBloc>(
builder: (BuildContext context) => _profileBloc
),
BlocProvider<ThingBloc>(
builder: (BuildContext context) => _thingBloc
),
],
child: BlocBuilder(
bloc: _navigationBloc,
builder: (context, state){
if (state is DrawProfilePage){
return ProfilePage();
} else if (state is DrawThingsPage){
return ThingsPage();
} else {
return null
}
}
)
)
}
}
And after that, we can use any of bloc from parent and all widgets will share the same state and can dispatch event on the same bloc
class ThingsPage extends StatefulWidget {
@override
_ThingsPageState createState() => _ThingsPageState();
}
class _ThingsPageState extends State<ThingsPage> {
@override
void initState(){
_profileBloc = BlocProvider.of<ProfileBloc>(context);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
child: BlocBuilder(
bloc: _profileBloc,
builder: (context, state){
if (state is ThingsAreUpdated){
return Container(
Text(state.count.toList())
);
} else {
return Container()
}
}
)
);
}
}