What is the difference between didChangeDependencies and initState?

Context of a state is available to us from the moment the State loads its dependencies.

At the time build is called, context is available to us and is passed as an argument.

Now moving on, initstate is called before the state loads its dependencies and for that reason no context is available and you get an error for that if you use context in initstate. However, didChangeDependencies is called just a few moments after the state loads its dependencies and context is available at this moment so here you can use context.

However both of them are called before build is called. The only difference is that one is called before the state loads its dependencies and the other is called a few moments after the state loads its dependencies.


I've found a significant difference between initState and didChangeDependencies:

  • initState is called only once for a widget.
  • didChangeDependencies may be called multiple times per widget lifecycle (in my case it was called when the keyboard appears / disappears)

initState() Called when new Widget is inserted into the tree. The framework will call this method exactly once for each [State] object it creates. This will be called once so perform work which required to be performed only once, but remember context can't be used here, as widget state gets loaded only initState() work is done.

Syntax:

@override
  void initState() {
    debugPrint('initState()');
    super.initState();
  }

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 gets called in ChildWidget class.

updateShouldNotify is returning true by default internally, as it knows the state got changed. Then Why we need updateShouldNotify? it's need 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 detail explanation:

https://medium.com/@jitsm555/differentiate-between-didchangedependencies-and-initstate-f98a8ae43164?sk=47b8dda310f307865d8d3873966a9f4f


  1. According to initState documentation

You cannot use BuildContext.inheritFromWidgetOfExactType from this method. However, didChangeDependencies will be called immediately following this method, and BuildContext.inheritFromWidgetOfExactType can be used there.

So you need to use BuildContext.inheritFromWidgetOfExactType in didChangeDependencies.

  1. Every Widget has its own context. That is why you have access to context outside build method.

Regarding build(BuildContext context), build method accepts context from the parent widget. It means this parameter BuildContext context is not current widget's context but its parent's context.