Differences between AnimatedBuilder and StatefulWidget in Flutter? - 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.

Related

How ChangeNotifier notify the state?

Can someone give me idea how provider notify the state?
I don't want to use ChangeNotifierProvider, Can you give me a suggestion without library?
I just need better explanation with example.
How provider combine InheritedWidget.
What do you think about the following example (inspired by an answer here) with an AnimatedBuilder:
import 'package:flutter/material.dart';
class MyChangeNotifier extends ChangeNotifier {
int count = 0;
void addOne() {
count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
final MyChangeNotifier myChangeNotifier = MyChangeNotifier();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: ExampleButton(myChangeNotifier),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: myChangeNotifier.addOne,
),
),
);
}
}
class ExampleButton extends StatelessWidget {
final MyChangeNotifier myChangeNotifier;
const ExampleButton(this.myChangeNotifier, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: myChangeNotifier,
builder: (context, child) {
return OutlinedButton(
onPressed: myChangeNotifier.addOne,
child: Text(
'Tap me - or the floating button\n\n${myChangeNotifier.count}',
textAlign: TextAlign.center,
));
});
}
}
void main() => runApp(MyApp());
The ChangeNotifier implements the Listenable class. You can see here how to listen to that Listenable, for example with an AnimatedBuilder (what my code does).
A ChangeNotifyProvider (I know, you don't want that) would also implement that for you and notify your widgets lower in the widget tree about changes.
Here is some idea for you :
widgets listen to changes and notify each other if there is a rebuild. As soon as the state changes, that particular widget rebuilds without affecting other widgets in the tree.
Three major components make all of this possible: the ChangeNotifier class in Flutter, the ChangeNotifierProvider (primarily used in our sample app), and the Consumer widgets.
Whatever change in the state observed from the ChangeNotifier class causes the listening widget to rebuild. The Provider package offers different types of providers – listed below are some of them:
The Provider class takes a value and exposes it, regardless of the value type
ListenableProvider is the specific provider used for listenable objects. It will listen, then ask widgets depending on it and affected by the state change to rebuild any time the listener is called
ChangeNotifierProvider is similar to ListenableProvider but for ChangeNotifier objects, and calls ChangeNotifier.dispose automatically when needed
ValueListenableProvider listens to a ValueListenable and exposes the value
StreamProvider listens to a stream, exposes the latest value emitted,
and asks widgets dependent on the stream to rebuild FutureProvider
takes a Future class and updates the widgets depending on it when the
future is completed
As a suggestion to learn provider from this article-
https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd

what does it mean when we see people calling widget in dart?

I have seen many times people calling widget. sth inside the code.
May I know what it is actually doing?
For example code below, (highlighted part is my confusion)
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
In flutter's StatefulWidget, we have the following architecture.
You have a StatefulWidget like this,
class MyOwnClass extends StatefulWidget {
State createState () => _MyOwnClassState();
}
And you have a State class for your StatefulWidget like this,
class _MyOwnClassState extends State<MyOwnClass> {
}
Now, State class is meant to house variables that tend to change in order for your UI to be rebuilt.
So you can have variables in your State that you can update using setState.
But what if you had some data that doesn't change and you want to avoid putting them inside the State class.
That's where your StatefulWidget comes to play.
You can store variables in your MyOwnClass and the widget variable inside the State class gives you a way to access them.
For example,
class MyOwnClass extends StatefulWidget {
int numberThatDoesntChange = 1;
State createState () => _MyOwnClassState();
}
You can access them in your State class like this,
class _MyOwnClassState extends State<MyOwnClass> {
Widget build(BuildContext context) {
return Text('$widget.numberThatDoesntChange');
}
}
Apart from this, your StatefulWidget has many more internal instance members that you can access inside of your State class using the widget variable.
The widget refers to the actual view that renders on the screen. It extends the StatefulWidget class of the flutter framework and overrides the createState() method. The createState() method is used to create the instance of state class. We will look into createState().
The state class is used to maintain the state of the widget so that it can be rebuilt again. It extends the State class of the flutter framework and overrides the build method.
The framework calls build() method again and again whenever setState() method is called. The setState() method notifies the framework that the internal state of this object has changed and it should be rebuilt. Suppose we change the value of text in StatefulWidget then we need to call setState().
Edit As Nisanth pointed outh in his comment - I missed your question completely; please ignore the below....
Let me try my answer, I don't think others are getting your point.
In your exapmle, Column(children: x) expect a list of Widgets.
You have two options - either provide this list directly:
class _MyOwnClassState extends State<MyOwnClass> {
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: <Widget>[SomeWidget()]),
),
);
}
}
Or if you have more complex code that generates widget - based on input parameters, or you have the same widget generated multiple times and you want to avoid the code duplication - you would create the separate function to do the job.
Something like:
class _MyOwnClassState extends State<MyOwnClass> {
List<Widget> makeWidgetChildren(int param) {
/*
some very complex logic here
/*
if (param>3 && param<4) {
return List<Widget>.generate(4, (index)=>SomeWidget1(index));
} else {
return <Widget>[Center()];
}
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Container(
child: Column(children: makeWidgetChildren(**widget.jsonObject)**),
),
);
}
}
So basically, it is just to make the code nicer; and to avoid having code repeated over and over again in the build function.

Does whole subtree rebuilds on setState in 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.

Update a parent widget from a child widget using flutter-provider

How do I update the state of a parent widget onTap of a child widget using provider
Currently what I am doing is pass a function from parent to child that calls the setState() function at the parent's end (Please refer the psuedocode below), but I'm looking for a more robust way of doing this.
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
_refresh() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return Container(
child: new ChildWidget(
notifyParent: _refresh(),
),
);
}
}
class ChildWidget extends StatelessWidget {
final Function notifyParent;
ChildWidget({#required this.notifyParent});
#override
Widget build(BuildContext context) {
return Container(
child: new FlatButton(
onPressed: notifyParent(), child: new Text("Update Parent")),
);
}
}
This is the simplest way I could represent the problem, the setState() in the parent (in the actual code) compels the parent widget to update its view.
In the actual code, I have the child widget handling the db queries, a click of the child widget changes the values in the db, and these values are to be updated by parent widget. Hence when I click the child widget, it updates the db then calls the notifyParent() which then leads to the parent querying the db again and updating the view.
Is there a better way of doing this without using the setState() by using the provider? How can I use say a ChangeNotifierProvider to notify of the db changes? Or is there any way I can notify the parent widget whenever something in the db is changed so that the parent can refresh itself?
Thank You.

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