将 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】:我们必须使用onPressed、OnTap、onLongPressed等事件处理程序
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 应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章
如何在不进行任何舍入的情况下将浮点数转换为小数点后 4 位?