如何将Future转为Stream以获取Firebase实时数据更新
实现Firestore数据实时更新(从Future转为Stream)
1. 编写Stream版本的数据获取方法
替换原来的一次性Future方法,改用Firestore的snapshots()监听集合实时变化:
Stream<List<Medicine>> getAllMedRecordStream(String id) { return _db.collection("ShareDate").doc(id).collection("medicineReminder") .snapshots() .map((snapshot) => snapshot.docs.map((doc) => Medicine.fromSnapshot(doc)).toList()); }
2. 替换FutureBuilder为StreamBuilder
将原来的FutureBuilder换成StreamBuilder,监听上述Stream实现UI自动刷新:
StreamBuilder<List<Medicine>>( stream: controller.getAllMedRecordStream(currentUser!), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.active) { if (snapshot.hasData) { final medicineList = snapshot.data!; return ListView.separated( scrollDirection: Axis.vertical, shrinkWrap: true, separatorBuilder: (_, __) => const SizedBox(height: 25), itemCount: medicineList.length, itemBuilder: (_, index) { final medicine = medicineList[index]; return GestureDetector( onTap: () { Get.to(() => MedicineDetailScreen(medicine: medicine)); }, child: Container( height: medicine.status! == "Pending"? 300 : 230, decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: tPrimaryColor, ), child: specialBackground( top1: -140, top2: 20, height: 80, child: Container( padding: EdgeInsets.all(15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Medicine Reminder", style: Theme.of(context) .textTheme .headlineLarge! .apply(color: tDarkModeColor), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( "Start Time: ${medicine.startTime!}", style: Theme.of(context) .textTheme .headlineMedium! .apply(color: tDarkModeColor), ), Divider(color: tDarkModeColor), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Image( image: NetworkImage(medicine.image!), height: 90, width: 90, ), SizedBox(width: 30), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Name: ${medicine.medicineName!}", style: Theme.of(context) .textTheme .headlineMedium! .apply(color: tDarkModeColor), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( "Type: ${medicine.medicineType!}", style: Theme.of(context) .textTheme .headlineMedium! .apply(color: tDarkModeColor), maxLines: 2, overflow: TextOverflow.ellipsis, ), Text( "Status: ${medicine.status!}", style: Theme.of(context) .textTheme .headlineMedium! .apply( color: medicine.status! == "Took" ? Colors.green : medicine.status! == "Pending"? Colors.grey : Colors.red ), ), ], ), ], ), medicine.status! == "Pending"? Column( children: [ SizedBox(height: 20), Row( children: [ Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.green), onPressed: () { controller.markStatusAsTook(medicine.date!); }, child: Text( "Took".toUpperCase(), style: Theme.of(context) .textTheme .headlineMedium! .apply(color: tWhiteColor), ), ), ), SizedBox(width: 20), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () { controller.markStatusAsForget(medicine.date!); }, child: Text( "Forgot".toUpperCase(), style: Theme.of(context) .textTheme .headlineMedium! .apply(color: tWhiteColor), ), ), ) ], ), ], ) : Container() ], ), ), ), ), ); }, ); } else if (snapshot.hasError) { return Center(child: Text(snapshot.error.toString())); } else { return const Center(child: Text('暂无数据')); } } else { return Center(child: CircularProgressIndicator()); } }, )
核心改动说明
snapshots()替代get():该方法会持续监听集合数据变化,每次数据增删改都会推送最新快照- StreamBuilder状态判断:改用
ConnectionState.active,因为Stream会保持活跃状态,而非Future的一次性完成 - 复用原有
Medicine.fromSnapshot:无需修改即可将文档快照转换为实体对象
内容的提问来源于stack exchange,提问作者ZZZ iioo
相关产品推荐
相关产品推荐

