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

Flutter状态切换导致子Widget被dispose时如何安全弹出对话框?

解决方案:使用全局NavigatorKey获取顶层稳定上下文

该方案完全符合你的两个诉求,无需调整现有组件拆分结构,也不需要给子组件传递任何回调,仅需3处小改动即可解决问题:

  1. 定义全局NavigatorKey,绑定到MaterialApp上,该Key对应的上下文属于应用顶层Navigator层级,不会随页面内子组件切换销毁而失效。
  2. 改造弹窗方法,优先使用顶层稳定上下文弹出弹窗,避免依赖子组件的临时上下文。

修改后的完整可运行代码:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// 新增:全局NavigatorKey,绑定到MaterialApp
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

void main() {
  runApp(const MyApp());
}

void showAlertDialog({
  BuildContext? context,
  required String title,
  required String body,
  required bool showCancelOption,
  Function? onConfirm,
}) {
  // 优先使用顶层稳定上下文,不受子组件销毁影响
  final safeContext = navigatorKey.currentContext ?? context!;
  showDialog<void>(
    context: safeContext,
    barrierDismissible: false,
    builder: (BuildContext context) {
      List<Widget> actions = [
        TextButton(
          child: const Text("OK"),
          onPressed: () {
            Navigator.pop(context);
            if (onConfirm != null) {
              onConfirm();
            }
          },
        ),
      ];

      if (showCancelOption) {
        actions.add(
          TextButton(
            child: const Text("Cancel"),
            onPressed: () => Navigator.pop(context),
          ),
        );
      }

      return AlertDialog(
        title: Text(title),
        content: Text(body),
        actions: actions,
      );
    },
  );
}

enum MyState {
  one,
  transition,
  two,
}

class MyStateModel extends ChangeNotifier {
  MyState _state = MyState.one;
  MyState get state => _state;
  Future<bool> transitionState() async {
    var newState = _state == MyState.one ? MyState.two : MyState.one;
    _state = MyState.transition;
    notifyListeners();
    await Future.delayed(const Duration(milliseconds: 500)); // 模拟网络请求
    _state = newState;
    notifyListeners();
    return true;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MyStateModel>(
      create: (context) => MyStateModel(),
      child: MaterialApp(
        title: 'Flutter Demo',
        // 绑定全局NavigatorKey
        navigatorKey: navigatorKey,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var model = Provider.of<MyStateModel>(context);
    Widget body;
    // 原有简洁的状态分支逻辑完全保留,无需传递任何回调
    switch (model.state) {
      case MyState.one:
        body = const StateOneWidget();
        break;
      case MyState.transition:
        body = const StateTransitionWidget();
        break;
      case MyState.two:
        body = const StateTwoWidget();
        break;
    }
    return Scaffold(
      body: body,
    );
  }
}

class StateOneWidget extends StatelessWidget {
  const StateOneWidget({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: const Text("切换到状态2"),
        onPressed: () {
          showAlertDialog(
            context: context,
            title: "确认切换到状态2?",
            body: "该操作会执行状态切换逻辑",
            showCancelOption: true,
            onConfirm: () async {
              var model = Provider.of<MyStateModel>(context, listen: false);
              var success = await model.transitionState();
              if (success) {
                showAlertDialog(
                  context: context,
                  title: "切换成功",
                  body: "已进入状态2",
                  showCancelOption: false,
                );
              }
            },
          );
        },
      ),
    );
  }
}

class StateTwoWidget extends StatelessWidget {
  const StateTwoWidget({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: const Text("切换到状态1"),
        onPressed: () {
          showAlertDialog(
            context: context,
            title: "确认切换到状态1?",
            body: "该操作会执行状态切换逻辑",
            showCancelOption: true,
            onConfirm: () async {
              var model = Provider.of<MyStateModel>(context, listen: false);
              var success = await model.transitionState();
              if (success) {
                showAlertDialog(
                  context: context,
                  title: "切换成功",
                  body: "已进入状态1",
                  showCancelOption: false,
                );
              }
            },
          );
        },
      ),
    );
  }
}

class StateTransitionWidget extends StatelessWidget {
  const StateTransitionWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: CircularProgressIndicator(),
    );
  }
}

方案说明

  • 完全保留了你按状态拆分无状态子组件的代码结构,所有子组件的逻辑不需要做大的调整
  • 根组件不需要给子组件传递任何回调参数,build逻辑保持简洁干净
  • 顶层Navigator上下文永远稳定存在,不会因为页面内子组件的切换、销毁而失效,从根源上避免了context失效的报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 23:36:01