You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需传参读取Riverpod中currentUserProvider的缓存状态

问题:Flutter Riverpod中如何实现无需传参的全局currentUser读取?

我刚接触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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:43:14