无需传参读取Riverpod中currentUserProvider的缓存状态
我刚接触Flutter,之前用Rails,里面的current_user配置很简单。现在用Riverpod实现了一个函数式的currentUserProvider:
@riverpod User currentUser(CurrentUserRef ref, User user) { User currentUser = user; return currentUser; }
这个Provider会在登录成功时,由负责验证状态的Authentication Provider初始化:
enum AuthenticationState { initial, loading, success, error, } @riverpod class Authentication extends _$Authentication { @override AuthenticationState build() { return AuthenticationState.initial; } Future<void> login() async { // API请求逻辑 if (response.statusCode == 200) { User currentUser = User.fromJson((json.decode(response.body))['data']['attributes']); currentUser = currentUser.copyWith(jwt: token); ref.read(currentUserProvider(user: currentUser)); state = AuthenticationState.success; } else { await SecureStorage().deleteSecureData('jwt'); state = AuthenticationState.error; } } }
现在遇到的问题是:后续在其他地方只想读取currentUserProvider的缓存值,但它要求必须传入User对象作为参数。我不想把参数设为可选类型,因为User对象绝不能为null,而且未登录用户会被重定向到登录页。请问有没有办法直接读取它的当前状态?
你当前的currentUserProvider是带参数的函数式Provider,这种Provider本质是工厂模式——每次传入不同的user参数,都会生成一个独立的Provider实例,并没有全局缓存的效果,所以自然没法直接读取之前设置的值。要实现全局可访问的currentUser,需要改成状态型Provider,比如StateProvider或自定义NotifierProvider。
1. 重构currentUserProvider为状态型Provider
这里用NotifierProvider做示例(比StateProvider更适合后续扩展逻辑):
@riverpod class CurrentUser extends _$CurrentUser { @override User build() { // 未登录时抛出异常,确保只有登录后才会访问该状态 throw StateError("Current user not initialized"); } // 设置当前用户 void setUser(User user) { state = user; } // 登出时清空(可选) void clearUser() { throw StateError("Current user logged out"); } }
如果用更简单的StateProvider:
@riverpod StateProvider<User> currentUser(CurrentUserRef ref) { return StateProvider((ref) => throw StateError("Current user not initialized")); }
2. 修改Authentication的登录逻辑
在login方法中,不再读取带参数的Provider,而是更新状态型Provider的状态:
对应NotifierProvider的写法:
Future<void> login() async { // API请求逻辑 if (response.statusCode == 200) { User currentUser = User.fromJson((json.decode(response.body))['data']['attributes']); currentUser = currentUser.copyWith(jwt: token); // 更新CurrentUser状态 ref.read(currentUserProvider.notifier).setUser(currentUser); state = AuthenticationState.success; } else { await SecureStorage().deleteSecureData('jwt'); state = AuthenticationState.error; } }
对应StateProvider的写法:
Future<void> login() async { // API请求逻辑 if (response.statusCode == 200) { User currentUser = User.fromJson((json.decode(response.body))['data']['attributes']); currentUser = currentUser.copyWith(jwt: token); // 更新StateProvider状态 ref.read(currentUserProvider.notifier).state = currentUser; state = AuthenticationState.success; } else { await SecureStorage().deleteSecureData('jwt'); state = AuthenticationState.error; } }
3. 在其他地方读取currentUser
现在可以直接读取,无需传参:
// 在Widget或其他Provider中 final currentUser = ref.watch(currentUserProvider); // 使用currentUser的属性,比如currentUser.name
4. 处理未登录场景
因为你提到未登录用户会被重定向到登录页,建议在读取currentUser前先监听authenticationProvider的状态,确保用户已登录:
final authState = ref.watch(authenticationProvider); if (authState == AuthenticationState.success) { final currentUser = ref.watch(currentUserProvider); // 渲染用户相关内容 } else { // 跳转到登录页,比如使用Navigator if (mounted) { Navigator.pushReplacementNamed(context, '/login'); } }
这样既保证了currentUser永远不为null,也能避免读取未初始化状态的异常。
内容的提问来源于stack exchange,提问作者bkunzi01

