【问题标题】:Flutter - how to fetch data correctly with ChangeNotifierFlutter - 如何使用 ChangeNotifier 正确获取数据
【发布时间】:2021-08-04 16:33:15
【问题描述】:

我想在应用运行后获取整个应用的天气统计数据。

我的ChangeNotifier

class WeatherStatsNotifier extends ChangeNotifier {
  WeatherService weatherService;
  Future<WeatherStats> _stats;

  WeatherStatsNotifier({this.weatherService});

  Future<WeatherStats> get stats => _stats;

  void refreshStats() {
    _stats = weatherService.fetchWeatherStats();
    notifyListeners(); // it works when I remove this line
  }
}

我的主文件:

final WeatherService weatherService = new WeatherService();

void main() => runApp(ChangeNotifierProvider(
    create: (context) => WeatherStatsNotifier(weatherService: weatherService),
    child: App()));

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void didChangeDependencies() {
    Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
    super.didChangeDependencies();
  }

  //... build method
}

还有更深的地方:

@override
  Widget build(BuildContext context) {
    return Consumer<WeatherStatsNotifier>(
      builder: (context, value, child) {
        return FutureBuilder<WeatherStats>(
          future: value.stats,
          builder: (context, snapshot) {
            return _buildForSnapshot(snapshot);
          },
        );
      },
    );
  }

最后我得到一个错误:

======== Exception caught by widgets library =======================================================
The following assertion was thrown building ChangeNotifierProvider<WeatherStatsNotifier>(message: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.\nSee also: https://flutter.dev/docs/testing/errors, dirty, renderObject: RenderErrorBox#c84e8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE DETACHED):
'package:flutter/src/widgets/framework.dart': Failed assertion: line 4195 pos 12: '!_dirty': is not true.

如何以正确的方式做到这一点?我可以在initStatedidChangeDependencies 中使用context 吗?

【问题讨论】:

    标签: flutter dart flutter-provider


    【解决方案1】:

    当依赖改变时,即 WeatherStatsNotifier,didChangeDependencies 被调用。小部件已经被认为是脏的并且正在构建(可以有多个监听器)。

    但是您的refreshStats 正在同步通知侦听器,这意味着应该再次构建侦听器,但它们已经在构建。这可能会导致框架不一致,这就是为什么它是一个错误。这也是为什么删除这个notifyListeners(); 是“解决”问题的原因。

    解决方法是通过addPostFrameCallbackFuture.microtask 异步执行更改。

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
    
        Future.microtask(() {
          Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
        });
    
      }
    

    请注意,如果您有多个依赖项,您可能应该检查 didChangeDependencies 中的实例是否已更改。

    【讨论】:

    • 什么时候调用didChangeDependenciesdidChangeDependenciesdidWidgetUpdate 有什么区别? “状态依赖”到底是什么意思?
    • State 有一个 BuildContext 并且通过这个上下文它可以订阅其他元素并且 Provider 是这个机制的一个很好的抽象。请参阅:dependOnInheritedWidgetOfExactType。这称为依赖关系。
    • 好的,如果我理解不正确,请纠正我:didChangeDependencies 是在与状态连接的小部件发生更改时调用的?当 ChangeNotifier 中的某些内容发生更改或例如我们使用 Theme.of 并且我们更改屏幕方向时会发生这种情况?
    • 小部件更改时会调用 didWidgetUpdate。当依赖关系改变时, didChangeDependencies 被调用。 Theme.of 可以是依赖项。
    【解决方案2】:

    这是我如何使用值通知器获取数据的最佳实践

    1. 类型数据定义值通知器
    ex:
    class _AppState extends State<App> {
    ValueNotifier<String> _weatherStatsNotifier;
    
    1. 为默认值添加到初始状态(处理空值),是的,如果需要,您也可以使用上下文
    @override
    void initState() {
    super.initState();
    _weatherStatsNotifier = ValueNotifier(emtpyString);
    
    1. 然后,如果数据已经获取,则更改值
    _weatherStatsNotifier.value = weatherService;
    

    或者在我的情况下,我通常使用监听状态的值通知器

    _weatherBloc.listen(
            (state) {
              if (state is weatherLoadedState) {
                _weatherStatsNotifier.value = state.weather;
              }
            },
          );
    

    4.使用数据作为数据ex String的返回

    ValueListenableBuilder<String>(
    valueListenable: _weatherStatsNotifier,
    builder: (context, value, _) => NextScreen(
                                    Weather: value,
                                    ),
                                  ),
    

    【讨论】:

      【解决方案3】:

      考虑将您的方法封装在 SchedulerBinding.instance.addPostFrameCallback 中,以便在下一帧调用该方法,而不仅仅是在重建小部件时调用

      class _AppState extends State<App> {
        @override
        void didChangeDependencies() {
          SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
          Provider.of<WeatherStatsNotifier>(context, listen: false).refreshStats();
      }
          super.didChangeDependencies();
        }
      
      
          });
      
      
      
      

      【讨论】:

        猜你喜欢
        • 2021-04-29
        • 2021-02-16
        • 1970-01-01
        • 2020-05-26
        • 1970-01-01
        • 2021-04-10
        • 2022-11-18
        • 2020-12-17
        • 2019-06-05
        相关资源
        最近更新 更多