Riverpod中authCheckerProvider监听StreamProvider未触发重建问题
我使用Riverpod编写了如下代码:
/// /// Data dependencies /// final authRepositoryProvider = Provider( name: 'authRepositoryProvider', (ref) { return AuthRepositoryImpl( authClient: ref.watch(firebaseAuthClientProvider), ); }, ); /// /// Application dependencies /// final authStateStreamProvider = StreamProvider<User?>( name: 'authStateStreamProvider', (ref) { return ref.watch(authRepositoryProvider).authStateChange; }, ); final authCheckerProvider = Provider( name: 'authCheckerProvider', (ref) { var authStateStreamData = ref.watch(authStateStreamProvider); authStateStreamData.when( data: (User? data) { debugPrint('data'); }, error: (Object error, StackTrace stackTrace) { debugPrint('error'); }, loading: () { debugPrint('loading'); }, ); }, );
当authStateStreamProvider接收到新数据时,authCheckerProvider并未触发重建,不符合预期。其中ref.read(authRepositoryProvider).authStateChange返回的是Firebase的userChanges()流。我尝试使用ref.watch(authStateStreamProvider.stream)并添加监听器可以收到新数据,但不清楚直接监听StreamProvider为何无效。
核心原因
你的authCheckerProvider是普通Provider,内部调用ref.watch(authStateStreamProvider)获取了AsyncValue<User?>,但没有将这个值作为Provider的返回值。Riverpod的Provider只有在返回值发生变化时才会触发重建,而当前Provider默认返回null,所以即使authStateStreamProvider的状态更新,authCheckerProvider也不会感知到变化并重建。
另外,when方法只是消费AsyncValue的状态,不会让Provider追踪值的变化——只有当Provider的返回值依赖被watch的对象时,Riverpod才会建立依赖关系并触发重建。
修正方案
方式一:让Provider返回AsyncValue并追踪变化
修改authCheckerProvider,让它返回authStateStreamProvider的AsyncValue,这样状态更新时Provider会自动重建,同时可以在内部处理日志逻辑:
final authCheckerProvider = Provider<AsyncValue<User?>>( name: 'authCheckerProvider', (ref) { final authState = ref.watch(authStateStreamProvider); // 处理状态日志 authState.when( data: (User? data) => debugPrint('data: $data'), error: (error, stack) => debugPrint('error: $error'), loading: () => debugPrint('loading'), ); // 返回被watch的值,让Riverpod追踪变化 return authState; }, );
方式二:使用ref.listen监听状态更新
如果不需要对外暴露状态,只是想内部监听authStateStreamProvider的变化,用ref.listen替代ref.watch,每次状态更新都会触发回调,无需依赖Provider返回值:
final authCheckerProvider = Provider( name: 'authCheckerProvider', (ref) { // 监听StreamProvider的状态变化 ref.listen<AsyncValue<User?>>(authStateStreamProvider, (previous, next) { next.when( data: (User? data) => debugPrint('data: $data'), error: (error, stack) => debugPrint('error: $error'), loading: () => debugPrint('loading'), ); }); // 返回占位值,避免Provider返回null return const Object(); }, );
为什么ref.watch(authStateStreamProvider.stream)有效?
authStateStreamProvider.stream返回原始的Stream<User?>,当你watch这个Stream时,Riverpod会监听流的新事件,每次有新事件都会触发Provider重建。但这种方式需要手动处理流订阅,而用ref.listen监听StreamProvider的AsyncValue更符合Riverpod的设计模式。
内容的提问来源于stack exchange,提问作者FRANCISCO BERNAD

