Flutter弹窗链-顺序弹出对话框

简介: Flutter弹窗链-顺序弹出对话框

效果

image.png

前言

弹窗的顺序执行在App中是一个比较常见的应用场景。比如进入App首页,一系列的弹窗就会弹出。如果不做处理就会导致弹窗堆积的全部弹出,严重影响用户体验。


如果多个弹窗中又有判断逻辑,根据点击后需要弹出另一个弹窗,这个弹窗优先级更高,需要在当前弹出框关闭后弹出,又添加了复杂度了,所以才会有需要管理多个弹窗的展示需求。

实现

  • 采用方式是拦截器法
/// 源码:https://github.com/yixiaolunhui/flutter_xy
/// 链式拦截器。
abstract class ChainInterceptor {
  /// 拦截器执行方法。
  void intercept(ChainHandler chain);
}

/// 链
abstract class Chain {
  /// 将拦截器添加到链中。
  /// 如果提供了 [index],则在指定的索引处添加拦截器。
  /// 否则,将拦截器添加到链的末尾。
  void addChain(ChainInterceptor interceptor, {int? index});

  /// 执行链
  void proceed();
}

/// 链状态监听器。
abstract class ChainStatusListener {
  /// 当链状态发生变化时调用。
  /// [isChainEnd] 表示链是否已经执行完毕。
  void onStatusChange(bool isChainEnd);
}

/// 构建和执行链帮助类
class ChainHelper {
  static Builder builder() {
    return Builder();
  }
}

/// 用于构建链的构建器类。
class Builder {
  final ChainHandler _chainHandler = ChainHandler();

  /// 将拦截器添加到链中。
  ///
  /// 如果提供了 [index],则在指定的索引处添加拦截器。
  /// 否则,将拦截器添加到链的末尾。
  Builder addChain(ChainInterceptor interceptor, {int? index}) {
    _chainHandler.addChain(interceptor, index: index);
    return this;
  }

  /// 设置链的状态监听器。
  Builder setChainStatusListener(ChainStatusListener chainStatusListener) {
    _chainHandler.setChainStatusListener(chainStatusListener);
    return this;
  }

  /// 获取 [ChainManager] 实例。
  ChainHandler get chainHandler => _chainHandler;

  /// 执行链。
  void execute() {
    _chainHandler.proceed();
  }
}

/// 链管理类
class ChainHandler implements Chain {
  final _chains = <ChainInterceptor>[];
  int _index = 0;
  ChainStatusListener? _statusListener;

  /// 设置链的状态监听器。
  void setChainStatusListener(ChainStatusListener chainStatusListener) {
    _statusListener = chainStatusListener;
  }

  /// 将拦截器添加到链中。
  ///
  /// 如果提供了 [index],则在指定的索引处添加拦截器。
  /// 否则,将拦截器添加到链的末尾。
  @override
  void addChain(ChainInterceptor interceptor, {int? index}) {
    if (index != null) {
      _chains.insert(index, interceptor);
    } else {
      _chains.add(interceptor);
    }
  }

  /// 获取链中拦截器的数量。
  int getChainCount() => _chains.length;

  /// 当前索引
  int get currentIndex => _index;

  /// 执行链。
  ///
  /// 通知 [ChainStatusListener] 链状态的变化。
  /// 如果链已经执行完毕,则清空链。
  @override
  void proceed() {
    bool isChainEnd = _index >= _chains.length;
    _statusListener?.onStatusChange(isChainEnd);
    if (isChainEnd) {
      clear();
      return;
    }
    _chains[_index++].intercept(this);
  }

  /// 清空链
  void clear() {
    _chains.clear();
    _index = 0;
  }
}

使用

  • 定义多个弹出框
/// 源码:https://github.com/yixiaolunhui/flutter_xy
///第1个弹窗
class OneDialog implements ChainInterceptor {
  @override
  void intercept(ChainHandler chain) {
    showDialog(
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('第1个弹出框'),
          content: const Text('这个是一个弹出框的内容文案'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('确认'),
            ),
          ],
        );
      },
      context: App.get().context,
    );
  }
}


///第2个弹窗
class TwoDialog implements ChainInterceptor {
  @override
  void intercept(ChainHandler chain) {
    showDialog(
      context: App.get().context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('第2个弹出框'),
          content: const Text('这个是一个弹出框的内容文案'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                //这里模拟插入新的弹出框,一般场景是点击按钮后,请求网络然后需要弹出新的弹出框
                chain.addChain(OtherDialog(), index: chain.currentIndex);
                chain.proceed();
              },
              child: const Text('添加其他弹出框'),
            ),
          ],
        );
      },
    );
  }
}

///第3个弹窗
class ThreeDialog implements ChainInterceptor {
  @override
  void intercept(ChainHandler chain) {
    showDialog(
      context: App.get().context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('第3个弹出框'),
          content: const Text('这个是一个弹出框的内容文案'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.addChain(OtherDialog(), index: chain.currentIndex);
                chain.proceed();
              },
              child: const Text('添加其他弹出框'),
            ),
          ],
        );
      },
    );
  }
}

///第4个弹窗
class FourDialog implements ChainInterceptor {
  @override
  void intercept(ChainHandler chain) {
    showDialog(
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('第4个弹出框'),
          content: const Text('这个是一个弹出框的内容文案'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('确认'),
            ),
          ],
        );
      },
      context: App.get().context,
    );
  }
}


///其他弹窗(用于案例中插入用)
class OtherDialog implements ChainInterceptor {
  @override
  void intercept(ChainHandler chain) {
    showDialog(
      context: App.get().context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('其他弹出框'),
          content: const Text('这个是一个弹出框的内容文案'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('取消'),
            ),
            TextButton(
              onPressed: () {
                Navigator.pop(context);
                chain.proceed();
              },
              child: const Text('确认'),
            ),
          ],
        );
      },
    );
  }
}

  • 如何使用
class ChainDialogPage extends StatefulWidget {
  const ChainDialogPage({super.key});

  @override
  State<ChainDialogPage> createState() => _ChainDialogPageState();
}

class _ChainDialogPageState extends State<ChainDialogPage> {
  var chainHelper = ChainHelper.builder();

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

  //显示对话框
  void showDialogs() {
    chainHelper.addChain(OneDialog());
    chainHelper.addChain(TwoDialog());
    chainHelper.addChain(ThreeDialog());
    chainHelper.addChain(FourDialog());
    chainHelper.execute();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: XYAppBar(
        title: "Flutter弹框链",
        onBack: () {
          Navigator.pop(context);
        },
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  showDialogs();
                },
                child: const Text("启动弹框链"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

运行后效果

image.png

详情:github.com/yixiaolunhui/flutter_xy

相关文章
|
1月前
|
Dart 搜索推荐 API
Flutter & 鸿蒙next版本:自定义对话框与表单验证的动态反馈与错误处理
在现代移动应用开发中,用户体验至关重要。本文探讨了如何在 Flutter 与鸿蒙操作系统(HarmonyOS)中创建自定义对话框,并结合表单验证实现动态反馈与错误处理,提升用户体验。通过自定义对话框和表单验证,开发者可以提供更加丰富和友好的交互体验,同时利用鸿蒙next版本拓展应用的受众范围。
84 1
|
4月前
|
开发者 Windows
Flutter笔记:Widgets Easier组件库(9)使用弹窗
Flutter笔记:Widgets Easier组件库(9)使用弹窗
123 3
|
4月前
|
UED 开发者
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
92 2
|
6月前
Flutter自定义对话框返回相关问题汇总
Flutter自定义对话框返回相关问题汇总
33 0
|
7月前
|
开发框架
Flutter自定义对话框返回相关问题汇总
Flutter自定义对话框返回相关问题汇总,详细解释 Flutter是一款流行的移动应用开发框架,它提供了很多内置的对话框,但是有时候我们需要自定义对话框来满足特定需求。在使用自定义对话框时,可能会遇到一些问题,下面是一些常见问题的解答:
|
存储 前端开发
Flutter 实现多选底部弹窗
本篇介绍了底部弹窗实现多选的方式,其中实现的方式还可以有很多种,例如直接在自定义组件中使用有状态组件。这里介绍的方法可以作为一个参考,通过动态构建有状态组件能够简单快速地实现底部弹窗的多选功能。
700 0
Flutter 实现多选底部弹窗
|
容器
Flutter 底部弹窗ModelBottomSheet详解
本篇介绍了三种 ModalBottomSheet 的方式,实际开发过程中,还可以根据需要,利用 ModalBottomSheet的 builder 方法构建自定义的底部弹窗,满足业务需要。
1235 0
Flutter 底部弹窗ModelBottomSheet详解
|
Dart API Android开发
flutter_update_dialog 一个漂亮的版本更新弹窗
flutter_update_dialog 一个漂亮的版本更新弹窗
382 0
|
存储
flutter使用 RfFlutter 来显示对话框
RfFlutter 具有看起来不错的基本警报,并且可以轻松使用。我们将设置一个带有 HomeView 无状态小部件的基本应用程序。我将使用功能性小部件,这样我就不会编写太多代码。您可以通过定义整个类来使用普通的无状态小部件。我们将让我们的应用程序带有一个简单的 HomeView 小部件。我们将在整个教程中使用的中心有一个按钮。
205 0
flutter自定义弹窗
今天接到这样一个需求,需要自定义弹窗,所以就有了如下的代码: 通过分析,一切皆widget 所以可以这样实现:
376 0