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

如何实现Firestore数据实时更新?Flutter列表视图不刷新问题

如何实现Firestore数据实时更新,让ListView自动刷新?

我用FutureBuilder结合ListView.builder展示餐厅列表,每个列表项调用ApprovedRestaurants组件从Firestore获取数据渲染。但点击按钮更新餐厅的status为Approved或Banned后,列表视图不会自动刷新,必须切换页面才会更新。

上层列表代码:

FutureBuilder(
                  future: getRestaurantsList(),
                  builder: (context, snapshot) {
                    return ListView.builder(
                        itemCount: approveList.length,
                        itemBuilder: (context, index) {
                          return ListTile(
                            title: ApprovedRestaurants(documentId: approveList[index] 
  as String, Index: index ,),
                          );
                        });
                  })

ApprovedRestaurants组件代码:

FutureBuilder<DocumentSnapshot>(
    future: restaurants.doc(documentId).get(),
    builder: ((BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot){
  if(snapshot.connectionState == ConnectionState.done){
    Map<String, dynamic> data = snapshot.data!.data() as Map<String,dynamic>;
    return Card(
      child: Container(
          padding: EdgeInsets.all(MediaQuery.of(context).size.height / 150),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                children: [
                  Container(
                    width: MediaQuery.of(context).size.width / 1.17,
                    height: MediaQuery.of(context).size.height / 8,
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(10),
                        color: Colors.white38,
                        image: DecorationImage(
                            fit: BoxFit.cover,
                            image: AssetImage(AppImages.foodImg))),
                  ),
                  SizedBox(height: MediaQuery.of(context).size.height / 200,),
                  Text(data['restaurantName'], style: TextStyle(fontWeight: FontWeight.w700,fontSize: 12),),
                  SizedBox(height: MediaQuery.of(context).size.height / 120,),
                  Text(data['phoneNumber'],style: TextStyle(fontSize: 11),),
                  SizedBox(height: MediaQuery.of(context).size.height / 250,),
                  Text(data['email'],style: TextStyle(fontSize: 11),),
                  SizedBox(height: MediaQuery.of(context).size.height / 250,),
                  Text(data['address'],style: TextStyle(fontSize: 11),),
                  Row(
                    children: [
                      IconButton(onPressed: (){
                        FirebaseFirestore.instance.collection("Restaurants").doc(documentId).update({'status' : 'Approved'});
                      }, icon: Icon(Icons.check, color: Colors.green,)),
                      IconButton(onPressed: (){
                        FirebaseFirestore.instance.collection("Restaurants").doc(documentId).update({'status' : 'Banned'});
                      }, icon: Icon(Icons.close, color: Colors.red,)),
                    ],
                  ),
                ],
              ),
            ],
          )
      ),
    );;
  }
  return Container(
    child: AlertDialog(
      //loading dialogue here
    ),
  );
}));

解决方案:用StreamBuilder替代FutureBuilder实现实时监听

FutureBuilder只会在Future完成时触发一次UI重建,而StreamBuilder能持续监听Firestore的数据流变化,数据更新时自动刷新UI。

1. 上层列表改用StreamBuilder监听集合快照

替换原来的FutureBuilder,直接监听餐厅集合的实时快照,无需依赖全局的approveList变量:

StreamBuilder<QuerySnapshot>(
  stream: FirebaseFirestore.instance.collection("Restaurants").snapshots(),
  builder: (context, snapshot) {
    // 加载状态处理
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(child: CircularProgressIndicator());
    }
    // 无数据处理
    if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
      return const Center(child: Text("暂无餐厅数据"));
    }

    final restaurantDocs = snapshot.data!.docs;
    return ListView.builder(
      itemCount: restaurantDocs.length,
      itemBuilder: (context, index) {
        final docId = restaurantDocs[index].id;
        // 直接返回组件,无需ListTile包裹(组件本身是Card)
        return ApprovedRestaurants(documentId: docId, index: index);
      },
    );
  },
)

2. ApprovedRestaurants组件改用StreamBuilder监听单个文档快照

把原来一次性获取文档的get()换成实时监听的snapshots(),确保单个餐厅状态更新时组件自动刷新:

StreamBuilder<DocumentSnapshot>(
  stream: FirebaseFirestore.instance.collection("Restaurants").doc(documentId).snapshots(),
  builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
    // 加载状态处理
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(child: CircularProgressIndicator());
    }
    // 文档不存在处理
    if (!snapshot.hasData || !snapshot.data!.exists) {
      return const Text("该餐厅已不存在");
    }

    Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
    return Card(
      child: Container(
        padding: EdgeInsets.all(MediaQuery.of(context).size.height / 150),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Column(
              children: [
                Container(
                  width: MediaQuery.of(context).size.width / 1.17,
                  height: MediaQuery.of(context).size.height / 8,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    color: Colors.white38,
                    image: const DecorationImage(
                      fit: BoxFit.cover,
                      image: AssetImage(AppImages.foodImg),
                    ),
                  ),
                ),
                SizedBox(height: MediaQuery.of(context).size.height / 200),
                Text(
                  data['restaurantName'],
                  style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 12),
                ),
                SizedBox(height: MediaQuery.of(context).size.height / 120),
                Text(data['phoneNumber'], style: const TextStyle(fontSize: 11)),
                SizedBox(height: MediaQuery.of(context).size.height / 250),
                Text(data['email'], style: const TextStyle(fontSize: 11)),
                SizedBox(height: MediaQuery.of(context).size.height / 250),
                Text(data['address'], style: const TextStyle(fontSize: 11)),
                Row(
                  children: [
                    IconButton(
                      onPressed: () {
                        FirebaseFirestore.instance
                            .collection("Restaurants")
                            .doc(documentId)
                            .update({'status': 'Approved'});
                      },
                      icon: const Icon(Icons.check, color: Colors.green),
                    ),
                    IconButton(
                      onPressed: () {
                        FirebaseFirestore.instance
                            .collection("Restaurants")
                            .doc(documentId)
                            .update({'status': 'Banned'});
                      },
                      icon: const Icon(Icons.close, color: Colors.red),
                    ),
                  ],
                ),
              ],
            ),
          ],
        ),
      ),
    );
  },
);

关键说明

  • 上层监听整个集合的快照:新增、删除餐厅时,列表会自动更新
  • 单个组件监听自身文档快照:点击按钮修改status后,组件会即时刷新
  • 移除了全局的approveList变量,避免手动维护数据导致的状态不同步问题

内容的提问来源于stack exchange,提问作者H. M. Zaheer Asghar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:00:58