试图从小部件树外部侦听提供者公开的值

Posted

技术标签:

【中文标题】试图从小部件树外部侦听提供者公开的值【英文标题】:Tried to listen to a value exposed with provider, from outside of the widget tree 【发布时间】:2020-05-21 20:17:52 【问题描述】:

所以,我有一个颤振项目,我正在尝试构建一个卡片列表,其中的内容取决于我的 OrderModel 类,我正在尝试使用 Provider 来实现这一点,但我收到了这个错误:

════════调度程序库捕获的异常══════════════════════════

试图从小部件树的外部侦听提供者公开的值。

这可能是由调用的事件处理程序(如按钮的 onPressed)引起的 Provider.of 不通过listen: false

要修复,请编写: Provider.of(context, listen: false);

它不受支持,因为可能会毫无意义地重建与 事件处理程序,当小部件树不关心值时。 '包:提供者/src/provider.dart': 断言失败:第 193 行 pos 7:'context.owner.debugBuilding ||听 == 假 || _debugIsInInheritedProviderUpdate'

当异常被抛出时,这是堆栈

#2 提供者.of 包:provider/src/provider.dart:193

#3 _OrderHistoryState._onAfterBuild 包:shinier_store/screens/order_history.dart:60

#4 _OrderHistoryState.build。 包:shinier_store/screens/order_history.dart:67

#5 SchedulerBinding._invokeFrameCallback 包:flutter/…/scheduler/binding.dart:1102

#6 SchedulerBinding.handleDrawFrame 包:flutter/…/scheduler/binding.dart:1049 ...

════════════════════════════════════════════════ ══════════ _onAfterBuild() 处的 Provider 调用的 Stacktrace

I/flutter ( 3092): #0      _AssertionError._doThrowNew  (dart:core-patch/errors_patch.dart:42:39)
I/flutter ( 3092): #1      _AssertionError._throwNew  (dart:core-patch/errors_patch.dart:38:5)
I/flutter ( 3092): #2      Provider.of package:provider/src/provider.dart:193
I/flutter ( 3092): #3      _OrderHistoryState._onAfterBuild package:shinier_store/screens/order_history.dart:61
I/flutter ( 3092): #4      _OrderHistoryState.build.<anonymous closure> 
package:shinier_store/screens/order_history.dart:71
I/flutter ( 3092): #5      SchedulerBinding._invokeFrameCallback 
package:flutter/…/scheduler/binding.dart:1102
I/flutter ( 3092): #6      SchedulerBinding.handleDrawFrame 
package:flutter/…/scheduler/binding.dart:1049
I/flutter ( 3092): #7      SchedulerBinding._handleDrawFrame 
package:flutter/…/scheduler/binding.dart:957
I/flutter ( 3092): #8      _rootRun  (dart:async/zone.dart:1126:13)
I/flutter ( 3092): #9      _CustomZone.run  (dart:async/zone.dart:1023:19)
I/flutter ( 3092): #10     _CustomZone.runGuarded  (dart:async/zone.dart:925:7)
I/flutter ( 3092): #11     _invoke  (dart:ui/hooks.dart:259:10)
I/flutter ( 3092): #12     _drawFrame  (dart:ui/hooks.dart:217:3)

我不知道如何解决这个问题,因为我在 Provider 调用中添加了 listen:false。我尝试使用 WidgetBinding,因为我认为应该在构建完成后进行 Provider 调用,但这似乎并没有解决问题。

代码如下:

OrderModel 类

class OrderModel extends ChangeNotifier 
  List<Order> myOrders;
  bool isLoading = true;
  String errMsg;
  int page = 1;
  bool endPage = false;

  void getMyOrder(UserModel userModel) async 
    try 
      isLoading = true;
      notifyListeners();
      myOrders = await WooCommerce().getMyOrders(userModel: userModel, page: 1);
      page = 1;
      errMsg = null;
      isLoading = false;
      endPage = false;
      notifyListeners();
     catch (err) 
      errMsg =
          "There is an issue with the app during request the data, please contact admin for fixing the issues " +
              err.toString();
      isLoading = false;
      notifyListeners();
    
  

  void loadMore(UserModel userModel) async 
    try 
      isLoading = true;
      page = page + 1;
      notifyListeners();
      var orders =
          await WooCommerce().getMyOrders(userModel: userModel, page: page);
      myOrders = [...myOrders, ...orders];
      if (orders.length == 0) endPage = true;
      errMsg = null;
      isLoading = false;
      notifyListeners();
     catch (err) 
      errMsg =
          "There is an issue with the app during request the data, please contact admin for fixing the issues " +
              err.toString();
      isLoading = false;
      notifyListeners();
    
  

order_history.dart - 状态类

class _OrderHistoryState extends State<OrderHistory> 
  void _onAfterBuild(BuildContext context)
     Provider.of<OrderModel>(context, listen: false)
        .getMyOrder(userModel: Provider.of<UserModel>(context));
  

  @override
  Widget build(BuildContext context) 
    var formatter = DateFormat('dd-MM-yyyy');
    var model = Provider.of<OrderModel>(context);
    WidgetsBinding.instance.addPostFrameCallback((_) => _onAfterBuild(context));

    return Scaffold(
      appBar: AppBar(
          title: Text(
            'Order History',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          elevation: 0.0),
      body: model.myOrders == null ? Center() :
      Padding(
        padding: const EdgeInsets.all(10.0),
        child: ListView.separated(
          separatorBuilder: (_, __) => SizedBox(height: 10.0),
          itemCount: model.myOrders.length,
          itemBuilder: (context, index) 
            String stat = model.myOrders[index].status;
            return Card(
              color: _buildColor(stat),
              elevation: 3.5,
              ...
  

main.dart - 构建方法

  @override
  Widget build(BuildContext context) 
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => UserModel()),
        ChangeNotifierProvider(create: (_) => CartModel()),
        ChangeNotifierProvider(create: (_) => SearchModel()),
        ChangeNotifierProvider(create: (_) => OrderModel()),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primaryColor: Colors.white,
          backgroundColor: Colors.white,
          canvasColor: Colors.white,
        ),
        home: MainTabs(),
        debugShowCheckedModeBanner: false,
        localizationsDelegates: [
          i18n,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],
        supportedLocales: i18n.supportedLocales,
      ),
    );
  

【问题讨论】:

你能包括整个堆栈跟踪吗?目前还不清楚您的例外是在哪一行获得的 使用堆栈跟踪编辑@RémiRousselet 啊,是的,您将标志传递给了 Provider.of 调用之一,但还有第二个,它没有标志。两者都需要它 我之前做过,但它也不起作用@RémiRousselet 实际上,我在代码的许多部分都遇到了提供程序的问题,颤动只是不允许我使用 notifyListeners 更新与提供程序的模型.我只使用了一些提供者,因为我使用了 listen: false 但这与根本不使用 notifyListeners 是一样的。它不断地从这个线程的标题发出消息。 【参考方案1】:

错误似乎指出这两行代码导致了问题。

#3 _OrderHistoryState._onAfterBuild package:shinier_store/screens/order_history.dart:60

#4 _OrderHistoryState.build. package:shinier_store/screens/order_history.dart:67

您提供的复制不完整,我只能猜测_onAfterBuild() 上添加了所需的标志listen: false。但是,错误日志指出var model = Provider.of&lt;OrderModel&gt;(context); 内的Widget build() 也需要有标志。 docs 中解释了此标志要求的原因。

如果您仍有问题,working minimal repro 将有助于我们了解为什么会发生这种行为。

【讨论】:

【参考方案2】:

我也遇到了类似的错误,发现在较新版本的 Provider Package 中(我使用的是provider: ^6.0.1),无论您在哪里更新提供者数据,都必须通过 listen: false

例如点击按钮或任何小部件 onChanged 事件。以下是 TextField onChanged 回调的示例。

TextField(
  onChanged: (newText) 
    Provider.of<Data>(context, listen: false).changeString(newText);
  ,
);

【讨论】:

【参考方案3】:

我还发现使用 BlocProvider,即使没有 listen 参数似乎也能解决这个问题。但在这种情况下,您将使用 BLOC 进行状态管理。

在我使用的所有情况下 var yyystate = BlocProvider.of(context);我总是添加listen参数。

【讨论】:

以上是关于试图从小部件树外部侦听提供者公开的值的主要内容,如果未能解决你的问题,请参考以下文章

试图从小部件访问状态,可能使用了错误的设计

提供者使用 Streams 公开不正确的值

如何在小部件测试中测试特定的 ProviderNotFoundException

Flutter:在从小部件树中删除时或在其生命周期结束时为小部件设置动画?

Visibility vs Offstage vs Opacity,哪个最适合从小部件树中隐藏孩子?

如何将上下文传递给提供者的第二个小部件树