【问题标题】:Flutter use ProxyProviderFlutter 使用 ProxyProvider
【发布时间】:2021-06-30 11:38:54
【问题描述】:

说明

我有一个页面(InitialElementResultScreen),它必须等待在 InitialElementResultNotifier 中完成多项操作才能显示页面的其余部分。

有效的代码

我的 InitialElementResultScreen:

class InitialElementResultScreen extends StatelessWidget {
  final String title;
  InitialElementResultScreen({
    Key key,
    @required this.title,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBarWidget.withoutMenu(title: title),
      body: _buildBody(context),
    );
  }

  Widget _buildBody(BuildContext context){

    // => For use variable in my screen I use the notifier.
    var _initialElementResultProvider = Provider.of<InitialElementResultNotifier>(context);

    return SingleChildScrollView(
      child: Column(
        children: [
          ContainerComponent(
            colorContainer: AppColors.backgroundLightBasic,
            children: [
              FutureBuilder(
                  future: _initialElementResultProvider.myFuture,
                  builder: (context, snapshot){
                    if(!snapshot.hasData) {
                      return Text('loading');
                    }else{
                      return Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          // ... some widgets
                        ],
                      );
                    }
                  }
              )
            ],
          ),
        ],
      ),
    );
  }
}

当我调用 InitialElementResultScreen 时:

ChangeNotifierProvider<InitialElementResultNotifier>(
    create: (BuildContext context) => InitialElementResultNotifier(getData: data)
)

我的 InitialElementResultNotifier 直接在其构造函数中接收外部数据。它将它们转换为对象,以便我可以对它们进行操作。

我的 InitialElementResultNotifier :

class InitialElementResultNotifier with ChangeNotifier{

  // External Notifier
  // ---------------------------------------------------------------------------

  // Variables
  // ---------------------------------------------------------------------------
  FormInitialElementModel dataReceived;
  Future myFuture;

  // Constructor
  // ---------------------------------------------------------------------------
  InitialElementResultNotifier({
    @required String getData,
  }){
    _initialise(getData);
  }

  // Initialisation
  // ---------------------------------------------------------------------------
  Future _initialise(String getData) async{
    print('--- initialise');

    dataReceived = await _convertDataReceived(getData);
    myFuture = loadingInitialElementData();
  }

  // Functions public
  // ---------------------------------------------------------------------------
  Future<void> loadingInitialElementData() async
  {
    print('---------------------- dataReceived');
    print(dataReceived); // => I see my data in the console
    // ... some operations
  }
}

不起作用的代码

在我的 loadingInitialElementData () 函数中,我想访问另一个通知程序。 LoaderNotifier 通知器根据其状态管理不同消息的显示,我将根据我在 loadingInitialElementData() 函数中的操作进行更改。

为此,我将使用代理提供商。所以现在我这样称呼我的 InitialElementResultScreen

ChangeNotifierProvider<LoaderNotifier>(
    create: (BuildContext context) => LoaderNotifier()
),
ProxyProvider<LoaderNotifier, InitialElementResultNotifier>(
    update: (BuildContext context, LoaderNotifier loaderNotifier, InitialElementResultNotifier initialElementResultNotifier) {
      return InitialElementResultNotifier(
          getData: data,
          loaderNotifier: loaderNotifier
      );
    }
),

我将更新我的 InitialElementResultNotifier

所有代码都是相同的,除了 3 行可以调用我的另一个通知程序。我在代码中将这些行标记为 cmets

class InitialElementResultNotifier with ChangeNotifier{

  // External Notifier
  // ---------------------------------------------------------------------------
  LoaderNotifier _loaderNotifier; // => New line !

  // Variables
  // ---------------------------------------------------------------------------
  FormInitialElementModel dataReceived;
  Future myFuture;

  // Constructor
  // ---------------------------------------------------------------------------
  InitialElementResultNotifier({
    @required String getData,
    LoaderNotifier loaderNotifier, // => New line !
  }){
    _loaderNotifier = loaderNotifier; // => New line !
    _initialise(getData);
  }

  // Initialisation
  // ---------------------------------------------------------------------------
  Future _initialise(String getData) async{
    print('--- initialise');

    dataReceived = await _convertDataReceived(getData);
    myFuture = loadingInitialElementData();
  }

  // Functions public
  // ---------------------------------------------------------------------------
  Future<void> loadingInitialElementData() async
  {
    print('---------------------- dataReceived');
    print(dataReceived); // => I see my data in the console
    // ... some operations
  }
}

错误

我不明白为什么会出现这个错误,我的错误是什么?

======== Exception caught by widgets library =======================================================
The following assertion was thrown building InitialElementResultScreen(dirty, dependencies: [_InheritedProviderScope<InitialElementResultNotifier>]):
Tried to use Provider with a subtype of Listenable/Stream (InitialElementResultNotifier).


This is likely a mistake, as Provider will not automatically update dependents
when InitialElementResultNotifier is updated. Instead, consider changing Provider for more specific
implementation that handles the update mechanism, such as:

- ListenableProvider
- ChangeNotifierProvider
- ValueListenableProvider
- StreamProvider

Alternatively, if you are making your own provider, consider using InheritedProvider.

If you think that this is not an error, you can disable this check by setting
Provider.debugCheckInvalidValueType to `null` in your main file:

【问题讨论】:

标签: flutter dart provider


【解决方案1】:

我想帮助你,但我觉得我很难跟上。然而,我注意到可能有帮助的一件事是,在 InitialElementResultNotifier myFuture 中已声明,但未初始化,并且在该类中没有您调用 notifyListeners 的位置,因此在 InitialElementResultScreen build 中不会调用 build dataReceivedmyFuture 最终收到值或更改。但是将FutureBuilderProvider 结合使用的整个想法对我来说是非常错误的。如果您要继续进行此特定设计,您需要查看Completers,但我建议您不要这样做。

【讨论】:

  • myFuture 在“_initialize()”函数中初始化。在我的 FutureBuilder 中,我没有直接将其调用到“loadingInitialElementData()”函数,而是通过了 myFuture 函数。这应该避免 FutureBuilder 双重调用......你不应该这样做吗?我不明白为什么提供者不擅长这样做?在futureBuilder中,除了放CircularProgressIndicator之外,我还想放一句话,显示我的加载状态的进度。
  • 当您在ChangeNotifierProvider 回调中获得InitialElementResultNotifier 的新瞬间时,您将获得null 的初始myFuture 值。然后在_initialiseFutureBuilder 中获取初始值之间存在竞争条件。可能由于这种竞争条件,您还没有遇到错误,但您可以。如果您需要这样的事情,您可以启动异步请求并稍后使用它。您必须等待它完成,因为在构造函数中调用了 _initialise,因此您没有执行此操作,或者使用 Completer
  • 另一点是你没有在设置myFuture 之后调用notifyListeners,所以如果你在异步完成之前到达FutureBuilder,你将得到null 作为你的future可能会导致不良影响,并且在设置 myFuture 之后,它不会对您的代码本身产生任何影响。
猜你喜欢
  • 2023-03-26
  • 2020-03-26
  • 1970-01-01
  • 2021-07-15
  • 2020-06-08
  • 2020-02-22
  • 2020-05-26
  • 2021-12-07
  • 1970-01-01
相关资源
最近更新 更多