Does whole subtree rebuilds on setState in flutter - flutter

I am new to flutter and really wondering if all the subtree of widgets gets rebuild when we call setState.
Subtree here means all the widget tree below that widget (including that widget as root node).
When we call setState function, the build method is called on the root node of the subtree, which triggers the build methods on its child. Say a branch (here MyWidget1) of a subtree (a child of that widget) is independent of the state variables. I noticed that even independent branches are rebuilt on setState called in the parent node.
class _MyAppState extends State<MyApp> {
int count=0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[ MyWidget1(),MyWidget2(count),],),
floatingActionButton: FloatingActionButton(onPressed: ()=>setState((){count++;}),),
);
}
}
class MyWidget1 extends StatelessWidget {
#override
Widget build(BuildContext context) { print("widget builds 1");
return Container(height: 100, color: Colors.orange,);
}
}
class MyWidget2 extends StatelessWidget {
final int count;
MyWidget2(this.count);
#override
Widget build(BuildContext context) { print("widget builds 2");
return Text(count.toString());
}
}
Here we can see that MyWidget1 is independent of the state variable (here count), so generally, setState should have no impact on it.
I was wondering if there should be any optimization to avoid that useless build of MyWidget1 on the call of setState function. As the tree below MyWidget1 can be too big, that too will be rebuild again.
My Questions:
Is it Ok for this Independent Widget (here MyWidget1) to build again on setState?
Is there a better way to deal with this situation to avoid its rebuild.
Note: I have read this question
In this question, there is a way to avoid useless build by creating an instance of the independent branch outside the build method,
My doubt is :
Is this the WAY to deal with this situation or some other better way or this situation isn't that big at all as tree builds in O(n) time (which I think shouldn't be the answer because building tree might be O(n) operation but it may include many time-consuming operations which may not be optimization friendly to call again and again uselessly).

Yes, MyWidget1 is rebuilt upon that setState. Just trust the code. After you call setState, build is called, which calls the constructor of MyWidget1. After each setState, the entire subtree is rebuilt. Old widgets are thrown away. States are not thrown away, though. State instances live on, they are not recreated (see didUpdateWidget).
So, yes. After each setState, the entire subtree is rebuilt.
This is OK, don't worry.
The widget classes here are very lightweight classes. Dart's garbage collector is optimized to instantiate many such objects and throw them away together.
This tree that you get to recreate again and again is just a facade. There are two more parallel trees that are not lightweight and are not recreated. Your widget trees are diff'ed together to find how the actual ui elements should be modified by the system.
Why all this trouble, you may ask. Because creating trees is easy and maintaining them is difficult. This reactive declarative framework lets us get away with only creating the tree and not maintaining it.
There are some resources about Flutter internals that you can read more about this. One such resource is this video: https://www.youtube.com/watch?v=996ZgFRENMs

class _MyAppState extends State<MyApp> {
int count=0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[ const MyWidget1(),MyWidget2(count),],),
floatingActionButton: FloatingActionButton(onPressed: ()=>setState((){count++;}),),
);
}
}
class MyWidget1 extends StatelessWidget {
const MyWidget1();
#override
Widget build(BuildContext context) { print("widget builds 1");
return Container(height: 100, color: Colors.orange,);
}
}
class MyWidget2 extends StatelessWidget {
final int count;
MyWidget2(this.count);
#override
Widget build(BuildContext context) { print("widget builds 2");
return Text(count.toString());
}
}
when the constructor starts with a "const" keyword, which allows
you to cache and reuse the widget.
When calling the constructor to initiate the widget, use the "const" keyword. By calling with the "const" keyword, the widget does not rebuild when any parent widgets change
their state in the tree. If you omit the "const" keyword, the widget will be build every time the parent
widget redraws.

Related

Differences between AnimatedBuilder and StatefulWidget in Flutter?

From my point of view, all animations continuously render the widget with some often-changed value. For example, a spinning hand on a clock has a value called 'angle' to indicate its position.
In Flutter, it seems that StatefulWidget is enough for it. My question is:
What functions do AnimatedBuilder/AnimatedWidget have?
What are the differences between AnimatedBuilder/AnimatedWidget and StatefulWidget?
I'll assume that AnimationBuilder is AnimatedBuilder because there is no such class as AnimationBuilder in the Flutter SDK.
Short answer
There are no differences besides the class names and the parameters.
Long answer
In Flutter, it seems that StatefulWidget is enough for it.
You are right.
What functions do AnimatedBuilder/AnimatedWidget have?
Nothing special, they are classes that exists only to wrap common/boilerplate code, see:
AnimatedWidget: flutter/lib/src/widgets/transitions.dart is simply a StatefulWidget that takes a listenable and triggers the setState whenever the listanable notifies a change.
The AnimatedBuilder: flutter/lib/src/widgets/transitions.dart is a subclass of ListenableBuilder which is a subclass of AnimatedWidget (!), the only difference is that AnimatedBuilder uses your callback as the build method of AnimatedWidget.
That being said, lets go to the code:
AnimatedBuilder is simply a StatefulWidget that uses your callback function (builder: (...) { }) as build method. It also triggers setState everytime the Listenable (animation) notifies a change.
Widget build(BuildContext context) {
return Center( // The [Center] widget instance will not rebuild.
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return /* Widge tree that will rebuild when [animation] changes. */;
},
),
);
}
The equivalent code using AnimatedWidget is:
Widget build(BuildContext context) {
return Center( // The [Center] widget instance will not rebuild.
child: MyAnimatedWidget(animation: animation),
);
}
// ...
class MyAnimatedWidget extends AnimatedWidget {
const MyAnimatedWidget({required Listenable animation}) : super(listenable: animation);
Widget build(BuildContext context) {
return /* Widge tree that will rebuild when [animation] changes. */;
}
}
What are the differences between AnimatedBuilder/AnimatedWidget and StatefulWidget?
As I said, there is no semantic or real difference. AnimatedWidget and AnimatedBuilder are only abstractions of StatefulWidget.

Which is better when using provider instance in a new widget?

Let's say I've written my code as below.
I've got a provider called SampleProvider, and I'm using it in my main widget.
class SampleProvider extends ChangeNotifier {}
class MainWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
SampleProvider provider = Provider.of<SampleProvider>(context);
}
}
And then, I want to make a new widget and use this provider in the new widget.
There will be two choices.
First, I just instantiate another provider in the new widget as below.
class NewWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
SampleProvider provider = Provider.of<SampleProvider>(context);
}
}
Or, I can send it from the main widget to the new widget as a constructor parameter.
Like this:
class NewWidget extends StatelessWidget {
final SampleProvider provider;
NewWidget(this.provider);
#override
Widget build(BuildContext context) {
}
}
I guess the first option is better because flutter draws a widget based on its build context, but I'm not sure.
I've googled it quite long, but there was no success.
Can anybody tell me whether I am right or wrong? Or Do they have no difference?
Prefer the first solution, it's easier to refactor.
Suppose you need move NewWidget in your widget tree, you also need to modify the "paramter pass" code if you choose second solution, which is not necessary with first solution.
One of Provider pacakage's purpose is avoid passing parameter deep in the widget tree by the way.
Depend on preference not like first or second one.
Have an exception when obtaining Providers inside initState. What can I do?
This exception happens because you're trying to listen to a provider from a life-cycle that will never ever be called again.
It means that you either should use another life-cycle (build), or explicitly specify that you do not care about updates.
As such, instead of:
initState() {
super.initState();
print(context.watch<Foo>().value);
}
you can do:
Value value;
Widget build(BuildContext context) {
final value = context.watch<Foo>.value;
if (value != this.value) {
this.value = value;
print(value);
}
}
which will print value whenever it changes (and only when it changes).
Alternatively, you can do:
initState() {
super.initState();
print(context.read<Foo>().value);
}
SRC: https://github.com/rrousselGit/provider#i-have-an-exception-when-obtaining-providers-inside-initstate-what-can-i-do
Yes, I believe the first option is the better way, of the top of my head I can't think of any situation in which you would prefer the second option to the first.
If you don't use new widget as children of any other widget , first choice is better .
otherwise , second is better .

Is it ok to nest ConsumerWidgets in riverpods?

In Riverpods, is it ok to nest ConsumerWidgets? Something like this? I want to do so to keep my apis separated, but it feels like it might slow down performance?
class Foo1 extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
// watch something
return Container(
child: Foo2(),
);
}
}
class Foo2 extends ConsumerWidget {
#override
// watch something
Widget build(BuildContext context, ScopedReader watch) {
return Container();
}
}
This is completely and totally fine. It would not be strange if most or all widgets in your application were ConsumerWidgets.
As long as you only watch providers when/where needed, you have nothing to be concerned about.
There are many ways to optimize rebuilds. For example, creating providers that only expose one aspect of state your widget may depend on or using Consumer to only rebuild certain children when the value exposed by a provider changes. If you're using hooks, you can use select which is super handy for conditionally rebuilding.

Trying to access state from widget, probably using wrong design

I'm learning flutter and trying to make a kind of MutableImage widget. The idea is to make a MutableImage StatefulWidget that would rebuild when a new image is provided. I try to avoid rebuilding the whole widget tree each time the image is changed because that seems overkill, and I plan to update the image several times per second. So I want to rebuild only that MutableImage widget.
So here is the code I have, with comments to explain where I'm stuck :
class MutableImage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MutableImageState();
}
void updateImage(List<int> bytes) {
// !!!!!! Would like to call this method here, but state is not available from Widget, which means I want to do something wrong, but not sure exactly how I should do it...
// this.state.updateImage(bytes);
}
}
class MutableImageState extends State<MutableImage> {
List<int> _bytes;
void updateImage(List<int> bytes) {
setState(() {
_bytes=bytes;
});
}
#override
Widget build(BuildContext context) {
if ((_bytes==null)||(_bytes.length==0)) {
return Center(child: CircularProgressIndicator());
}
return Image.memory(_bytes);
}
}
Then the idea was to use this widget like this for example in another stateful widget
MutableImage _mutableImage;
#override
Widget build(BuildContext context) {
if (_mutableImage == null) _mutableImage=MutableImage();
return : Row( //using Row as an example, the idea is that the mutable Image is deep into a tree of widgets, and I want to rebuild only _mutableImage when image changes, not all widgets.
children : <Widget>[
child0, child1, _mutableImage, child3, child4
]
);
}
void updateImage(List<int> bytes) {
_mutableImage?.updateImage(bytes);
}
So is there a good way to do this ? I'm quite confused, thx for any help/hint.
This is a place for an application of a GlobalKey. In the parent Widget of MutableImage make a global key and pass that to MutableImage. With that key you can access MutableImage state by using .currentState on the key and calling updateImage.
You'll have to add key as an argument of the MutableImage constructor and call super(key: key). updateImage should also be moved the the state of MutableImage.
Key:
final GlobalKey<MutableImageState> _imageKey = GlobalKey<MutableImageState>();
Pass the key:
MutableImage(key: _imageKey);
Access the state:
_imageKey.currentState.updateImage();

Understanding Flutter didChangeDependencies mechanics

After reading the docs here and the State lifecycle here, I am still not sure about how didChangeDependencies works.
As far as I understand it will be triggered after initState and after any change in an InheritedWidget, but what are these changes? I think it's important to understand what changes trigger didChangeDependencies, so we can understand when and how to use it properly.
When Flutter calls updateShouldNotify() and it returns true, then widgets that requested an inherited widget in build() previously are notified by didChangeDependencies being called.
updateShouldNotify should return true if its state changed since the last time it was called.
TLDR
As the creator of the Widget, you set it's dependencies (InheritedWidget) by using of or maybeOf, etc., so you should understand when they call updateShouldNotify (or they're dependencies call it). It's quite complex:
Usage
You usually don't have to override this method in State because your widget will rebuild when a dependency changes anyway. If you want to do expensive work, like making a network request, then you would avoid making that network request in a normal build, and put this expensive work in didChangeDependencies instead. This allows you to only make these expensive operations when a dependency changes. Tbh, I struggled to think of a situation where you'd want to make an expensive operation purely if an InheritedWidget changes if you're properly structuring your app's logic into services. So I looked at the Flutter framework internals...
Examples of didChangeDependencies overrides:
Flutter's Image widget (the comments are my commentary, and tbh load is probably not the right word I'm using, but resolve isn't much better IMHO)
#override
void didChangeDependencies() {
_updateInvertColors(); // Checks if the image should inverted or not.
_resolveImage(); // Reloads the image if necessary?
if (TickerMode.of(context)) // This method returns a bool: Whether tickers in the given subtree should be enabled or disabled.
_listenToStream(); // Keep loading the image
else
_stopListeningToStream(keepStreamAlive: true); // Be efficient and not read from the stream if not needed. (ticker is false)
super.didChangeDependencies();
}
And when is didChangeDependencies called? aka. what are Image's dependencies (which are all InheritedWidgets)? It uses both of and maybeOf to register the InheritedWidget dependencies, including MediaQuery, Directionality, DefaultAssetBundle, TickerMode, Localizations. So when these dependencies change/ get updated, Image's didChangeDependencies will be called.
What is a dependency?
You make a widget depend on a subtype of InheritedWidget with InheritedWidgetType.of(context), which internally calls context.dependOnInheritedWidgetOfExactType<InheritedWidgetType>();. So the widget has a dependency on that InheritedWidget. For example, Theme.of(BuildContext context) can be seen here.
From the docs inside the Flutter Framework's comments about dependOnInheritedWidgetOfExactType:
Obtains the nearest widget of the given type T, which must be the
type of a concrete [InheritedWidget] subclass, and registers this
build context with that widget such that when that widget changes (or
a new widget of that type is introduced, or the widget goes away),
this build context is rebuilt so that it can obtain new values from
that widget.
Once a widget registers a dependency on a particular type by calling
this method, it will be rebuilt, and [State.didChangeDependencies]
will be called, whenever changes occur relating to that widget until
the next time the widget or one of its ancestors is moved (for
example, because an ancestor is added or removed).
There's more docs, which are really interesting, have a read.
Jitesh's answer is currently just wrong. Dependencies are not Widget's state, they are only relevant for InheritedWidget.
didChangeDependencies() Called when a dependency of this [State] object changes.
So, exactly How it gets called? as by the above definition, it looks like it will be called after state changes but how we come to know the state is changed?
Example:
The below example uses the Provider state management mechanism to update the child widget from the parent widget. The Provider has an attribute called updateShouldNotify which decides whether to state is changed or not. If it's returning true then only didChangeDependencies get called in ChildWidget class.
updateShouldNotify is returning true by default internally, as it knows the state got changed.
Why do we need updateShouldNotify?
It’s needed because if someone wants to update the state on a specific condition. Eg: if UI required to show only even values then we can add a condition like
updateShouldNotify: (oldValue, newValue) => newValue % 2 == 0,
Code Snippet:
class ParentWidget extends StatefulWidget {
ParentWidget({Key key, this.title}) : super(key: key);
final String title;
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Life Cycle'),
),
body: Provider.value(
value: _counter,
updateShouldNotify: (oldValue, newValue) => true,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Press Fab button to increase counter:',
),
ChildWidget()
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ChildWidget extends StatefulWidget {
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
int _counter = 0;
#override
void initState() {
print('initState(), counter = $_counter');
super.initState();
}
#override
void didChangeDependencies() {
_counter = Provider.of<int>(context);
print('didChangeDependencies(), counter = $_counter');
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
print('build(), counter = $_counter');
return Text(
'$_counter',
);
}
}
Output Logs:
I/flutter ( 3779): didChangeDependencies(), counter = 1
I/flutter ( 3779): build(), counter = 1
For more info:
https://medium.com/#jitsm555/differentiate-between-didchangedependencies-and-initstate-f98a8ae43164