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 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


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.

Tags:

Flutter