Flutter中继承ChangeNotifier的类内使用其他Provider是否为最佳实践?
场景
我有一个FirebaseManager类,负责处理Firebase相关调用,该类继承自ChangeNotifier,其他类可通过Provider访问其实例。此外我还有一个同样继承自ChangeNotifier的UserManager类,内部存储了Firestore中所有用户构成的List<User>列表。
问题
我希望在FirebaseManager的一个async异步方法中,通过Provider访问UserManager实例来填充上述用户列表。请问在一个Provider对应的类内部调用另一个Provider是否属于良好开发实践?如果不是,这类场景下更优的设计方案是什么?
补充说明
目前我已经可以将获取UserManager Provider所需的context传入FirebaseManager中。
在Provider内部通过context获取另一个Provider实例并调用,不属于良好开发实践,核心隐患有三点:
- 异步场景下容易出现context失效:你的调用逻辑在async方法中,如果方法执行时传入的context对应的widget已经被销毁(比如用户退出了当前页面),直接通过context读Provider会直接抛出异常,很容易引发线上难以复现的崩溃。
- 耦合度过高:FirebaseManager直接和UserManager、Provider框架绑定,后续调整逻辑或者做单元测试时,需要同时初始化两个Provider,还要构造可用的context,测试成本极高。
- 职责边界混乱:FirebaseManager的定位是处理Firebase相关调用,不应该承担更新UserManager状态的逻辑,违背单一职责原则。
更优的实现方案
方案1:构造函数依赖注入(最常用,适配绝大多数场景)
初始化Provider时直接将依赖的实例传入对应类的构造函数,不需要在运行时通过context获取,从根源上解决context失效和耦合问题。
示例代码:
// 通常在main.dart的顶层初始化Provider ChangeNotifierProvider(create: (context) => UserManager()), // 创建FirebaseManager时直接传入已经初始化的UserManager实例 ChangeNotifierProvider(create: (context) => FirebaseManager( userManager: context.read<UserManager>() )),
之后你可以直接在FirebaseManager内部持有UserManager的引用,异步方法里直接调用即可,不需要传context,测试时也可以直接传入Mock的UserManager实例做单元测试,成本很低。
方案2:事件驱动解耦(适合复杂业务场景,需要完全隔离两个Provider的依赖)
如果不想让两个Provider有直接依赖,可以用事件通知的方式解耦:FirebaseManager拉取到用户数据后仅对外抛出数据更新的事件,由上层逻辑监听事件,再调用UserManager的方法更新用户列表。
可以用Provider自带的监听逻辑实现,示例代码:
// 顶层Widget中添加监听 Consumer<FirebaseManager>( builder: (context, firebaseManager, child) { // 监听到FirebaseManager拉取到用户数据后,调用UserManager更新 if (firebaseManager.fetchedUserList != null) { WidgetsBinding.instance.addPostFrameCallback((_) { context.read<UserManager>().updateUserList(firebaseManager.fetchedUserList); }); } return child!; }, child: const YourAppBody(), )
这种方案下两个Provider完全不知道对方的存在,后续调整逻辑只需要修改上层的监听逻辑即可,灵活度最高,适合大型项目使用。
非常不推荐你现在使用的传context到Provider内部的方案,异步场景下context失效的概率很高,排查问题成本极高。
内容的提问来源于stack exchange,提问作者ark-tik

