Flutter嵌套StreamProvider加载卡顿及单元测试失败问题排查
问题分析与解决
问题现象
- 嵌套使用
recordStreamProvider(父)和userStreamProvider(子)时,recordStreamProvider卡在加载状态,无法响应userStreamProvider的新数据(userStreamProvider本身工作正常) - 单元测试预期
recordStream返回空列表,实际得到null
原代码展示
repositories.dart
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final userStreamProvider = StreamProvider((_) { return FirebaseAuth.instance.authStateChanges().map((user) { return user; }); }); final recordStreamProvider = StreamProvider<List<Record>>((ref) { User? user = null; // User? user = ref.watch(userStreamProvider).value; if (user == null) { return Stream.value([]); } else { CollectionReference<Map<String, dynamic>> cref = FirebaseFirestore.instance .collection('/$usersFieldKey/${user.uid}/$recordsFieldKey'); return cref.snapshots().map((snapshot) => snapshot.docs.map((doc) => Record.fromJson(doc.data())).toList()); } });
repositories_test.dart
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:test/test.dart'; void main() async { group('recordStreamProvider', () { test('returns empty list if user is null', () async { // Override the userStreamProvider with a null AsyncValue. final container = ProviderContainer( overrides: [ userStreamProvider.overrideWith((ref) => Stream.value(null)), ], ); container.refresh(userStreamProvider); await Future.delayed(const Duration(seconds: 1)); final recordStream = container.read(recordStreamProvider); // Retrieve the stream provider and evaluate its state. expect(recordStream.value, []); }); }); }
测试失败结果
00:03 +0 -1: recordStreamProvider returns empty list if user is null [E] Expected: [] Actual: <null> Which: is not Iterable package:test_api expect test\record_repogitory_test.dart 42:7 main.<fn>.<fn> 'recordStreamProvider returns empty list if user is null' 00:03 +0 -1: Some tests failed.
原因分析
- recordStreamProvider无法响应user变化:你注释掉了
ref.watch(userStreamProvider).value,硬编码user = null,导致provider不会监听userStreamProvider的状态变化,自然无法更新数据;同时错误的初始化逻辑使框架认为provider处于加载状态。 - 单元测试返回null:测试中直接读取
recordStream.value时,provider可能还未完成状态初始化,处于AsyncLoading状态,此时value字段为null。
解决方案
1. 修复recordStreamProvider的依赖监听
修改recordStreamProvider,正确监听userStreamProvider并处理其异步状态:
final recordStreamProvider = StreamProvider<List<Record>>((ref) { // 监听userStreamProvider的状态变化 final userAsync = ref.watch(userStreamProvider); // 处理加载状态:返回空列表流,避免卡住 if (userAsync.isLoading) { return Stream.value([]); } // 处理错误状态:返回空列表,可根据需求调整为抛出错误 if (userAsync.hasError) { return Stream.value([]); } final User? user = userAsync.value; if (user == null) { return Stream.value([]); } else { CollectionReference<Map<String, dynamic>> cref = FirebaseFirestore.instance .collection('/$usersFieldKey/${user.uid}/$recordsFieldKey'); return cref.snapshots().map((snapshot) => snapshot.docs.map((doc) => Record.fromJson(doc.data())).toList()); } });
2. 修正单元测试逻辑
测试中需等待provider状态稳定,确保读取到完整的异步状态:
void main() async { group('recordStreamProvider', () { test('returns empty list if user is null', () async { final container = ProviderContainer( overrides: [ userStreamProvider.overrideWith((ref) => Stream.value(null)), ], ); addTearDown(container.dispose); // 测试结束后释放资源 // 等待provider状态完成初始化 final recordListener = container.listen(recordStreamProvider); await recordListener.future; final recordState = container.read(recordStreamProvider); expect(recordState.value, []); }); }); }
额外说明
- 使用
ref.watch监听userStreamProvider后,recordStreamProvider会在用户状态更新时自动重新执行,解决卡在加载状态的问题。 - 测试中通过
listen并等待future,确保provider已完成状态初始化,避免读取到null值。
内容的提问来源于stack exchange,提问作者Kicchan
相关产品推荐
相关产品推荐

