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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:28:08