Is your feature request related to a problem? Please describe.
If you have a massive widget tree under a BlocBuilder and only the first (or second or third or however you want to utilize the builder) need the state update, but the REST of the tree does NOT need to be rebuilt, then you have a performance problem where a massive widget tree is rebuilt for the sake of a limited scope the bloc needs to access, because it rebuilds the entire widget tree under the builder for the sake of one widget.
Describe the solution you'd like
From flutter docs:
The third argument is child, which is there for optimization. If you have a large widget subtree under your Consumer that doesn鈥檛 change when the model changes, you can construct it once and get it through the builder.
This is an essential feature for any builder, I'm surprised Bloc doesn't do this. I think Provider does this as well but I'm not sure.
Additional context
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#consumer
Hi @SwissCheese5 馃憢
Thanks for opening an issue!
Can you please provide a sample use-case where you feel this becomes necessary? Thanks!
Here's a limited example:
BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
return FloatingActionButton(
backgroundColor: state is NetworkStateNotConnected
? Colors.red
: Theme.of(context).accentColor,
child: AnimatedSwitcher(
transitionBuilder: (child, animation) => ScaleTransition(
child: child,
scale: CurvedAnimation(parent: animation, curve: Curves.easeInOutBack)),
duration: const Duration(milliseconds: 500),
child: state is NetworkStateNotConnected
? Icon(Icons.offline_bolt,
key: ValueKey(2), color: Theme.of(context).backgroundColor)
: (navigationController.index == 1
? Icon(
Icons.add,
key: ValueKey('0'),
color: Theme.of(context).backgroundColor,
)
: Icon(
Icons.create_new_folder,
key: ValueKey('1'),
color: Theme.of(context).backgroundColor,
))),
);
}),
I've omitted onPressed for simplicity's sake
That code can be made into this to avoid rebuilding 3 icon widgets. again, this is a very limited example, I have significantly larger examples that I can't use because my project is in the middle of a massive rebuild
BlocBuilder<AuthBloc, AuthState>(builder: (context, state, child) {
return FloatingActionButton(
backgroundColor: state is NetworkStateNotConnected
? Colors.red
: Theme.of(context).accentColor,
child: AnimatedSwitcher(
transitionBuilder: (child, animation) => ScaleTransition(
child: child,
scale: CurvedAnimation(parent: animation, curve: Curves.easeInOutBack)),
duration: const Duration(milliseconds: 500),
child: state is NetworkStateNotConnected
? Icon(Icons.offline_bolt,
key: ValueKey(2), color: Theme.of(context).backgroundColor)
: child),
);
},
child: navigationController.index == 1
? Icon(
Icons.add,
key: ValueKey('0'),
color: Theme.of(context).backgroundColor,
)
: Icon(
Icons.create_new_folder,
key: ValueKey('1'),
color: Theme.of(context).backgroundColor,
)),
Here's a more extreme example but it's very messy and incomplete. (red lines everywhere), but it gets the point across.
@SwissCheese5 thanks for sharing an example but I'm still not convinced 馃槄 . Can you please provide a more realistic example where you would actually need to use child due to performance issues?
I could only find two places in Flutter where this pattern is used:
TweenAnimationBuilderValueListenableBuilderStreamBuilder is arguably the closest Flutter widget to BlocBuilder and it doesn't support this use-case.
I'm a bit skeptical of the value that this feature brings as it's going to force every developer to add , child to their builder which is pretty inconvenient (not to mention confusing) especially if it isn't used majority of the time.
BlocBuilder<MyBloc, MyState>(
builder: (context, state, child) {
// return widget
},
)
Also for the Consumer example used in the docs:
return Consumer<CartModel>(
builder: (context, cart, child) => Stack(
children: [
// Use SomeExpensiveWidget here, without rebuilding every time.
child,
Text("Total price: ${cart.totalPrice}"),
],
),
// Build the expensive widget here.
child: SomeExpensiveWidget(),
);
You can rewrite it without child like:
return Stack(
children: [
SomeExpensiveWidget(),
Consumer<CartModel>(
builder: (context, cart) => Text("Total price: ${cart.totalPrice}"),
),
],
)
In my opinion, it's bad practice to lift a Consumer higher up in the tree than it is needed. In the example, it's scoped to the entire Stack when it is only needed by the Text.
Would love to hear other people's thoughts 馃槃
There are situations when caching the child would result in great performance improvements, but I don't think BlocBuilder needs this. This pattern is extensively used and recommended when dealing with animations, since you don't wanna rebuild your sub-tree on each animation frame. Widgets like AnimatedBuilder makes great use of this approach.
As for BlocBuilder, it doesn't drive things like animations where a lot of rebuilds are performed, but instead builds UI based on a state containing some data(which is rarely changing at a fast pace).
If you'd to implement this feature it would only result in having people deal with an extra annoying parameter which is rarely ever gonna be used, not to mention having to refactor all existing code to include it.
If you really want to optimize performance in regards to bloc then just use multiple BlocBuilders as low as possible in your widget tree and make use of buildWhen to further control when they rebuild.
Going to close this for now since I'm still not convinced that the benefit of adding a child outweighs the cons described above. Feel free to comment with additional thoughts/examples and I'm happy to continue the discussion 馃憤
Most helpful comment
There are situations when caching the child would result in great performance improvements, but I don't think
BlocBuilderneeds this. This pattern is extensively used and recommended when dealing with animations, since you don't wanna rebuild your sub-tree on each animation frame. Widgets likeAnimatedBuildermakes great use of this approach.As for
BlocBuilder, it doesn't drive things like animations where a lot of rebuilds are performed, but instead builds UI based on a state containing some data(which is rarely changing at a fast pace).If you'd to implement this feature it would only result in having people deal with an extra annoying parameter which is rarely ever gonna be used, not to mention having to refactor all existing code to include it.
If you really want to optimize performance in regards to bloc then just use multiple
BlocBuilders as low as possible in your widget tree and make use ofbuildWhento further control when they rebuild.