What is the difference between didChangeDependencies and initState?
Asked Answered
B

9

82

I am new to flutter and when I want to call my context in InitState it throws an error : which is about BuildContext.inheritFromWidgetOfExactType but then I use didChangeDependencies and it works correctly.

Now I have 2 question:

1- Why calling our context in initState does not work but it works when calling from didChangeDependencies ? (because as I read in official doc This method is also called immediately after [initState], and both of them will be called before build method. )

2- Why do we have access to our context outside of build method ( because there we have build(BuildContext context) and we can use our context but in didChangeDependencies we don't have anything like didChangeDependencies(BuildContext context) , so from where can we call context to use it) ?

Barcus answered 14/10, 2019 at 7:24 Comment(0)
H
106

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.

Hoplite answered 2/11, 2019 at 5:0 Comment(2)
Quick question @Hoplite initState gets called only one time, what about didChangeDependencies? Because i have found myself in a continuous loop when i use it! Thanks in advance for your answer!Bedwarmer
@Bedwarmer Hey look, initstate is called only once when we firstly initialize the data. we cannot call initstate again and again to update the data because no context is there so it doesn't knows which data to update (i.e. initialize again). So to remove this problem we have didchangedependencies which have the context and knows which data to update. So YES we can call didchangedependencies again and again to update/initialize our dataHoplite
V
25

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)
Vallombrosa answered 17/8, 2020 at 19:6 Comment(0)
U
15

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

Urticaceous answered 20/9, 2020 at 9:10 Comment(1)
very good explanation, can I ask you that is it possible that didChangeDependencies method called in another situation, means it will be called only if updateShouldNotify returned true ?Barcus
M
7

initState() is the first method called after a widget is created. This is similar to onCreate() in Android or viewDidLoad() in iOS.

The first time the framework builds a widget, it calls didChangeDependencies() after initState(). It might call didChangeDependencies() again if your state object depends on an inherited widget that has changed.

enter image description here

source: Flutter apprentice published by kodeco

Mccants answered 11/4, 2023 at 11:23 Comment(0)
L
4
  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.

Lardaceous answered 14/10, 2019 at 14:7 Comment(2)
Thanks, I understand the second answer , but not the first, I know this behavior but What is the reason?Barcus
didChangeDependencies will be called immediately following initState, ok but why we can't use BuildContext.inheritFromWidgetOfExactType in initState? (I am enthusiastic for reason not the behavior)Barcus
F
4

The notion of context being mysteriously accessible outside build() was the one that bugged me. I think clarifying this subtle point supplements explanations given in other answers about the first question.

How is context accessible from outside the build() method?

The confusion stems from the (wrong) assumption that context needed to be passed to State.build() at all. Note that the State class already has a context property and according to the documentation, it is redundantly provided to build() here, so that its signature matches that of a WidgetBuilder. However, this is not the same build() method as that of a StatelessWidget.

Fibered answered 28/5, 2022 at 19:54 Comment(0)
S
1

This is a supplemental answer showing what the OP described.

The State class of a StatefulWidget has a context property. This build context is first available in didChangeDependencies. Trying to use context in initState will cause an error.

class HomeWidget extends StatefulWidget {
  const HomeWidget({Key key}) : super(key: key);

  @override
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  @override
  void initState() {
    print('initState');
    // print(Theme.of(context));        // ERROR!
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print('didChangeDependencies');
    print(Theme.of(context));           // OK
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    print('build');
    print(Theme.of(context));           // OK
    return Container();
  }
}

Running that gives the print statements in the following order:

initState
didChangeDependencies
ThemeData#93b06
build
ThemeData#93b06

See also Working with didChangeDependencies() in Flutter

Strange answered 13/1, 2021 at 4:55 Comment(1)
If I call a Provider to get information and set it to variable then will we need to call setState() after assigning value to variable which will be used in build to show updates? eg. code is as: @override void didChangeDependencies() { chatMsgList = Provider.of<ChatUserConversationProvider>(context).getChatUserConversationList; setState(() {}); super.didChangeDependencies(); } Kindly confirm and share your suggestion. Thanks.Lakieshalakin
O
0

Called when a dependency of this State object changes.

For example, if the previous call to build referenced an InheritedWidget that later changed, the framework would call this method to notify this object about the change.

This method is also called immediately after initState. It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.

In fact Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.

Octofoil answered 2/9, 2020 at 4:34 Comment(0)
U
-4

you can still use context in initState() method, its hack buts works, all you need to do is sought of delay whatever you will need to execute that has context in it like so:

 @override
  void initState() {
    Future.delayed(Duration.zero).then((_) {
      // you code with context here
    });
    super.initState();
  }
Utilize answered 12/11, 2020 at 16:12 Comment(1)
The post isn't about how to get the BuildContext insideinitState.Canterbury

© 2022 - 2024 — McMap. All rights reserved.