在 Widget 的 initState 中使用 Provider 或初始化生命周期

Posted

技术标签:

【中文标题】在 Widget 的 initState 中使用 Provider 或初始化生命周期【英文标题】:Using Provider in Widget's initState or initialising life-cycle 【发布时间】:2021-07-14 13:37:47 【问题描述】:

所以在学习 Flutter 的过程中,initState() 似乎不是使用 Providers 的地方,因为它还没有访问必须通过的context。我的导师解决这个问题的方法是使用didChangeDependencies() 生命周期钩子和一个标志,这样里面的任何代码都不会运行超过一次:

bool _isInit = true;

@override
  void didChangeDependencies() 
    if (_isInit) 
      // Some provider code that gets/sets some state
    
    _isInit = false;
    super.didChangeDependencies();
  

这对我来说是一次糟糕的开发体验。没有其他方法可以在可以访问context 的 Flutter Widget 中运行初始化代码吗?或者有没有计划推出更实用的东西?

我见过的唯一其他方法是使用Future.delayed,感觉有点“hacky”:

@override
  void initState() 
    Future.delayed(Duration.zero).then(() 
      // Some provider code that gets/sets some state 
    );
    super.initState();
  

【问题讨论】:

【参考方案1】:

我在 didChangeDependencies 里面实现了如下

 @override
void didChangeDependencies() 
    super.didChangeDependencies();
    if (_isInit) 
      setState(() 
        _isLoading = true;
      );
      Provider.of<Products>(context).fetchAndSetProducts().then((_) 
        setState(() 
          _isLoading = false;
        );
      );
    
    _isInit = false;
  
       

【讨论】:

据我了解,这样做的问题是您最终可能会重建不依赖 Provider 数据的嵌套子级。因此,在一个只需要一些数据一次(不监听)的小部件中,如果我使用 Consumer 它可能会导致浪费的重新渲染。在您的示例中,每次更改 Provider 数据时都会重新渲染整个应用程序。 我已经编辑了我的答案可能对你有帮助 您对问题的更新与我在问题中提出的在初始化程序中获取数据的机制相同。您包含的添加的业务逻辑甚至看起来像是直接从我一直关注的教程中提取的。我正在寻找使用变量标志 + didChangeDependencies() 的替代方法。【参考方案2】:

可以安排代码在当前帧结束时运行。如果安排在initState() 内,则似乎在代码运行时 Widget 已完全设置。

为此,您可以使用SchedulerBinding 实例的addPostFrameCallback 方法:

@override
  void initState() 
    super.initState();

    SchedulerBinding.instance.addPostFrameCallback((_) 
      // Some provider code that gets/sets some state 
    )
  

您也可以为此使用WidgetsBinding.instance.addPostFrameCallback()。它们在构建/加载 Widget 后运行代码一次的行为相同,但here 提供了更多关于差异的详细信息。

注意:一定要导入SchedulerBinding所需的文件:

import 'package:flutter/scheduler.dart';

【讨论】:

【参考方案3】:

您可以将 Provider 放在一个单独的函数中,并在 initState() 中调用该函数

bool isInit = true;

Future<void> fetch() async 
    await Provider.of<someProvider>(context, listen: false).fetch();


@override
  void initState() 
    if (isInit) 
      isInit = false;
      fetch();
    
  isInit = false;
  super.initState();
    

【讨论】:

以上是关于在 Widget 的 initState 中使用 Provider 或初始化生命周期的主要内容,如果未能解决你的问题,请参考以下文章

如何测试 initState() 中有异步调用的 Flutter 应用程序?

如何在 initstate() 中读取和使用共享偏好值?我可以在其他小部件中读取和使用值,但不能在我在 initstate 中调用的 API 中读取和使用值

Flutter遇到的坑

Flutter的生命周期

Flutter的生命周期

如何在 initState 方法中访问颤振块?