简体   繁体   English

didChangeDependencies 和 initState 有什么区别?

[英]What is the difference between didChangeDependencies and initState?

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.我是 flutter 的新手,当我想在 InitState 中调用我的上下文时,它会抛出一个错误:这是关于BuildContext.inheritFromWidgetOfExactType但后来我使用了 didChangeDependencies 并且它工作正常。

Now I have 2 question:现在我有2个问题:

1- Why calling our context in initState does not work but it works when calling from didChangeDependencies? 1- 为什么在 initState 中调用我们的上下文不起作用,但在从 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. ) (因为我在官方文档中看到This method is also called immediately after [initState]的,并且它们都会在 build 方法之前调用。)

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)? 2-为什么我们可以在构建方法之外访问我们的上下文(因为我们有build(BuildContext context)并且我们可以使用我们的上下文但是在 didChangeDependencies 我们没有类似didChangeDependencies(BuildContext context)的东西,所以从哪里可以我们调用上下文来使用它)?

Context of a state is available to us from the moment the State loads its dependencies.从 State 加载其依赖项的那一刻起,我们就可以使用 state 的上下文。

At the time build is called, context is available to us and is passed as an argument.在调用 build 时,我们可以使用context并作为参数传递。

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 .现在继续,在initstate加载其依赖项之前调用 initstate ,因此没有可用的上下文,如果在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.然而, didChangeDependencies在 state 加载其依赖项后不久被调用,此时上下文可用,因此您可以在此处使用上下文。

However both of them are called before build is called.但是,它们都在调用build之前被调用。 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.唯一的区别是,一个在 state 加载其依赖项之前调用,另一个在 state 加载其依赖项之后调用。

I've found a significant difference between initState and didChangeDependencies :我发现initStatedidChangeDependencies之间存在显着差异:

  • initState is called only once for a widget.一个小部件只调用一次initState
  • didChangeDependencies may be called multiple times per widget lifecycle (in my case it was called when the keyboard appears / disappears)每个小部件生命周期可能会多次调用didChangeDependencies (在我的情况下,它是在键盘出现/消失时调用的)

initState() Called when new Widget is inserted into the tree. initState()当新的 Widget 插入树时调用。 The framework will call this method exactly once for each [State] object it creates.框架将为它创建的每个 [State] object 仅调用一次此方法。 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.这将被调用一次,因此执行只需要执行一次的工作,但请记住这里不能使用context ,因为小部件 state 仅加载完成initState()工作。

Syntax:句法:

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

didChangeDependencies() Called when a dependency of this [State] object changes. didChangeDependencies()当此 [State] object 的依赖项发生更改时调用。

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?根据上面的定义,看起来它会在 state 更改后被调用,但我们如何知道 state 已更改?

Example:例子:

The below example uses the Provider state management mechanism to update the child widget from the parent widget.下面的示例使用Provider state 管理机制从父窗口小部件更新子窗口小部件。 The Provider has an attribute called updateShouldNotify which decides whether to state is changed or not. Provider有一个名为updateShouldNotify的属性,它决定是否更改 state。 If it's returning true then only didChangeDependencies gets called in ChildWidget class.如果它返回true ,那么在ChildWidget class 中只会调用didChangeDependencies

updateShouldNotify is returning true by default internally, as it knows the state got changed. updateShouldNotify 默认在内部返回 true,因为它知道 state 已更改。 Then Why we need updateShouldNotify?那为什么我们需要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这是必要的,因为如果有人想在特定条件下更新 state,例如:如果 UI 只需要显示even数值,那么我们可以添加一个条件,例如

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: Output 日志:

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 https://medium.com/@jitsm555/differentiate-between-didchangedependencies-and-initstate-f98a8ae43164?sk=47b8dda310f307865d8d3873966a9f4f

  1. According to initState documentation根据initState文档

You cannot use BuildContext.inheritFromWidgetOfExactType from this method.您不能从此方法中使用BuildContext.inheritFromWidgetOfExactType However, didChangeDependencies will be called immediately following this method, and BuildContext.inheritFromWidgetOfExactType can be used there.但是,在这个方法之后会立即调用didChangeDependencies ,并且可以在那里使用BuildContext.inheritFromWidgetOfExactType

So you need to use BuildContext.inheritFromWidgetOfExactType in didChangeDependencies .所以你需要在didChangeDependencies中使用BuildContext.inheritFromWidgetOfExactType

  1. Every Widget has its own context .每个 Widget 都有自己的context That is why you have access to context outside build method.这就是为什么您可以访问构建方法之外的上下文的原因。

Regarding build(BuildContext context) , build method accepts context from the parent widget.关于build(BuildContext context)build方法接受来自父小部件的context It means this parameter BuildContext context is not current widget's context but its parent's context.这意味着这个参数BuildContext context不是当前widget 的context 而是它的parent 的context。

Called when a dependency of this State object changes.当此 State object 的依赖项发生更改时调用。

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.例如,如果先前的 build 调用引用了后来更改的InheritedWidget ,则框架将调用此方法来通知此 object 更改。

This method is also called immediately after initState .此方法也会在initState之后立即调用。 It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.从此方法调用BuildContext.dependOnInheritedWidgetOfExactType是安全的。

In fact Subclasses rarely override this method because the framework always calls build after a dependency changes.事实上,子类很少覆盖这个方法,因为框架总是在依赖改变后调用 build。 Some subclasses do override this method because they need to do some expensive work (eg, network fetches) when their dependencies change, and that work would be too expensive to do for every build.一些子类确实覆盖了这个方法,因为当它们的依赖关系发生变化时,它们需要做一些昂贵的工作(例如,网络获取),而且这些工作对于每次构建来说都太昂贵了。

This is a supplemental answer showing what the OP described.这是一个补充答案,显示了 OP 所描述的内容。

The State class of a StatefulWidget has a context property. StatefulWidgetState class 具有context属性。 This build context is first available in didChangeDependencies .此构建上下文首先在didChangeDependencies中可用。 Trying to use context in initState will cause an error.尝试在initState中使用context会导致错误。

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另请参阅在 Flutter 中使用 didChangeDependencies()

The notion of context being mysteriously accessible outside build() was the one that bugged me.build()之外可以神秘地访问context的概念是困扰我的一个概念。 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?如何从build()方法外部访问context

The confusion stems from the (wrong) assumption that context needed to be passed to State.build() at all.混淆源于(错误的)假设,即需要将context传递给State.build() 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 .请注意, State class 已经有一个context属性,根据文档,它在这里被冗余地提供build() ,因此它的签名与WidgetBuilder的签名匹配。 However, this is not the same build() method as that of a StatelessWidget .但是,这与StatelessWidgetbuild()方法不同。

The answer is here答案在这里

This method should not be called from widget constructors or from State.initState methods, because those methods would not get called again if the inherited value were to change.不应从小部件构造函数或 State.initState 方法调用此方法,因为如果要更改继承的值,则不会再次调用这些方法。 To ensure that the widget correctly updates itself when the inherited value changes, only call this (directly or indirectly) from build methods, layout and paint callbacks, or from State.didChangeDependencies.为确保在继承值更改时小部件正确更新自身,仅从构建方法、布局和绘制回调或 State.didChangeDependencies 调用(直接或间接)。

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:您仍然可以在 initState() 方法中使用上下文,它的 hack 但可以工作,您需要做的就是寻求延迟,无论您需要执行什么,其中包含上下文,如下所示:

 @override
  void initState() {
    Future.delayed(Duration.zero).then((_) {
      // you code with context here
    });
    super.initState();
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter 中的 initState 和类构造函数有什么区别? - What is the difference between initState and a class constructor in Flutter? 在 initState 中初始化,但在 didChangeDependencies 中出现后期错误 - Initialized in initState but late error in didChangeDependencies 是否有无状态小部件的 initState 和 didChangeDependencies 方法的替代品? - Is there a replacement for initState and didChangeDependencies methods for Stateless Widgets? 如何在弹出堆栈后调用 initstate 或 didchangedependencies - how to call initstate or didchangedependencies after pop stack 为什么在 didChangeDependencies() 方法中使用上下文安全而不在 initState() 方法中使用上下文是安全的? - Why is context safe to be used inside didChangeDependencies() method but not inside the initState() method? 在 super.initState() 之前和在 super.initState() 之后运行方法的区别 - difference between running a method before super.initState() and after super.initState() 在 initState 中为变量赋值与在 Flutter StatefulWidget 中不赋值有什么区别吗? - is there any difference between assigning value to the variable inside of initState or not in Flutter StatefulWidget? flutter 中的 initState 和 super.initState 是什么? - What is initState and super.initState in flutter? Flutter 中的 InitState 有什么问题? - What is Wrong With My InitState in Flutter? flutter 中 // 和 /// 有什么区别 - What is the difference between // and /// in flutter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM