【问题标题】:What is the difference between didChangeDependencies and initState?didChangeDependencies 和 initState 有什么区别?
【发布时间】:2020-02-10 18:56:05
【问题描述】:

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

现在我有两个问题:

1- 为什么在 initState 中调用我们的上下文不起作用,但在从 didChangeDependencies 调用时却起作用? (因为我在官方文档This method is also called immediately after [initState] 中读到, 并且它们都将在构建方法之前被调用。 )

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

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    从状态加载其依赖项的那一刻起,我们就可以使用状态的上下文。

    在调用 build 时,context 可供我们使用并作为参数传递。

    现在继续, initstate 在状态加载其依赖项之前被调用,因此没有可用的上下文,如果您在 initstate 中使用上下文,则会出现错误。 但是,didChangeDependencies 在状态加载其依赖项后不久被调用,并且此时上下文可用,因此您可以在此处使用上下文。

    但是,它们都在调用 build 之前被调用。 唯一的区别是一个在状态加载它的依赖之前调用,另一个在状态加载它的依赖之后调用。

    【讨论】:

    • 快速问题@SanjaySingh initState 只被调用一次,那么 didChangeDependencies 呢?因为当我使用它时,我发现自己处于一个连续的循环中!提前感谢您的回答!
    • @abrsh 你看,initstate 在我们第一次初始化数据的时候只被调用一次。我们不能一次又一次地调用 initstate 来更新数据,因为没有上下文,所以它不知道要更新哪些数据(即再次初始化)。因此,为了消除这个问题,我们使用了具有上下文并知道要更新哪些数据的 didchangedependencies。所以是的,我们可以一次又一次地调用 didchangedependencies 来更新/初始化我们的数据
    【解决方案2】:

    这是一个补充答案,显示了 OP 所描述的内容。

    StatefulWidgetState 类具有context 属性。此构建上下文首先在 didChangeDependencies 中可用。尝试在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();
      }
    }
    

    按以下顺序给出打印语句的运行:

    initState
    didChangeDependencies
    ThemeData#93b06
    build
    ThemeData#93b06
    

    另见Working with didChangeDependencies() in Flutter

    【讨论】:

    • 如果我调用 Provider 来获取信息并将其设置为变量,那么在将值分配给将用于构建以显示更新的变量之后,我们是否需要调用 setState()?例如。代码为:@override void didChangeDependencies() { chatMsgList = Provider.of&lt;ChatUserConversationProvider&gt;(context).getChatUserConversationList; setState(() {}); super.didChangeDependencies(); } 请确认并分享您的建议。谢谢。
    【解决方案3】:

    你仍然可以在 initState() 方法中使用上下文,它的 hack 但是有效,你需要做的就是寻求延迟任何你需要执行的有上下文的东西,如下所示:

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

    【讨论】:

    • 这篇文章不是关于如何获得BuildContext insideinitState
    【解决方案4】:

    initState() 在将新的 Widget 插入树时调用。 框架将为每个 [State] 对象仅调用一次此方法 它创建。这将被调用一次,因此执行只需要执行一次的工作,但请记住 context 不能在此处使用,因为小部件状态仅加载 initState() 工作已完成。

    语法:

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

    didChangeDependencies() 在此 [State] 对象的依赖项发生更改时调用。

    那么,究竟它是如何被调用的? 根据上面的定义,看起来它会在状态改变后被调用,但我们是如何知道状态改变的呢?

    示例:

    以下示例使用Provider 状态管理机制从父小部件更新子小部件。 Provider 有一个名为 updateShouldNotify 的属性,它决定是否更改状态。如果它返回true,那么只有didChangeDependenciesChildWidget 类中被调用。

    updateShouldNotify 默认在内部返回 true,因为它知道状态已更改。 那为什么我们需要 updateShouldNotify? 之所以需要,是因为如果有人想在特定条件下更新状态, 例如:如果 UI 需要仅显示 even 值,那么我们可以添加类似的条件

    updateShouldNotify: (oldValue, newValue) => newValue % 2 == 0,
    

    代码片段:

    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',
        );
      }
    }
    

    输出日志:

    I/flutter ( 3779): didChangeDependencies(), counter = 1
    I/flutter ( 3779): build(), counter = 1
    

    详细说明:

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

    【讨论】:

    • 很好的解释,请问didChangeDependencies方法是否有可能在另一种情况下调用,意味着只有在updateShouldNotify返回true时才会调用它?
    【解决方案5】:

    当此 State 对象的依赖项发生变化时调用。

    例如,如果之前对 build 的调用引用了一个后来更改的 InheritedWidget,则框架将调用此方法来通知此对象有关更改。

    这个方法也会在initState之后立即被调用。从此方法调用 BuildContext.dependOnInheritedWidgetOfExactType 是安全的。

    事实上,子类很少覆盖这个方法,因为框架总是在依赖改变后调用 build。一些子类确实会覆盖这个方法,因为当它们的依赖关系发生变化时,它们需要做一些昂贵的工作(例如,网络获取),而且每次构建都需要做这些工作。

    【讨论】:

      【解决方案6】:

      我发现initStatedidChangeDependencies 之间存在显着差异:

      • initState 只为一个小部件调用一次
      • didChangeDependencies 可以在每个小部件生命周期中被调用多次(在我的例子中,它是在键盘出现/消失时调用的)

      【讨论】:

        【解决方案7】:

        答案是here

        不应从小部件构造函数或 State.initState 方法调用此方法,因为如果继承的值发生更改,这些方法将不会被再次调用。为确保小部件在继承的值更改时正确更新自身,请仅从构建方法、布局和绘制回调或从 State.didChangeDependencies 调用(直接或间接)。

        【讨论】:

          【解决方案8】:
          1. According to initState documentation

          您不能通过此方法使用BuildContext.inheritFromWidgetOfExactType。但是didChangeDependencies会在这个方法之后立即被调用,BuildContext.inheritFromWidgetOfExactType可以在那里使用。

          所以你需要在didChangeDependencies中使用BuildContext.inheritFromWidgetOfExactType

          1. 每个小部件都有自己的context。这就是为什么您可以访问构建方法之外的上下文。

          关于build(BuildContext context)build 方法接受来自父窗口小部件的context。这意味着这个参数BuildContext context不是当前widget的上下文,而是它的父级的上下文。

          【讨论】:

          • 谢谢,我理解第二个答案,但不是第一个,我知道这种行为,但原因是什么?
          • didChangeDependencies 将在 initState 之后立即调用,好的,但是为什么我们不能在 initState 中使用 BuildContext.inheritFromWidgetOfExactType? (我热情是因为原因而不是行为)
          猜你喜欢
          • 2019-02-03
          • 2023-04-10
          • 2010-10-02
          • 2011-12-12
          • 2010-09-16
          • 2012-03-14
          • 2012-02-06
          • 2011-02-25
          • 2011-11-22
          相关资源
          最近更新 更多