Flutter StreamBuilder跳转后重建致帖子列表重置问题求助
Flutter StreamBuilder跳转后重建导致帖子列表重置问题
问题描述
使用StreamBuilder展示帖子列表,点击按钮通过Navigator.of(context).push()跳转至评论页时,首页的StreamBuilder会触发重建,返回首页后帖子列表被重置回到第一条。已通过打印语句确认跳转期间StreamBuilder确实发生重建,暂未找到有效解决办法。
问题代码
StreamBuilder( // StreamBuilder de refresh stream: Provider.of<MyProvider>(context, listen: false).refreshStream, builder: (context, snapshot) { print("rebuild"); if (snapshot.hasData) { _uniqueKey = UniqueKey().toString(); } return StreamBuilder( key: Key(_uniqueKey), stream: FirebaseFirestore.instance .collection("Users") .doc(FirebaseAuth.instance.currentUser!.uid) .snapshots(), builder: (context, snapshot) { if (snapshot.data == null) { // Chargement ... return const CupertinoActivityIndicator( radius: 25, ); } if (snapshot.data!["followedPostsOnly"]) { // Afficher les posts des utilisateurs suivis uniquement if (snapshot.data!["followedAccounts"] == null || snapshot.data!["followedAccounts"] == [] || snapshot.data!["followedAccounts"].isEmpty) { // Aucun compte suivis = Aucun post à afficher return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "Aucun compte suivi", style: GoogleFonts.kanit( fontSize: 30, fontWeight: FontWeight.bold, ), ), const Gap(30), Padding( padding: EdgeInsets.symmetric( horizontal: MediaQuery.of(context).size.width * 0.05), child: const Text( "Pour afficher les posts de tous les utilisateurs de l'application, rendez-vous dans les réglages.", textAlign: TextAlign.center, ), ), ], ), ); } // Afficher les posts return StreamBuilder( stream: FirebaseFirestore.instance .collection("Posts") .where("author", whereIn: snapshot.data!["followedAccounts"]) .orderBy("timestamp", descending: true) .snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // Chargement ... return const CupertinoActivityIndicator( radius: 25, ); } if (snapshot.data == null || snapshot.data!.docs.isEmpty) { // Aucun post return Center( child: Text( "Aucun post", style: GoogleFonts.kanit( fontSize: 30, fontWeight: FontWeight.bold, ), ), ); } return Stack( children: [ // Message plus de posts (en dessous des tous les posts ce qui le rend visible seulement quand il n'y en a plus) Center( child: Text( Provider.of<MyProvider>(context, listen: false) .foregroundPost == null ? "Plus de posts ..." : "", style: GoogleFonts.kanit( fontSize: 25, fontWeight: FontWeight.bold, ), ), ), // Posts Stack( children: List.generate( snapshot.data!.docs.length, (index) { return MySimplePost( postID: snapshot.data!.docs[index].reference.id, stackIndex: snapshot.data!.docs.length - 1 - index, ); }, ), ), ], ); }, ); } // Afficher les posts de tout le monde return StreamBuilder( stream: FirebaseFirestore.instance .collection("Posts") .orderBy("timestamp", descending: false) .snapshots(), builder: (context, snapshot) { if (snapshot.data == null || snapshot.data!.docs.isEmpty || snapshot.data!.docs.isEmpty) { // Aucun post return Center( child: Text( "Aucun post", style: GoogleFonts.kanit( fontSize: 30, fontWeight: FontWeight.bold, ), ), ); } WidgetsBinding.instance.addPostFrameCallback( (timeStamp) { Provider.of<MyProvider>(context, listen: false).setLastPost( snapshot.data!.docs.first.reference.id, ); }, ); return Stack( children: [ // Message plus de posts (en dessous des tous les posts ce qui le rend visible seulement quand il n'y en a plus) Center( child: Consumer<MyProvider>( builder: (context, provider, _) { return Text( provider.foregroundPost == null ? "Plus de posts ..." : "", style: GoogleFonts.kanit( fontSize: 25, fontWeight: FontWeight.bold, ), ); }, ), ), // Posts Stack( children: List.generate( snapshot.data!.docs.length, (index) { return MySimplePost( postID: snapshot.data!.docs[index].reference.id, stackIndex: snapshot.data!.docs.length - 1 - index, ); }, ), ), ], ); }, ); }, ); }, )
问题根源
- 动态Key强制重建:最外层StreamBuilder在
refreshStream有数据时,会更新_uniqueKey并传递给子StreamBuilder,导致子树被强制完全重建,丢失之前的帖子显示状态。 - Stream重复订阅:在build方法中直接创建Firestore Stream,每次重建都会重新订阅,触发数据重置。
- 状态未持久化:当前显示的帖子状态仅依赖Widget自身,未存入Provider等持久化容器,重建后无法恢复。
解决方案
1. 移除动态生成的Key
删除子StreamBuilder的key: Key(_uniqueKey)配置,避免因Key变化触发的强制重建。如果确实需要在特定场景下重建子树,改用固定的GlobalKey或仅在必要时更新Key。
2. 在Provider中持久化当前帖子状态
在MyProvider中添加状态变量保存当前显示的帖子ID,确保重建后能读取状态恢复位置:
class MyProvider extends ChangeNotifier { String? _currentPostId; String? get currentPostId => _currentPostId; void setCurrentPostId(String id) { _currentPostId = id; notifyListeners(); } }
在MySimplePost的交互逻辑(如滑动切换帖子)中调用setCurrentPostId更新状态,重建时根据currentPostId恢复显示位置。
3. 在Provider中缓存Firestore Stream
将Stream的创建逻辑移至Provider,避免每次build重新订阅:
class MyProvider extends ChangeNotifier { Stream<QuerySnapshot>? _postsStream; Stream<QuerySnapshot>? get postsStream => _postsStream; List<String>? _followedAccounts; void initPostsStream(bool followedOnly, List<String>? followedAccounts) { _followedAccounts = followedAccounts; if (followedOnly && _followedAccounts != null && _followedAccounts!.isNotEmpty) { _postsStream = FirebaseFirestore.instance .collection("Posts") .where("author", whereIn: _followedAccounts!) .orderBy("timestamp", descending: true) .snapshots(); } else { _postsStream = FirebaseFirestore.instance .collection("Posts") .orderBy("timestamp", descending: false) .snapshots(); } } }
在Widget中直接使用Provider缓存的Stream:
return StreamBuilder( stream: Provider.of<MyProvider>(context).postsStream, builder: (context, snapshot) { // 原有构建逻辑,同时根据currentPostId恢复显示状态 }, );
4. 优化Navigator跳转的上下文使用
确保跳转时使用的上下文不会触发父Widget重建,可通过GlobalKey获取Navigator:
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); // 在MaterialApp中配置 MaterialApp( navigatorKey: _navigatorKey, ); // 跳转时使用 _navigatorKey.currentState?.push(MaterialPageRoute(builder: (context) => CommentPage()));
内容的提问来源于stack exchange,提问作者Furiy
相关产品推荐
相关产品推荐

