Flutter Provider跨视图访问及Provider间调用相关问题咨询
Flutter Provider 常见问题解答
问题1:不同页面通过Provider.of获取的同类型实例是否为同一个
结论:你在两个页面拿到的providerOneFoo和providerTwoFoo是完全相同的实例,Provider.of方法不会创建新实例,只会查找组件树中离当前context最近的、对应类型的已注册Provider实例并返回引用。
具体机制说明:
- 你在应用顶层通过
MultiProvider注册的ChangeNotifierProvider,其create回调仅会在第一次有子组件请求对应类型实例时执行1次,生成的实例会被存在当前Provider的组件节点上,供所有子树内的组件访问。 - 只要两个页面的context都处于顶层
MultiProvider的子树范围内(你把Provider挂在runApp的根节点下,所有页面默认都在这个范围里),无论写在多少个不同的dart文件里,调用Provider.of<FooProvider>(context)拿到的都是同一个实例。 - 只有两种情况会拿到不同实例:一是组件树中嵌套了多个同类型的Provider,当前context处于内层Provider的作用域时会优先拿到内层的实例;二是调用时传了
listen: false也不会改变实例本身,只是取消了监听刷新的绑定。
问题2:跨Provider传参/互相持有属性是否合理
首先你写的调用方式功能上可以运行,但存在明显的设计缺陷,甚至会直接引发bug:
- 首先是逻辑分层问题:你在UI层同时获取两个Provider,再把其中一个作为参数传入另一个的方法,等于把两个状态类的交互逻辑耦合到了UI代码里,后续排查状态联动问题时需要翻遍各个UI页面的代码,维护成本极高。
- 其次是状态更新bug:你在
FooProvider的someFunction里直接修改了传入的barProvider的属性,但只调用了FooProvider自己的notifyListeners(),所有依赖BarProvider的组件根本收不到状态变更的通知,会出现数据改了但UI不刷新的问题。 - 至于把外部Provider设置为另一个Provider的属性,强烈不建议这么做:Provider实例的生命周期是和它在组件树中的位置绑定的,如果一个Provider被组件树销毁(比如内层路由的Provider随路由弹出被回收),被其他Provider持有的引用会造成内存泄漏,甚至会出现操作已销毁状态对象的异常。
多Provider联动的正确实现方式是使用官方提供的ProxyProvider,它会自动监听依赖的其他Provider的更新,自动完成状态同步,不需要手动传参或者持有引用,也不会出现生命周期不匹配的问题。
内容的提问来源于stack exchange,提问作者Mauricio
相关产品推荐
相关产品推荐

