Flutter Firebase登出时Unmounted Widget状态异常求助
Flutter页面已卸载后调用setState导致上下文错误
错误信息
Flutter This widget has been unmounted, so the State no longer has a context (and should be considered defunct). Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active
相关代码
加载客户门店页面代码
late List<Map<String, dynamic>> items; bool isLoaded = false; var collection = FirebaseFirestore.instance.collection("ClientStores").snapshots(); getStoresSubscribed() async { try { if (idCurrentSession != "" && usrClient == true) { List<Map<String, dynamic>> tempList = []; await for (var StoreSubscribedSnap in collection) { for (var StoreSubscribedDocs in StoreSubscribedSnap .docs) { if (StoreSubscribedDocs["StoreID"] != null) { String idStore = StoreSubscribedDocs["StoreID"]; var collection2 = FirebaseFirestore.instance .collection("ClientStores") .doc(idStore) .collection("Clients"); var docSnapshot = await collection2.doc(idCurrentSession).get(); if (docSnapshot.exists) { Map<String, dynamic>? data = docSnapshot.data(); String clientIdStore = data?['StoreID']; var dataStore = await FirebaseFirestore.instance .collection("Stores") .where("StoreID", isEqualTo: clientIdStore) .get(); for (var element in dataStore.docs) { tempList.add(element.data()); } setState(() { items = tempList; isLoaded = true; }); } } } } } } catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( backgroundColor: Colors.orangeAccent, content: Text( "$e", style: TextStyle(fontSize: 18.0), ))); } } @override void initState() { super.initState(); getStoresSubscribed(); }
返回主页代码
leading: BackButton( onPressed: () { Navigator.of(context).pop(); }, )
主页登出函数代码
Future<void> signout({required BuildContext context}) async { await FirebaseAuth.instance.signOut(); await Future.delayed(const Duration(seconds: 1)); Navigator.pushReplacement( context, MaterialPageRoute( builder: (BuildContext context) => const HomeScreen())); }
解决方案
问题核心是页面已被卸载(返回主页或登出跳转后),但getStoresSubscribed里的Stream监听和异步请求还在继续,之后调用setState或使用context就会报错。具体修复步骤:
取消Stream订阅
因为你用了snapshots()获取Stream,需要在页面销毁时取消订阅,避免后续回调执行:StreamSubscription? _storeSubscription; @override void initState() { super.initState(); _storeSubscription = collection.listen((snap) { _processStoreSnap(snap); // 将原await for内的逻辑移到这个方法中 }); } @override void dispose() { _storeSubscription?.cancel(); // 页面销毁时取消订阅 super.dispose(); }所有异步操作后检查
mounted
每次调用setState或使用context(比如显示SnackBar)前,必须先判断当前State是否还挂载:// 修改setState部分 if (mounted) { setState(() { items = tempList; isLoaded = true; }); } // 修改catch块中的SnackBar if (mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( backgroundColor: Colors.orangeAccent, content: Text( "$e", style: TextStyle(fontSize: 18.0), ))); }优化异步逻辑,减少不必要请求
- 在
getStoresSubscribed开头就检查mounted,如果已经卸载直接返回:getStoresSubscribed() async { if (!mounted) return; // 后续逻辑... } - 若只需加载一次数据,用
get()替代snapshots(),避免持续监听:// 替换原collection定义 var storeSnap = await FirebaseFirestore.instance.collection("ClientStores").get(); for (var storeDoc in storeSnap.docs) { // 原处理逻辑... }
- 在
登出时清理页面栈
用Navigator.pushAndRemoveUntil替代pushReplacement,确保所有旧页面被销毁:Future<void> signout({required BuildContext context}) async { await FirebaseAuth.instance.signOut(); Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => const HomeScreen()), (route) => false, // 移除所有之前的路由 ); }
内容的提问来源于stack exchange,提问作者Anthony Von Der Kreuz
相关产品推荐
相关产品推荐

