Closed PhilipChng closed 3 years ago
@Allan-Nava as of now you can nest the BlocBuilders.
Ok, but the UI is:
I have to fill the dropdown with categories
What is the issue? You can have a categories bloc which manages the state of the categories which is used by the dropdown section via a BlocBuilder.
What is the issue? You can have a categories bloc which manages the state of the categories which is used by the dropdown section via a BlocBuilder.
Right now the memory leak, and i need to show in the same time cause I do two http requests. Maybe this is the solution is:
you should probably create a single bloc for that feature and the bloc can handle making both network requests. If they can be made in parallel you can use Future.wait([...]). Then in your UI you just need one BlocBuilder.
What is the issue? You can have a categories bloc which manages the state of the categories which is used by the dropdown section via a BlocBuilder.
Right now the memory leak, and i need to show in the same time cause I do two http requests. Maybe this is the solution is:
you should probably create a single bloc for that feature and the bloc can handle making both network requests. If they can be made in parallel you can use Future.wait([...]). Then in your UI you just need one BlocBuilder.
I tried but I got the same problem and the app crash.
@felangel thanks for your support lib and package, which helps me a lot.
I have one case, I have a widget ContinueButton
which has two properties isLoading
and isValid
the isLoading
state is controlled by the current page bloc, in this case, the PaidQrcodeResumeBloc
. The isValid
state is controlled by reusable Widget ValueField
that has your own Bloc ValueFieldBloc
. How I can change these properties values without MultiBlocBuilder
?
Example code:
BlocBuilder<ValueFieldBloc, ValueFieldState>(
builder: (context, state) {
return ContinueButton(
onPressed: () => context.bloc<PaidQrcodeResumeBloc>().add(
PaidQrcodeResumeOnPaid(
widget.paidQrcode
.copyWith(value: moneyController.numberValue),
),
),
isValid: state is ValueFieldValid,
text: Strings.paid,
isLoading: ,
);
},
)
Hi @rafaelmpessoa ๐
It sounds that both isLoading
and isValid
should be part of the same bloc's state, ValueFieldBloc
in your case.
You can also add an event to ValueFieldBloc
to flip isLoading
to true when ContinueButton
is pressed.
Or you could even have a BlocListener
on PaidQrcodeResumeBloc
that would react to state changes and would add the proper event on ValueFieldBloc
which will result in flipping isLoading
.
Hope that gives you a couple of ideas on how you could improve your implementation ๐
@felangel - I believe there is at least one good use case over there and as many people insisting for the feature mentioned, there are few reasons for the request:
I will also put here my use case since from my standing point it makes sense:
I have a calendar widget, which can be either filtered by specific appointment types (e.g. work, holiday and etc) or change its view (day, week, month and etc.).
Hence, I created two separate BLoC(s) - one for event filter EventFilterBloc
and one for calendar view CalendarViewBloc
. I would consider each one as a feature rather than a resource. I have a few other places with calendars, where I need CalendarViewBloc
, but since they have different appointment types (not events), I don't need EventFilterBloc
part there, I rather need other appointment types. Hence I created additional BLoCs - each for these different calendars and I combine them with CalendarViewBloc
.
One more question - since we have MultiBlocProvider
I guess some of the arguments behind creating it can apply to MultiBlocBuilder
case, can't it?
Anyway - I will understand if the implementation is complex, but my firm belief is that it is a needed feature.
I am nesting different bloc builders in my widget ,multi blocbuilder sounds great but what if you want to put a particular widget somewhere on the screen by this ,you will still end up back at the core implementation of flutter bloc and put that particular builder somewhere.My point is on let the bloc builder finds a way to behave like the body it will be all difficult manipulating the widget positions, I don't know if I have made sense but that's my view.
Hey there ๐ I want to give some Impressions on this topic too.
I am currently working on a workout timer application. In this application I have a TimerBloc which handles Timer ticks and reduces the remaining time left. After some time I thought it would be awesome if the app has a burn-in-protection, because i did not want the users phones to break, because itโs displaying a text for a couple of minutes. So iโve build a BurnInBloc.
The TimerBloc internally runs a Timer.periodic which triggers a tick event each second. The BurnInBloc internally runs a Timer.periodic which triggers a protect Event each 60sec. If a touch event is emitted the BurnInBloc Timer will get resetted.
I thought it would be a nice idea to seperate these featues, because of the benefits @angel1st mentioned.
Now in the UI I have to combine TimerBloc and BurnInBloc with builders, which looks kind of messy, or Iโd have to add the features of BurnInBloc into TImerBloc. The second option is not the one Iโd prefer, because the new TimerBloc would get kind of messy I think. Having nested BlocBuilder on the other hand is also not very nice UI-wise.
BUT I think if flutter_bloc would provide something like BlocBuilder5 itโll lead to very messy Blocs written by others (Not saying my Blocs are well written ๐). Giving the option will tell developers itโs fine to have BlocBuilder in BlocBuilder in BlocBuilder. In the current situation i have to think about how i structure my blocs and if i should rethink the current architecture of my blocs.
I think we need to have something to combine small blocs that benefit from decoupling, or a guide/way to combine small blocs, but should prevent giving developers the feeling nesting 100 BlocBuilder is natural.
(Also thanks for maintaining this package and having a discussion about this with us all)
Hello, I was thinking about the problem of multiple widget composition of BlocBuilder
and BlocListener
,then I created an experimental package with the same API than flutter_bloc
: https://pub.dev/packages/flutter_hooks_bloc
This package reimplements those widgets based on HookWidget
(https://pub.dev/packages/flutter_hooks).
A widget that rebuilds depending on many bloc would be written like this:
class MyMultiBlocBuilder extends HookWidget {
MyMultiBlocBuilder
@override
Widget build(BuildContext context){
// onEmitted is called every time that the state is emitter in a cubit/bloc
final cubitA = useBloc<CubitA, int>(onEmitted: (context, previousState, state){
// with true, the widget rebuild, otherwise, it behave like a BlocListener
return buildWhenA?.call(previousState, state);
});
final blocC = useBloc<BlocB, String>(onEmitted: (context, previousState, state){
// If you also want to have a BlocListener behavior, you can add some code here
if(listenWhen?.call(previousState, state) ?? true){
listener(context, state);
}
return buildWhenB?.call(previousState, state);
});
return Column(
children: [
Text('${cubitA.state}'),
Text('${blocB.state}'),
],
);
}
}
Hello, I was thinking about the problem of multiple widget composition of BlocBuilder
and BlocListener
,then I created an experimental package with the same API than flutter_bloc
: https://pub.dev/packages/flutter_hooks_bloc
This package reimplements those widgets based on HookWidget
(https://pub.dev/packages/flutter_hooks).
A widget that rebuilds depending on many bloc would be written like this:
class MyMultiBlocBuilder extends HookWidget {
const MyMultiBlocBuilder({Key key}) : super(key: key);
@override
Widget build(BuildContext context){
// onEmitted is called every time that the state is emitter in a cubit/bloc
final cubitA = useBloc<CubitA, int>(onEmitted: (context, previousState, state){
// with true, the widget rebuild, otherwise, it behave like a BlocListener
return buildWhenA?.call(previousState, state) ?? true;
});
final blocB = useBloc<BlocB, String>(onEmitted: (context, previousState, state){
// If you also want to have a BlocListener behavior, you can add some code here
if(listenWhen?.call(previousState, state) ?? true){
listener(context, state);
}
return buildWhenB?.call(previousState, state) ?? true;
});
// always rebuild when cubit emits a new state
final cubitC = useBloc<CubitC, double>();
return Column(
children: [
Text('${cubitA.state}'),
Text('${blocB.state}'),
Text('${cubitC.state}'),
],
);
}
}
Hey @kranfix,
I think your solution looks good and would implement everything we need but there still some issues I am seeing here:
Maybe we should look more into Bloc-To-Bloc-Communication to solve this kind of problem.
This would give the option to have one or more separate Blocs which are combined through communication. Problem here is also combining multiple Blocs into one would result that including this one "SuperBloc" in the UI would seem pretty normal and not very problematic to the developer.
@felangel Does it hurt performance somehow to have multiple BlocBuilder
nested or is this something we can do without a problem?
If not my 2. argument is pretty much invalid. (If so sorry ๐)
@kiesman99 yeah. I know that flutter-hooks is POC, that's why I said flutter_hooks_bloc
is an experimental package. But, it simplify many tasks that are not easy without hooks.
@kiesman99 nested BlocBuilders will not be less performant than hooks because at the end of the day if you want nested BlocBuilders it usually means you want something to rebuild if any of the bloc states change. In those cases, thereโs nothing technically wrong with nested BlocBuilders although in my experience itโs usually a sign that the blocs are data-driven and not feature-driven. Ideally, there should be one bloc per feature in which case there should be less of a need to nest BlocBuilders. Hope that helps ๐
although in my experience itโs usually a sign that the blocs are data-driven and not feature-driven. Ideally, there should be one bloc per feature in which case there should be less of a need to nest BlocBuilders. Hope that helps ๐
@felangel - I believe there are at least a couple of examples e.g. this one and this one, where we posted our real-life examples, to show that, in more complex apps, it is not unusual to have two or even three nested BlocBuilders
and this is not a sign of bad architectural approach.
On the other hand, if the implementation of so-called MultiBlocBuilder
is doable even as syntactic sugar, once you do it, you will have even more happy followers ๐
Besides - I would like also to thank you for the great library and all your efforts to maintain and develop it!
I'm now developing an app and faced the same problem but I conclude it was unnecessary for the following:
The definition of BlocBuilder
according to the documentation is:
BlocBuilder is a Flutter widget which requires a Bloc and a builder function. BlocBuilder handles building the widget in response to new states. Source
So BlocBuilder should be use only when a widget will response to new states.
Solutions to avoid this issue to be open again in the future:
People should know how to handle correctly the Bloc Concepts, that's why I opened this issue a week ago. This isn't part of the core functionality so doesn't add any value, it solves a problem but is not the way to do it.
Note: The code provided is for demonstration purposes.
I simply use:-
BlocBuilder<UsernameupdateCubit, UsernameupdateState>( builder: (context, state) { //Note this return BlocBuilder<QuantityCubit, QuantityState>( builder: (context, stateQuantity) { //i changed variable name return ElevatedButton(
Thank you
I simply use:-
BlocBuilder<UsernameupdateCubit, UsernameupdateState>( builder: (context, state) { //Note this return BlocBuilder<QuantityCubit, QuantityState>( builder: (context, stateQuantity) { //i changed variable name return ElevatedButton(
Thank you
You can also use context.watch
@override
Widget build(BuildContext context) {
final userState = context.watch<UsernameUpdateCubit>().state;
final quantityState = context.watch<QuantityCubit>().state;
return ElevatedButton(...);
}
@felangel I know this is an old issue but I am using two cubits to manage theme and language respectively with one states each and I am passing both as individual blocbuilders over the material app. So is this a correct way to do it? or I can use one single cubit for both.
What if instead of MultiBlocBuilder provide MultiBlocSelector in order to to combine data from different states and rebuild ui when obtained data slice is changed. If you combine whole state it will work like MultiBlocBuilder
@felangel I know this is an old issue but I am using two cubits to manage theme and language respectively with one states each and I am passing both as individual blocbuilders over the material app. So is this a correct way to do it? or I can use one single cubit for both.
I recommend one bloc/cubit per feature state.
What if instead of MultiBlocBuilder provide MultiBlocSelector in order to to combine data from different states and rebuild ui when obtained data slice is changed. If you combine whole state it will work like MultiBlocBuilder
Can you provide a concrete use case in which this would be necessary? I recommend a single bloc/cubit per feature so ideally your widgets should not need to aggregate data from multiple bloc/cubit states.
It's not necessary, but convenient. For example if you have have newsfeed which is contains articles and bloc for it and some other feed like favorite or something else which is also have bloc and articles. And you have article detail page. User can go to details page from both feeds by passing article id to details route. So i want to select article from both feeds and update ui whenever article in feeds is changed. I can merge streams, use nested blocBuilders or reorganize my buisness logic (by adding third bloc, pass one bloc to another or something else) but multiselector will be more convinient for me. I think it's possible to find more use cases. Selectors is good feature and the way to combine selectors could be like selectors improvement
@felangel I have this idea for the long time: What If we use Bloc per Screen + Global Blocs for features like Authentication or Theme? Is this a very bad idea or it have something in it?
@felangel usecase: In the main.dart we want to globally use localisation bloc, theme bloc. I just think that nesting 2 blocbuilders is exactly like that: nasty. Anyway, good job with the bloc and also mason, great tools mate.
@tananga we recommend a bloc per feature, which sometimes mean a bloc per screen if the screen covers the whole feature.
@bbura localization is UI logic and should be done in the presentation layer, not in the bloc.
I also agree with @felangel that something like a MultiBlocBuilder
would add unnecessary complexity to Bloc and should not be part of the package. However, I needed something similar for a HomeScreen, where information from multiple blocs is read and I wanted a global loading state.
Disclaimer: In the end, I used RxDart
to combine the state stream instead of using the BlocBuilder3
Still, I will share my code for the BlocBuilder3
here, as it will save time for people who might want it in the future:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
typedef BlocBuilderCondition<S> = bool Function(S previous, S current);
typedef BlocWidgetBuilder3<StateA, StateB, StateC> = Widget Function(
BuildContext,
StateA,
StateB,
StateC,
);
class BlocBuilder3<
BlocA extends StateStreamable<BlocAState>,
BlocAState,
BlocB extends StateStreamable<BlocBState>,
BlocBState,
BlocC extends StateStreamable<BlocCState>,
BlocCState> extends StatelessWidget {
const BlocBuilder3({
Key? key,
required this.builder,
this.blocA,
this.blocB,
this.blocC,
this.buildWhenA,
this.buildWhenB,
this.buildWhenC,
}) : super(key: key);
final BlocWidgetBuilder3<BlocAState, BlocBState, BlocCState> builder;
final BlocA? blocA;
final BlocB? blocB;
final BlocC? blocC;
final BlocBuilderCondition<BlocAState>? buildWhenA;
final BlocBuilderCondition<BlocBState>? buildWhenB;
final BlocBuilderCondition<BlocCState>? buildWhenC;
@override
Widget build(BuildContext context) {
return BlocBuilder<BlocA, BlocAState>(
bloc: blocA,
buildWhen: buildWhenA,
builder: (context, blocAState) {
return BlocBuilder<BlocB, BlocBState>(
bloc: blocB,
buildWhen: buildWhenB,
builder: (context, blocBState) {
return BlocBuilder<BlocC, BlocCState>(
bloc: blocC,
buildWhen: buildWhenC,
builder: (context, blocCState) {
return builder(context, blocAState, blocBState, blocCState);
},
);
},
);
},
);
}
}
@S-ecki how did you combine the states using RxDart? Just curious.
@S-ecki how did you combine the states using RxDart? Just curious.
I took the state streams and used combineLatest if I remember correctly.
Note that with combineLatest
only emits after each stream emitted an event by default.
I simply use:-
BlocBuilder<UsernameupdateCubit, UsernameupdateState>( builder: (context, state) { //Note this return BlocBuilder<QuantityCubit, QuantityState>( builder: (context, stateQuantity) { //i changed variable name return ElevatedButton(
Thank you
You can also use
context.watch
@override Widget build(BuildContext context) { final userState = context.watch<UsernameUpdateCubit>().state; final quantityState = context.watch<QuantityCubit>().state; return ElevatedButton(...); }
Thats awesome i used the same approach also you can use select if you need to listen on only value like this final userState = context.select((UsernameUpdateCubit cubit) => cubit.state);
Please take a look of this flutter bloc documents https://bloclibrary.dev/#/flutterbloccoreconcepts?id=usage-2
For safe implementation of multiple blocbuilder, use Builder and context.watch.
@override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
final stateA = context.watch<BlocA>().state;
final stateB = context.watch<BlocB>().state;
final stateC = context.watch<BlocC>().state;
return const Text('Hello World');
},
);
}
MultiBlocBuilder
would be interesting, BlocBuilder2
... doesn't look good, I would like to know why MultiBlocBuilder
is not a good idea
For clean architecture purposes, MultiBlocBuilder
, MultiBlocListener
or MultiBlocConsumer
would be useful. I encountered a situation where I wanted to add two global blocs on top of a particular feature (which also has a bloc itself). Here is the idea behind what I was trying to implement:
BlocBuilder<Global1Bloc, Global1State>(
buildWhen: (previous, current) {
return previous.loading != current.loading;
},
builder: (context, global1State) {
return BlocConsumer<Global2Bloc, Global2State>(
listenWhen: (previous, current) {
return !current.loading;
},
listener: (context, state) {
if(state.isSuccess) {...};
},
buildWhen: (previous, current) {
return previous.loading != current.loading;
},
builder: (context, global2State) {
return BlocBuilder<FeatureBloc, FeatureState>(
buildWhen: (previous, current) {
return previous.hasInput!= current.hasInput;
},
builder: (context, featureState) {
return CustomTextButton(
bgColor: Colors.black,
label: "Confirm",
loading: featureState.loading || global1State.loading,
onPressed: {...},
);
},
);
},
);
},
);
}
This is working but its kinda messy to look at. I will stick to this pattern for the meantime but might follow https://github.com/felangel/bloc/issues/538#issuecomment-1711049615
For clean architecture purposes,
MultiBlocBuilder
,MultiBlocListener
orMultiBlocConsumer
would be useful. I encountered a situation where I wanted to add two global blocs on top of a particular feature (which also has a bloc itself). Here is the idea behind what I was trying to implement:BlocBuilder<Global1Bloc, Global1State>( buildWhen: (previous, current) { return previous.loading != current.loading; }, builder: (context, global1State) { return BlocConsumer<Global2Bloc, Global2State>( listenWhen: (previous, current) { return !current.loading; }, listener: (context, state) { if(state.isSuccess) {...}; }, buildWhen: (previous, current) { return previous.loading != current.loading; }, builder: (context, global2State) { return BlocBuilder<FeatureBloc, FeatureState>( buildWhen: (previous, current) { return previous.hasInput!= current.hasInput; }, builder: (context, featureState) { return CustomTextButton( bgColor: Colors.black, label: "Confirm", loading: featureState.loading || global1State.loading, onPressed: {...}, ); }, ); }, ); }, ); }
This is working but its kinda messy to look at. I will stick to this pattern for the meantime but might follow #538 (comment)
Instead of buildwhen and listenWhen use selectors (context.select<...>(...)). You don't need blocBuilders here at all. You can rewrite your code with one listener and couple of selectors. You will be surprised how clean your code will become. It's not realted to clean architechture beacuse it's all about presentation layer
Sometimes I would need to access different blocs in the same widget, and the code goes pretty messy. I am thinking maybe we can have something like
MultiBlocBuilder
which serve the similar purpose ofMultiRepositoryProvider
andMultiBlocProvider
.