For various reasons, sometimes the build
method of my widgets is called again.
I know that it happens because a parent updated. But this causes undesired
Flutter also has ValueListenableBuilder<T> class
. It allows you to rebuild only some of the widgets necessary for your purpose and skip the expensive widgets.
you can see the documents here ValueListenableBuilder flutter docs
or just the sample code below:
return Scaffold(
appBar: AppBar(
title: Text(widget.title)
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
ValueListenableBuilder(
builder: (BuildContext context, int value, Widget child) {
// This builder will only get called when the _counter
// is updated.
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('$value'),
child,
],
);
},
valueListenable: _counter,
// The child parameter is most helpful if the child is
// expensive to build and does not depend on the value from
// the notifier.
child: goodJob,
)
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.plus_one),
onPressed: () => _counter.value += 1,
),
);
The build method is designed in such a way that it should be pure/without side effects. This is because many external factors can trigger a new widget build, such as:
Class.of(context)
pattern) changeThis means that the build
method should not trigger an http call or modify any state.
How is this related to the question?
The problem you are facing is that your build method has side-effects/is not pure, making extraneous build call troublesome.
Instead of preventing build call, you should make your build method pure, so that it can be called anytime without impact.
In the case of your example, you'd transform your widget into a StatefulWidget
then extract that HTTP call to the initState
of your State
:
class Example extends StatefulWidget {
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
Future<int> future;
@override
void initState() {
future = Future.value(42);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
// create some layout here
},
);
}
}
I know this already. I came here because I really want to optimize rebuilds
It is also possible to make a widget capable of rebuilding without forcing its children to build too.
When the instance of a widget stays the same; Flutter purposefully won't rebuild children. It implies that you can cache parts of your widget tree to prevent unnecessary rebuilds.
The easiest way is to use dart const
constructors:
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
Thanks to that const
keyword, the instance of DecoratedBox
will stay the same even if build were called hundreds of times.
But you can achieve the same result manually:
@override
Widget build(BuildContext context) {
final subtree = MyWidget(
child: Text("Hello World")
);
return StreamBuilder<String>(
stream: stream,
initialData: "Foo",
builder: (context, snapshot) {
return Column(
children: <Widget>[
Text(snapshot.data),
subtree,
],
);
},
);
}
In this example when StreamBuilder is notified of new values, subtree
won't rebuild even if the StreamBuilder/Column do.
It happens because, thanks to the closure, the instance of MyWidget
didn't change.
This pattern is used a lot in animations. Typical uses are AnimatedBuilder
and all transitions such as AlignTransition
.
You could also store subtree
into a field of your class, although less recommended as it breaks the hot-reload feature.
You can prevent unwanted build calling, using these way
1) Create child Statefull class for individual small part of UI
2) Use Provider library, so using it you can stop unwanted build method calling
In these below situation build method call