Understanding Flutter didChangeDependencies mechanics
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 tbhload
is probably not the right word I'm using, butresolve
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 allInheritedWidgets
)? It uses bothof
andmaybeOf
to register theInheritedWidget
dependencies, includingMediaQuery
,Directionality
,DefaultAssetBundle
,TickerMode
,Localizations
. So when these dependencies change/ get updated,Image
'sdidChangeDependencies
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
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.