You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
                      );
                    },
                  ),
                ),
              ],
            );
          },
        );
      },
    );
  },
)

问题根源

  1. 动态Key强制重建:最外层StreamBuilder在refreshStream有数据时,会更新_uniqueKey并传递给子StreamBuilder,导致子树被强制完全重建,丢失之前的帖子显示状态。
  2. Stream重复订阅:在build方法中直接创建Firestore Stream,每次重建都会重新订阅,触发数据重置。
  3. 状态未持久化:当前显示的帖子状态仅依赖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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 01:55:55