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

Flutter中两个StatefulWidget交互时出现‘Widget已卸载,State不再拥有上下文’错误的解决方案咨询

解决Flutter "This widget has been unmounted" 错误的实用方案

这个报错本质上是因为你的异步onAccept方法执行完成时,_HandScreen的State已经被卸载了——连续拖动两次时,第二次的异步操作大概率是在组件已经不再挂载的情况下执行,这时再用这个State的context去弹窗或导航,自然会触发错误。

下面给你几个针对性的解决办法,按优先级推荐:

1. 执行context相关操作前检查mounted状态

这是最直接的修复方式,也是Flutter官方推荐的做法。State类自带的mounted属性可以判断当前组件是否还处于挂载状态,你只需要在弹窗或导航前加上这个判断:

onAccept(value) async {
  print('value $value');
  // 先执行你的其他异步逻辑(如果有的话)
  // ...

  // 关键:在使用context前检查组件是否还挂载
  if (!mounted) return;

  try {
    Alert(
      context: context,
      title: 'Finished!',
      desc: 'You\'ve reached ',
      image:Lottie.asset(
        'assets/lotties/completed-check.json',
        height: 85.0,
        width: 85.0,
      ),
      buttons: [
        DialogButton(
          child: Text(
            "Selesai",
            style: TextStyle(color: Colors.white, fontSize: 20),
          ),
          onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
          color: Color.fromRGBO(0, 179, 134, 1.0),
        ),
      ],
    ).show();
    // 如果是导航操作,同样加判断
    // if (mounted) Navigator.pushNamed(context, MainScreen.id);
  } catch(e) {
    print('error = $e'); 
  }
}

这个判断能帮你避开组件已销毁但异步操作还在执行的场景,从根源上阻止无效的context调用。

2. 使用全局导航Key替代当前context

如果你觉得每次写mounted判断麻烦,可以在App根组件定义一个全局的GlobalKey<NavigatorState>,用它来获取全局有效的context进行弹窗或导航,不再依赖_HandScreen的局部context:

首先在main.dart中定义并绑定全局key:

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

void main() {
  runApp(MyApp(navigatorKey: navigatorKey));
}

class MyApp extends StatelessWidget {
  final GlobalKey<NavigatorState> navigatorKey;
  const MyApp({super.key, required this.navigatorKey});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey, // 绑定到MaterialApp
      // ...其他路由、主题配置
    );
  }
}

然后在onAccept中直接使用这个全局key:

onAccept(value) async {
  print('value $value');
  await Future.delayed(Duration(milliseconds: 100)); // 模拟你的异步操作

  try {
    Alert(
      context: navigatorKey.currentContext!, // 使用全局context
      title: 'Finished!',
      desc: 'You\'ve reached ',
      image:Lottie.asset(
        'assets/lotties/completed-check.json',
        height: 85.0,
        width: 85.0,
      ),
      buttons: [
        DialogButton(
          child: Text(
            "Selesai",
            style: TextStyle(color: Colors.white, fontSize: 20),
          ),
          onPressed: () => navigatorKey.currentState!.pop(),
          color: Color.fromRGBO(0, 179, 134, 1.0),
        ),
      ],
    ).show();
    // 导航操作示例:
    // navigatorKey.currentState!.pushNamed(MainScreen.id);
  } catch(e) {
    print('error = $e'); 
  }
}

注意:只要App正常运行,navigatorKey.currentContext就不会为null,这种方式适合频繁需要跨组件导航/弹窗的场景。

3. 结合Provider优化逻辑分离

因为你用了Provider做状态管理,可以把弹窗/导航的逻辑放到Provider中,但要注意Provider本身不持有context,所以还是需要结合mounted判断或全局key:

// 你的Provider类
class MyProvider extends ChangeNotifier {
  void showFinishAlert(BuildContext context) {
    if (!context.mounted) return; // 同样检查context有效性
    Alert(
      context: context,
      // ...弹窗配置
    ).show();
  }
}

然后在_HandScreen的onAccept中调用:

onAccept(value) async {
  print('value $value');
  await yourAsyncLogic(value); // 你的业务异步逻辑
  if (!mounted) return;
  Provider.of<MyProvider>(context, listen: false).showFinishAlert(context);
}

这种方式能把UI交互逻辑和业务逻辑做轻度分离,同时保证context的有效性。

总结

最推荐第一种方法——直接检查mounted属性,它简单高效,不需要额外的全局变量或复杂重构,完全适配你当前的组件结构,能快速解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 12:37:30