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

Flutter点击Retry按钮触发组件已卸载上下文异常问题求助

问题描述

启动页检查用户网络状态,无网时展示无网络页面并提供Retry按钮,点击按钮时触发以下异常:

Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct)
E/flutter (25542): Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.

相关代码

启动页代码

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  Future<bool> isLoggedIn() async {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString("token");
    print("Token obtained: $token");
    return token != null;
  }

  @override
  void initState() {
    _init();
    super.initState();
  }

  Future<void> _init() async {
    bool isConnected = await NetworkHelper.checkInternet();
    if (!isConnected) {
      if (mounted) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(
              builder: (context) => CheckConnection(
                    onRetry: _init,
                  )),
        );
      }
    } else {
      await Future.delayed(const Duration(seconds: 3), () async {
        final isTokenValid = await isLoggedIn();
        if (isTokenValid) {
          if (mounted) {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(builder: (context) => BottomNav()),
            );
          }
        } else {
          if (mounted) {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(builder: (context) => Signup()),
            );
          }
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.white,
        child: FlutterLogo(size: MediaQuery.of(context).size.height),
      ),
    );
  }
}

无网络页面代码

class CheckConnection extends StatelessWidget {
  final VoidCallback onRetry;
  const CheckConnection({Key? key, required this.onRetry}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.asset("assets/image/no_internet.jpg"),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: onRetry,
            child: const Text("Retry"),
          ),
        ],
      ),
    );
  }
}

问题原因

当SplashScreen通过Navigator.pushReplacement跳转到CheckConnection后,SplashScreen的State已被销毁(unmounted),但CheckConnection仍持有SplashScreen中_init方法的引用。点击Retry按钮调用_init时,方法内的mounted检查针对的是已失效的SplashScreenState,其context无法再用于导航操作,从而触发异常。

解决方案

将网络检查和页面导航逻辑迁移到CheckConnection内部,让它独立处理重试逻辑,不再依赖已销毁的SplashScreenState。修改后的代码如下:

修改后的无网络页面(转为StatefulWidget)

class CheckConnection extends StatefulWidget {
  const CheckConnection({Key? key}) : super(key: key);

  @override
  State<CheckConnection> createState() => _CheckConnectionState();
}

class _CheckConnectionState extends State<CheckConnection> {
  Future<bool> isLoggedIn() async {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString("token");
    print("Token obtained: $token");
    return token != null;
  }

  Future<void> _checkAndNavigate() async {
    bool isConnected = await NetworkHelper.checkInternet();
    if (isConnected) {
      final isTokenValid = await isLoggedIn();
      if (mounted) {
        if (isTokenValid) {
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(builder: (context) => BottomNav()),
          );
        } else {
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(builder: (context) => Signup()),
          );
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Image.asset("assets/image/no_internet.jpg"),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: _checkAndNavigate,
            child: const Text("Retry"),
          ),
        ],
      ),
    );
  }
}

修改后的启动页代码

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  Future<bool> isLoggedIn() async {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString("token");
    print("Token obtained: $token");
    return token != null;
  }

  @override
  void initState() {
    _init();
    super.initState();
  }

  Future<void> _init() async {
    bool isConnected = await NetworkHelper.checkInternet();
    if (!isConnected) {
      if (mounted) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (context) => const CheckConnection()),
        );
      }
    } else {
      await Future.delayed(const Duration(seconds: 3), () async {
        final isTokenValid = await isLoggedIn();
        if (mounted) {
          if (isTokenValid) {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(builder: (context) => BottomNav()),
            );
          } else {
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(builder: (context) => Signup()),
            );
          }
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.white,
        child: FlutterLogo(size: MediaQuery.of(context).size.height),
      ),
    );
  }
}

说明

  • 将CheckConnection改为StatefulWidget,内部实现_checkAndNavigate方法处理网络检查和导航,确保使用当前页面的有效context。
  • 启动页跳转到CheckConnection时不再传递回调,避免引用已销毁的State方法。
  • 所有导航操作前保留mounted检查,防止页面在异步操作完成前被销毁引发异常。

内容的提问来源于stack exchange,提问作者Mansoor Malik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:10:12