【问题标题】:Widgets not updating when modifying a Riverpod Provider from outside the UI从 UI 外部修改 Riverpod 提供程序时小部件未更新
【发布时间】:2021-01-30 13:51:05
【问题描述】:

我正在尝试从 UI 外部更新我的 Provider 内部的值,如文档中所述:

final container = riverpod.ProviderContainer();
AppProvider _appProvider = container.read(appProvider);
_appProvider.setMode(true);

在我的 setMode 方法中,我调用 notifyListeners()。现在的问题是我的小部件没有重建,即使我的提供程序中的值成功更改并通知了它的侦听器。小部件是这样监听的:

riverpod.Consumer(builder: (context, watch, child) {
    AppProvider _appProvider = watch(appProvider);
...

当从用户界面内部更新提供程序时,小部件会按预期重建。

在这种情况下,我还需要做什么才能使我的 UI 正确重建?

【问题讨论】:

    标签: flutter provider riverpod


    【解决方案1】:

    在这种情况下,文档有些误导。确实,您可以在没有上下文的情况下以这种方式访问​​Provider,但您也在实例化一个新的ProviderContainer,这是存储所有提供者状态的地方。通过这样做,您正在创建然后修改一个新的Notifier;这意味着您的小部件正在收听的Notifier 保持不变。

    您可以在小部件树之外使用提供程序的一种方法是在ProviderScope 之外声明您的ProviderContainer。小心这一点,因为它可能会导致意想不到的后果。

    将您的 main.dart 代码替换为:

    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    //Provider container which holds the state of all my providers
    //This would normally be inaccessible inside of the ProviderScope
    final providerContainer = ProviderContainer();
    
    //A function that accesses and uses myNotifierProvider ***Without needing a context***
    void incrementCountWithoutContext() {
      var provider = providerContainer.read(myNotifierProvider);
      provider.incrementCount();
    }
    
    final myNotifierProvider = ChangeNotifierProvider((_) {
      return MyNotifier();
    });
    
    class MyNotifier extends ChangeNotifier {
      int count = 0;
    
      void incrementCount() {
        count++;
        notifyListeners();
      }
    }
    
    void main() {
      runApp(
        //Here is where we pass in the providerContainer declared above
        UncontrolledProviderScope(
          container: providerContainer,
          child: MyApp(),
        ),
      );
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends ConsumerWidget {
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        final _provider = watch(myNotifierProvider);
    
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                Text(
                  '${_provider.count}',
                  style: Theme.of(context).textTheme.headline4,
                ),
                ElevatedButton(
                  //Increment count by accessing the provider the usual way
                  onPressed: _provider.incrementCount,
                  child: Text('Increment count the usual way'),
                ),
                ElevatedButton(
                  //Increment the count using our global function
                  //Notice no context is passed to this method
                  onPressed: incrementCountWithoutContext,
                  child: Text('Increment count without context'),
                )
              ],
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 我明白了,据我了解,Riverpod 的主要卖点是可以从任何地方访问提供商,但我想这并不完全正确,仅对测试有用。按照您展示的方式进行操作!但我猜可能不是我们应该做的事情。那还不如坚持使用Provider。
    • @Chrispy11 Riverpod 从未声称您可以在“任何地方”访问提供商。实际上,这是您无法做到的功能。否则这只是一个全局变量,不利于可维护性。此处给出的代码是正确的(尽管我建议不要将 ProviderContainer 作为全局变量)。
    • @RémiRousselet,如果不使用全局 ProviderContainer 变量,您对如何在 ui 之外调用提供程序有什么建议吗?这一次,我需要从 ui 外部调用它以获取 FirebaseMessaging 背景消息。
    • 我也想知道这个 - 你收到回复了吗?
    猜你喜欢
    • 2020-01-13
    • 2021-07-18
    • 2023-02-14
    • 2021-08-17
    • 2021-11-29
    • 2021-11-22
    • 2021-10-12
    • 2021-02-25
    • 1970-01-01
    相关资源
    最近更新 更多