Flutter使用Firestore的listen监听数据更新触发整页刷新如何解决
问题根因
- 两处全局
setState调用触发了整页重绘:DropdownButton的onChanged回调中用全局setState包裹选中值修改逻辑,会触发当前页面所有组件重绘getRouteName方法监听routes集合快照的回调里调用了全局setState,只要routes集合数据变化就会触发整页重绘
getRoute方法存在冗余写入逻辑:选中值为No Route时会重复写入两次相同内容,无实际意义。
解决方案
核心思路
用ValueNotifier配合ValueListenableBuilder实现局部刷新,仅让DropdownButton组件自己重绘,不影响页面其他组件,同时优化Firestore读写逻辑减少不必要操作。
示例修改代码
1. 定义局部状态管理器
替换原来的selectedItem变量定义:
// 替换原 String? selectedItem 定义 ValueNotifier<String?> selectedItem = ValueNotifier(null); List<String> routeName = [];
2. 修改DropdownButton组件代码
用ValueListenableBuilder包裹下拉组件,仅监听选中值变化触发局部刷新:
Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ValueListenableBuilder<String?>( valueListenable: selectedItem, builder: (context, value, child) { return DropdownButton( items: routeName.map((item) { return DropdownMenuItem<String>( value: item, child: Text(item), ); }).toList(), underline: SizedBox(height: 0,), onChanged: (value) { // 不再调用全局setState,仅更新局部状态 selectedItem.value = value; polylineCoordinates.clear(); getRoute(); }, hint: Text("Select a Route"), value: value, ); }, ) ], ),
3. 优化下拉选项拉取逻辑
如果不需要实时更新下拉选项,用单次拉取替代长期监听,避免不必要的触发:
Future<void> getRouteName() async{ // 仅拉取一次路由列表,不需要实时更新就用getDocuments final routeData = await Firestore.instance.collection('routes').getDocuments(); routeName.clear(); if(routeData.documents.isNotEmpty){ routeName.add("No Route"); for (int i = 0; i < routeData.documents.length; i++){ routeName.add(routeData.documents[i].documentID); } } // 通知下拉组件刷新选项 selectedItem.notifyListeners(); // 如果确实需要实时监听路由列表变化,用下面的代码替代上面的单次拉取 // Firestore.instance.collection('routes').snapshots().listen((routeData) { // routeName.clear(); // if(routeData.documents.isNotEmpty){ // routeName.add("No Route"); // for (int i = 0; i < routeData.documents.length; i++){ // routeName.add(routeData.documents[i].documentID); // } // } // selectedItem.notifyListeners(); // }); }
4. 优化写入Firestore逻辑
去掉冗余写入操作:
Future<void> getRoute() async{ final user = await FirebaseAuth.instance.currentUser(); await Firestore.instance.collection('user').document(user.email).setData( {"Route": selectedItem.value}, merge: true ); }
5. 资源释放
页面销毁时释放状态管理器避免内存泄漏:
@override void dispose() { selectedItem.dispose(); // 如果用了实时监听routes集合,这里也要取消对应的StreamSubscription super.dispose(); }
内容的提问来源于stack exchange,提问作者randomstudent
相关产品推荐
相关产品推荐

