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.
According to the Flutter official docs, didChangeDependencies() is called when a dependency of the State object changes or immediately after initState(). It is safe to call BuildContext.
InheritedWidget is a base class that allows classes that extend it to propagate information down the tree efficiently. Basically, it works by notifying registered build contexts if there is any change. Therefore, the descendant widgets that depend on it will only be rebuilt if necessary.
In flutter, the inherited widget is a base class that allows those classes to extend the information under the tree from it. Inherited widgets are also a kind of state management technique. It works by telling registered build references when a change occurs.
The build method is called any time you call setState , your widget's dependencies update, or any of the parent widgets are rebuilt (when setState is called inside of those). Your widget will depend on any InheritedWidget you use, e.g. Theme. of(context) , MediaQuery. of(context) etc.
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
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:
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...
didChangeDependencies
overrides: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();
}
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.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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With