Flutter 多个弹窗关闭指定弹窗

前言

大家都知道Flutter的页面是堆栈式管理,通常关闭页面是最后进入的最先关闭,通过pop进行一个退栈操作。

但是我碰到一个问题,有时需要在同一页面上显示多个弹窗。如果此时需要关闭指定的某一个弹窗,那退栈操作明显不合适了,总不能随便关闭最顶级的Dialog吧。

通过几天的琢磨,终于让我发现可以使用 GlobalKeyNavigator 的结合来实现。以下是详细的步骤和原理。

第一步:创建需要关闭弹窗的 GlobalKey

首先,在页面中为每个需要控制的弹窗创建一个 GlobalKey

dart 复制代码
GlobalKey<NavigatorState> dialogKey1 = GlobalKey();
GlobalKey<NavigatorState> dialogKey2 = GlobalKey();

第二步:将 Key 传递给 showDialog 中的 builder 创建的任意一个小部件

在调用 showDialog 时,将 GlobalKey 传递给弹窗的构建器

dart 复制代码
_showDialog() {
  showDialog(
    context: context,
    builder: (context) {
      return Dialog(
        key: dialogKey1,
        child: Text('弹窗1'),
      );
    },
  );
}

_showDialog2() {
  showDialog(
    context: context,
    builder: (context) {
      return Dialog(
        key: dialogKey2,
        child: Text('弹窗2'),
      );
    },
  );
}

第三步:关闭指定的弹窗

当需要关闭指定的弹窗时,只需在页面中调用如下方法:

dart 复制代码
_dismissDialog1() {
  if (dialogKey1.currentContext != null) {
    final routeDialog = ModalRoute.of(dialogKey1.currentContext!);
    if (routeDialog != null) {
      Navigator.removeRoute(context, routeDialog);
    }
  }
}

原理

该方法的原理是,弹窗是通过 Navigator 创建的。通过 Navigator.removeRoute 可以关闭指定路由页面。结合 GlobalKey 获取对应页面的 BuildContext,再通过 ModalRoute 从上下文中提取出 Route,并将其传递给 Navigator 进行移除就能达到关闭指定页面的目的。这种方法不仅适用于 Dialog,也适用于所有通过 Navigator 路由的页面,而且这也不是唯一的方案,通过了解发现可以通过堆栈式管理等方案也可以做到同样的操作

完整示例代码

以下是完整的示例代码,展示如何实现上述功能:

dart 复制代码
import 'package:flutter/material.dart';

class DemoPage extends StatefulWidget {
  const DemoPage({super.key});

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  GlobalKey<NavigatorState> dialogKey1 = GlobalKey();
  GlobalKey<NavigatorState> dialogKey2 = GlobalKey();

  @override
  void initState() {
    super.initState();
    // 一秒后连续打开两个 Dialog
    Future.delayed(Duration(seconds: 1)).then((_) {
      _showDialog();
      _showDialog2();
    });
    // 三秒后仅关闭第一个弹出的 Dialog
    Future.delayed(Duration(seconds: 3)).then((_) {
      _dismissDialog1();
    });
  }

  _showDialog() {
    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          key: dialogKey1,
          child: Text('弹窗1'),
        );
      },
    );
  }

  _showDialog2() {
    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          key: dialogKey2,
          child: Text('弹窗2'),
        );
      },
    );
  }

  _dismissDialog1() {
    if (dialogKey1.currentContext != null) {
      final routeDialog = ModalRoute.of(dialogKey1.currentContext!);
      if (routeDialog != null) {
        Navigator.removeRoute(context, routeDialog);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Text('弹窗Demo');
  }
}

结论

作者对于Flutter的了解也是知之甚少,作为一款目前最优秀的跨平台开发框架,即使碰到问题我也还是会去琢磨琢磨,但是真心希望Flutter别再将大量精力花费在小部件了,几百上千个小部件真学不动了

最后我还有一句话要说

周围二十座雪山,

唯一动弹的

是乌鸫的眼睛。

Wallace Stevens《观察乌鸫的十三种方式》

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试