Flutter SqlBrite问题:StreamBuilder监听的列表无法自动更新如何解决
问题成因
- SQL语法错误:数据库层查询语句的两个匹配条件之间缺少
AND关键字,会导致查询异常,无法正常返回匹配的消息数据。
- SQL语法错误:数据库层查询语句的两个匹配条件之间缺少
- 流类型转换缺失:SqlBrite的
createQuery方法返回的是Stream<Query>类型,你没有将其转换为实际的消息列表类型,流无法正常输出有效数据。
- 流类型转换缺失:SqlBrite的
- Stream实例频繁重建:UI层在
build方法中直接调用getMessageYieldBase生成新的Stream实例,每次页面重建都会销毁旧的流监听、创建新的监听,只有重建时才会拉取一次最新数据,平时不会收到更新推送。
- Stream实例频繁重建:UI层在
- BriteDatabase实例不统一:如果插入操作和查询操作使用的不是同一个BriteDatabase实例,SqlBrite无法监听到跨实例的表写入事件,自然不会触发流更新。
- 冗余代码干扰:Provider层
addMessageTodb中调用getMessageYieldBase的逻辑完全无效,既没有监听返回的流,也不会触发现有流的更新。
- 冗余代码干扰:Provider层
可行解决方案
步骤1:修复SQL语法与流转换
修正数据库层的查询逻辑,补全SQL条件连接符,添加Query到消息列表的转换逻辑。
/// 插入新消息到数据库 Future<int> insertNewMessage(String id, int result, BriteDatabase briteDb, Map<String, dynamic> row) async { messageList.add(id); final isExists = await ifexists(id, messageId, briteDb); if (isExists) { print('message already In'); return 0; } return await briteDb.insert(messageTable, row); } /// 监听指定用户的消息流 Stream<List<Map<String, dynamic>>> getMessageMapListbyId({ required String sendId, required String receiveId, required BriteDatabase briteDb, }) { return briteDb.createQuery( messageTable, distinct: false, // 补全两个条件之间的AND关键字 where: '$senderId = ? AND $receiverId = ?', whereArgs: [sendId, receiveId], // 补充Query到结果列表的转换,SqlBrite写入时会自动触发这个流更新 ).mapToList((query) => query.rows); }
步骤2:全局统一BriteDatabase实例
确保插入、查询操作使用同一个BriteDatabase实例,全局单例初始化一次即可复用。
// Provider层全局保存单例实例 late final BriteDatabase globalBriteDb; // APP启动时执行一次初始化 Future initGlobalDatabase() async { final rawDb = await openDatabase(/* 你的数据库初始化参数 */); globalBriteDb = BriteDatabase(rawDb); } /// 添加消息到数据库 Future addMessageTodb(message) async { final messageRow = modelFuncs.messageMaping(message, msgFuncs); // 使用全局单例执行插入 await ldbH.msg_insertMessage(message.id, 0, globalBriteDb, messageRow); // 无需额外调用流方法,SqlBrite自动触发同表的所有查询流更新 } /// 获取消息流 Stream<List<Map<String, dynamic>>> getMessageYieldBase(senderId, receiverId) { return ldbH.getMessageMapListbyId( sendId: senderId, receiveId: receiverId, briteDb: globalBriteDb, ); }
步骤3:UI层固定Stream实例
在State初始化时生成一次流实例,避免build时频繁重建流。
class ChatPageState extends State<ChatPage> { late Stream<List<Map<String, dynamic>>> _messageStream; @override void initState() { super.initState(); // 仅初始化时创建一次流,后续复用该实例 _messageStream = messageStream.getMessageYieldBase( widget._currentUserId, widget.receiver.uid, ); } @override Widget build(BuildContext context) { return StreamBuilder<List<Map<String, dynamic>>>( // 使用预先创建的固定流实例 stream: _messageStream, builder: (context, AsyncSnapshot<List<Map<String, dynamic>>> snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final newList = snapshot.data!.reversed.toList(); return ListView.builder( reverse: true, padding: const EdgeInsets.all(10), controller: widget._listScrollController, itemCount: newList.length, itemBuilder: (BuildContext context, int index) { return DisplayMessage( currentUserId: widget._currentUserId, receiver: widget.receiver, message: newList[index], ); }, ); }, ); } }
内容的提问来源于stack exchange,提问作者Philip
相关产品推荐
相关产品推荐

