如何将Firestore流函数返回的Stream数据关联到Flutter Bloc并整合仓库?
将Firestore Stream关联到Flutter Bloc并整合FirebaseChatApi仓库
下面一步步实现Firestore流与Flutter Bloc的关联,同时整合你提供的FirebaseChatApi仓库:
1. 定义Bloc的事件和状态
首先明确Bloc需要处理的事件和对应的状态:
事件类
abstract class ChatContactsEvent {} // 触发获取聊天联系人列表的事件 class FetchChatContacts extends ChatContactsEvent {}
状态类
abstract class ChatContactsState {} // 加载中状态 class ChatContactsLoading extends ChatContactsState {} // 加载成功状态,携带联系人数据 class ChatContactsLoaded extends ChatContactsState { final List<ChatContactModel> contacts; ChatContactsLoaded(this.contacts); } // 加载失败状态,携带错误信息 class ChatContactsLoadFailed extends ChatContactsState { final String error; ChatContactsLoadFailed(this.error); }
2. 实现Bloc并关联Firestore Stream
在Bloc中注入FirebaseChatApi,通过监听它返回的Stream来更新状态:
class ChatContactsBloc extends Bloc<ChatContactsEvent, ChatContactsState> { final FirebaseChatApi _firebaseChatApi; StreamSubscription? _contactsSubscription; ChatContactsBloc(this._firebaseChatApi) : super(ChatContactsLoading()) { // 处理FetchChatContacts事件 on<FetchChatContacts>((event, emit) { emit(ChatContactsLoading()); // 先取消之前的订阅,避免重复监听 _contactsSubscription?.cancel(); // 监听Firestore返回的Stream _contactsSubscription = _firebaseChatApi.getChatContacts().listen( (contacts) { emit(ChatContactsLoaded(contacts)); }, onError: (error) { emit(ChatContactsLoadFailed(error.toString())); }, ); }); // 可选:Bloc初始化时自动触发获取联系人事件 add(FetchChatContacts()); } @override Future<void> close() { // 关闭Bloc时取消Stream订阅,防止内存泄漏 _contactsSubscription?.cancel(); return super.close(); } }
3. 整合FirebaseChatApi到Bloc
你提供的FirebaseChatApi可以直接作为依赖传入Bloc,这里做一点小优化,把Firebase实例提到类成员,避免每次调用方法都重新创建:
class FirebaseChatApi { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; Stream<List<ChatContactModel>> getChatContacts() { return _firestore .collection('users') .doc(_auth.currentUser!.uid) .collection('chats') .snapshots() .asyncMap((event) async { List<ChatContactModel> contacts = []; for (var document in event.docs) { var chatContact = ChatContactModel.fromJson(document.data()); contacts.add(ChatContactModel( name: chatContact.name, profilePic: chatContact.profilePic, contactId: chatContact.contactId, timeSent: chatContact.timeSent, lastMessage: chatContact.lastMessage, )); } return contacts; }); } }
4. 在UI中使用Bloc
通过BlocBuilder监听状态变化,渲染对应的UI:
BlocBuilder<ChatContactsBloc, ChatContactsState>( builder: (context, state) { if (state is ChatContactsLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is ChatContactsLoaded) { return ListView.builder( itemCount: state.contacts.length, itemBuilder: (context, index) { final contact = state.contacts[index]; return ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(contact.profilePic), ), title: Text(contact.name), subtitle: Text(contact.lastMessage), trailing: Text(DateFormat.jm().format(contact.timeSent)), ); }, ); } else if (state is ChatContactsLoadFailed) { return Center(child: Text('加载失败: ${state.error}')); } return const SizedBox(); }, )
需要注意的地方
- 务必在Bloc的
close方法中取消Stream订阅,避免内存泄漏 - 建议用依赖注入框架(比如get_it)来管理FirebaseChatApi实例,方便后续替换实现或做单元测试
- 要处理
_auth.currentUser为null的情况,比如添加空值判断,避免空指针崩溃
内容的提问来源于stack exchange,提问作者Janam Maharjan
相关产品推荐
相关产品推荐

