Flutter:如何在ListView.builder中按vote长度降序排序评论
问题描述
我已获取数组快照,通过length统计其长度并完成展示,但希望在ListView.builder中将数据按vote数量从多到少重新排序,目前尝试在内部StreamBuilder中过滤排序但未生效。现有代码中usersComment是Widget树上层StreamBuilder快照里的数组,后端结构如图所示(原问题提及结构图,此处未提供)。
原代码如下:
// 补充:这是上层StreamBuilder快照里的usersCommentId final usersComment = snapshot.data?['usersComment']; ListView.builder( physics: const BouncingScrollPhysics(), itemCount: usersComment.length, shrinkWrap: true, itemBuilder: (context, index) { return StreamBuilder( stream: FirebaseFirestore.instance .collection("usersComment") // 我尝试在这里过滤,但没用 .where(usersComment[index]) .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) { final userComments = snapshot.data!.docs ..sort((a, b) => ((b.data()['vote'] as List<dynamic>?)?.length ?? 0) .compareTo((a.data()['vote'] as List<dynamic>?)?.length ?? 0)); final comment = userComments[index]; final countVote = (comment.data()['vote'] as List<dynamic>?)?.length ?? 0; if (!snapshot.hasData) { return Container(); } return Text( countVote.toString(), style: const TextStyle(color: Colors.black, fontSize: 15), ); }); }),
问题分析
- 内部每个StreamBuilder实际只查询单条评论(
.where(usersComment[index])用法错误,本质是按ID查单条),所以userComments只有一条数据,排序毫无意义。 - 外层ListView完全按原
usersComment数组的顺序渲染,内部排序根本影响不到外层列表的展示顺序。 - 用外层的
index去取排序后的userComments数据,逻辑完全混乱,属于错误的索引复用。
解决方案
要实现按vote数量降序排序,必须先获取所有目标评论数据,统一排序后再渲染,不能在子StreamBuilder里单独处理。
方案1:批量获取+排序(适合数据量不大的场景)
直接一次性查询所有需要的评论,排序后再构建ListView:
// 上层StreamBuilder的builder回调内 final usersComment = snapshot.data?['usersComment'] as List<String>? ?? []; return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance .collection("usersComment") .where(FieldPath.documentId, whereIn: usersComment) // 根据ID批量查询所有评论 .snapshots(), builder: (context, commentSnapshot) { if (!commentSnapshot.hasData) { return const Center(child: CircularProgressIndicator()); } // 按vote数组长度降序排序 final sortedComments = commentSnapshot.data!.docs.toList() ..sort((a, b) { final bVoteCount = (b.data()['vote'] as List?)?.length ?? 0; final aVoteCount = (a.data()['vote'] as List?)?.length ?? 0; return bVoteCount.compareTo(aVoteCount); }); return ListView.builder( physics: const BouncingScrollPhysics(), itemCount: sortedComments.length, shrinkWrap: true, itemBuilder: (context, index) { final comment = sortedComments[index]; final countVote = (comment.data()['vote'] as List?)?.length ?? 0; return Text( countVote.toString(), style: const TextStyle(color: Colors.black, fontSize: 15), ); }, ); }, );
方案2:实时监听单条评论+统一排序(适合需要实时更新vote的场景)
如果每条评论的vote会实时变化,需要单独监听,但仍要统一排序:
// 上层StreamBuilder的builder回调内 final usersComment = snapshot.data?['usersComment'] as List<String>? ?? []; // 为每条评论创建单独的监听Stream final commentStreams = usersComment.map((commentId) { return FirebaseFirestore.instance .collection("usersComment") .doc(commentId) .snapshots() .map((doc) => doc.data()); }).toList(); // 合并所有Stream,任意一条评论更新时重新排序 return StreamBuilder<List<Map<String, dynamic>?>>( stream: CombineLatestStream.list(commentStreams), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } // 过滤空数据后按vote长度降序排序 final sortedComments = snapshot.data! .where((data) => data != null) .cast<Map<String, dynamic>>() .toList() ..sort((a, b) { final bVoteCount = (b['vote'] as List?)?.length ?? 0; final aVoteCount = (a['vote'] as List?)?.length ?? 0; return bVoteCount.compareTo(aVoteCount); }); return ListView.builder( physics: const BouncingScrollPhysics(), itemCount: sortedComments.length, shrinkWrap: true, itemBuilder: (context, index) { final comment = sortedComments[index]; final countVote = (comment['vote'] as List?)?.length ?? 0; return Text( countVote.toString(), style: const TextStyle(color: Colors.black, fontSize: 15), ); }, ); }, );
注意:方案2需要依赖rxdart包的CombineLatestStream,需在pubspec.yaml中添加依赖:rxdart: ^0.27.7(版本号可按需调整)。
内容的提问来源于stack exchange,提问作者It'sPhil
相关产品推荐
相关产品推荐

