繁体   English   中英

Flutter 未处理的异常:此小部件已被卸载,因此 State 不再具有上下文(应被视为已失效)

[英]Flutter Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct)

我已经检查了以前的答案,但帮不了我。

我正在检查用户是否有 inte.net,如果没有,我会显示没有 inte.net 的图片。 现在我有一个Retry按钮,用户可以在其中单击它以检查他是否已返回 inte.net。

现在我在点击Retry按钮时遇到错误

未处理的异常:此小部件已被卸载,因此 State 不再具有上下文(应被视为已失效)。 E/flutter (25542):考虑在“dispose”期间取消任何活动工作或使用“mounted”getter 来确定 State 是否仍然活动。

更新的启动画面

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),
      ),
    );
  }
}

如果没有 inte.net,这是我的图像。

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"),
          ),
        ],
      ),
    );
  }
}

你的代码有问题。 您正在尝试在所谓的asynchronous suspension之后访问context ,即,您在某些Future完成其工作后访问context ,而在这种情况下, context可能处于无效的 state,可能是因为Element树在期间发生了变化这种“异步暂停”。 我还假设您收到了有关此的警告,但您忽略了它。

这个问题的解决方案是,正如错误提示的那样,在你的Future完成它的工作之后和你访问context之前,使用State class 中可用的 getter mounted

await Future.delayed(...);
if(mounted){
   // you can use the context
}
else{
   // you can't use the context
}

在您的情况下,您需要在 Future 返回后使用它之前通过检查mounted变量来检查上下文是否仍然有效:

if(mounted) // you should put this check in all places where you use context after an asyncronous suspension
{
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(builder: (context) => BottomNav()),
          );
}
else{
// the context is not valid
// you should handle this case according to your app needs
}

另请参阅此答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM