将 Provider 3 转换为 4 后 Flutter 应用程序崩溃

Posted

技术标签:

【中文标题】将 Provider 3 转换为 4 后 Flutter 应用程序崩溃【英文标题】:Flutter app crash after converting Provider 3 to 4 【发布时间】:2020-04-22 17:34:00 【问题描述】:

我今天尝试升级我的 Flutter 应用以使用 Provider 4.0.1,但以下代码在将值分配给 null 时崩溃。

这是我尝试转换的代码。我只将SingleChildCloneableWidget 更改为SingleChildStatelessWidget,编译OK。

import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';

List<SingleChildStatelessWidget> providers = [
  ...independentServices,
  ...dependentServices,
  ...uiConsumableProviders
];

List<SingleChildStatelessWidget> independentServices = [
  Provider.value(value: Api()),
  Provider.value(value: Tbl()),
  Provider.value(value: Bill()),
  Provider.value(value: Sale()),
  Provider.value(value: Category()),
  Provider.value(value: Menu()),
];

List<SingleChildStatelessWidget> dependentServices = [
  ProxyProvider<Api, AuthenticationService>(
    update: (context, api, authenticationService) => AuthenticationService(api: api),
  ),
];

List<SingleChildStatelessWidget> uiConsumableProviders = [
  StreamProvider<User>(
    create: (context) => Provider.of<AuthenticationService>(context, listen: false).user,
  ),
    lazy: false
];

我是这样实现的:

StreamController<User> _userController = StreamController<User>();
Stream<User> get user => _userController.stream;

崩溃发生在这一行:

Future<void> _setFixedLanguageStrings(BuildContext context) async 

 User _user = Provider.of<User>(context);
 
 _user.homeString = await translate(context, 'Home');

在 null 上调用了 getter 'language'。收件人:空

这在 Provider 3.0.3 上运行良好,但显然我需要做更多。

我的原始代码来自this tutorial。

编辑:我通过在流提供程序创建方法中添加lazy: false 解决了这个问题,但随后在此代码中出现了另一个错误。

Future<String> translate(BuildContext context, _term) async 

  final String _languageCode = Provider.of<User>(context).language;

产生了这个错误:

发生了异常。 _AssertionError ('package:provider/src/provider.dart': 断言失败: line 213 pos 7: 'context.owner.debugBuilding || listen == 假 || _debugIsInInheritedProviderUpdate': 试图听一个 从小部件树外部与提供者公开的值。

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

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

它不受支持,因为可能会毫无意义地重建小部件 关联到事件处理程序,当小部件树不关心时 关于价值。 )

我在上面的行中添加了listen: false,这似乎已经解决了这个问题,但是我尝试使用的下一个提供程序产生了这个错误:

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

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

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

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

我现在应该去每个我调用提供者的实例并添加listen: false吗?我需要有人来解释发生了什么变化以及为什么我在 Flutter 还很新,而且 Provider 的文档很少。有很多次我在代码中调用 Provider 并且最后一个错误没有返回代码位置。

现在是否总是需要 listen: false 而以前不需要它,或者我错过了其他东西?我开始在每次调用中添加 listen: false 以实例化 Provider 变量,它似乎正在工作,但这是正确的方法吗?我应该在每次调用Provider.of 时添加listen: false 并收工吗?

edit:每当从小部件树的可见部分之外调用提供程序时都会出现错误。这种区别很重要。

【问题讨论】:

更新时遇到同样的问题。问题是,如果我可能会或可能不会更改小部件树怎么办?我正在回滚。 【参考方案1】:

我有同样的“问题”,如果我在我调用 Provider 的所有地方添加 listen: false,问题就消失了,但我不知道这是否是正确的解决方案...?

【讨论】:

我想知道为什么listen: false 不是SingleChildStatelessWidget 的默认值? listen: false 没有错误,但 UI 没有更新,所以需要其他东西才能使其正常工作【参考方案2】:

listen : false 在数据不会更新 UI 中的任何内容时调用,应该使用,例如在单击按钮时删除小部件中的所有卡片。

欲了解更多信息,请阅读此go_to_link

【讨论】:

【参考方案3】:

listen:true 作为默认值是合乎逻辑的。

它没有在不符合逻辑的事件处理程序中指定。listen: false

此外,4.1.0 会以某种方式为 Provider.of 提供更短的替代方案:

context.read<T>() // Provider.of<T>(context, listen: false)
context.watch<T>() // Provider.of<T>(context)

【讨论】:

感谢您的帮助 你能指出这些的文档链接吗?【参考方案4】:

在我的情况下,我收到以下错误:-

I/flutter ( 7206): Tried to listen to a value exposed with provider, from outside of the widget tree.
I/flutter ( 7206): 
I/flutter ( 7206): This is likely caused by an event handler (like a button's onPressed) that called
I/flutter ( 7206): Provider.of without passing `listen: false`.
I/flutter ( 7206): 
I/flutter ( 7206): To fix, write:
I/flutter ( 7206): Provider.of<AstroDetailsProvider>(context, listen: false);
I/flutter ( 7206): 
I/flutter ( 7206): It is unsupported because may pointlessly rebuild the widget associated to the
I/flutter ( 7206): event handler, when the widget tree doesn't care about the value.

如您所见,错误消息本身中存在解决方案。

因此,我们不应该在事件处理程序中使用具有默认 (listen:true) 的提供程序。

或者,

context.read<T>() is same as Provider.of<T>(context, listen: false) And
context.watch<T>() is same as Provider.of<T>(context)```

Ref :- https://github.com/rrousselGit/provider/issues/313

【讨论】:

【参考方案5】:

我们必须使用onPressedOnTaponLongPressed等事件处理程序

Provider.of<T>(context,listen:false)

原因是他们不会监听任何更新更改,而是负责进行更改。

而文本等小部件负责显示...因此需要在每次更改时更新...因此使用

Provider.of<T>(context,listen:true)  //by default is listen:true

【讨论】:

【参考方案6】:

如果您在构建之外使用提供程序而没有 listen: false

那么,当然,您不能收听更改,因为它没有为更改再次构建小部件。这不是默认值,因为提供程序不应该在构建之外使用,因为它用作状态管理工具并注入依赖项。 但是,如果您在构建之外使用,则必须使用 listen: false

【讨论】:

【参考方案7】:

如果我们使用,

 Provider.of<T>(context, listen: true).method(); 

内部构建方法和如果我们有

 notifyListeners();

在那个方法()中,然后它会导致无限递归,就像每个

 notifyListeners(); 

invoke ,所有的provider调用,listen:true都会执行,即重新执行build方法,这意味着

 method(), 

被一遍又一遍地调用并导致错误

【讨论】:

如果我们想要更新 UI 怎么办?如果我添加了监听会出错:true,但也需要更新 ui。 :(

以上是关于将 Provider 3 转换为 4 后 Flutter 应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

这些开源项目 yyds

如何在不进行任何舍入的情况下将浮点数转换为小数点后 4 位?

使用反应时如何将表格行转换为列

如何将 utf-8 字节偏移量转换为 utf-8 字符偏移量

怎样将word,excel文件转换为PDF文件

将String转换为二维数组