Flutter 弹窗队列管理:实现一个线程安全的通用弹窗队列系统

在开发复杂的 Flutter 应用时,弹窗的管理往往是一个令人头疼的问题。尤其是在多个弹窗需要按顺序显示,或者弹窗的显示需要满足特定条件时,手动管理弹窗的显示和隐藏不仅繁琐,还容易出错。为了解决这个问题,我们可以实现一个通用的弹窗队列管理系统,它能够自动管理弹窗的显示顺序,并且支持条件判断,决定是否显示某个弹窗。

一、需求分析

在实现弹窗队列管理系统之前,我们先明确一下需求:

  1. 支持弹窗队列:能够将多个弹窗按顺序排队,依次显示。
  2. 条件判断:每个弹窗可以指定一个条件函数,只有当条件满足时,弹窗才会显示。
  3. 线程安全:在多线程环境下,确保弹窗队列的操作是安全的。
  4. 通用性:不限制在 StatefulWidget 中使用,可以在任何地方调用。

二、实现思路

为了实现上述需求,我们采用以下设计思路:

  1. 单例模式:使用单例模式管理弹窗队列,确保全局只有一个队列管理实例。
  2. 线程安全:使用 synchronized 包来确保对队列的操作是线程安全的。
  3. 独立函数:提供一个独立的 showQueueDialog 函数,可以在任何地方调用,而不仅仅是 StatefulWidgetState 中。

三、代码实现

以下是完整的代码实现:

1. 弹窗队列管理类

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:synchronized/synchronized.dart';

const _defaultTag = 'default_dialog_queue_tag';

typedef BSQueueDialogCondition = FutureOr<bool> Function(BuildContext context);
typedef BSQueueDialogShow = FutureOr<void> Function(BuildContext context);

class BSQueueDialog {
  final BSQueueDialogCondition? shouldShow;
  final BSQueueDialogShow show;

  const BSQueueDialog({this.shouldShow, required this.show});
}

class DialogQueueManager {
  static final DialogQueueManager _instance = DialogQueueManager._internal();
  factory DialogQueueManager() => _instance;

  DialogQueueManager._internal();

  final _dialogQueue = <String, List<BSQueueDialog>>{};
  final _displayingDialog = <String, BSQueueDialog>{};
  final _lock = Lock();

  Future<void> showQueueDialog<R>({
    required BuildContext context,
    BSQueueDialogCondition? shouldShow,
    required BSQueueDialogShow show,
    String tag = _defaultTag,
  }) async {
    final dialog = BSQueueDialog(shouldShow: shouldShow, show: show);
    await _lock.synchronized(() async {
      var queue = _dialogQueue[tag];
      if (queue == null) {
        queue = <BSQueueDialog>[];
        _dialogQueue[tag] = queue;
      }
      queue.add(dialog);
      final displayingDialog = _displayingDialog[tag];
      if (displayingDialog == null) {
        _displayingDialog[tag] = dialog;
        await _showQueueDialog(tag, context);
      }
    });
  }

  Future<void> _showQueueDialog(String tag, BuildContext context) async {
    while (true) {
      await _lock.synchronized(() async {
        final queue = _dialogQueue[tag];
        if (queue == null || queue.isEmpty) {
          _dialogQueue.remove(tag);
          _displayingDialog.remove(tag);
          return;
        }
        final dialog = queue.removeAt(0);
        if (!mounted) return;
        final shouldShow = await dialog.shouldShow?.call(context) ?? false;
        if (!mounted) return;
        if (mounted && shouldShow) {
          _displayingDialog[tag] = dialog;
        } else {
          return; // 如果不应该显示,则直接返回
        }
      });

      if (!mounted) return;
      await dialog.show(context);

      await _lock.synchronized(() {
        _displayingDialog.remove(tag);
      });
    }
  }
}

2. 独立的 showQueueDialog 函数

// 独立的 showQueueDialog 函数
Future<void> showQueueDialog<R>({
  required BuildContext context,
  BSQueueDialogCondition? shouldShow,
  required BSQueueDialogShow show,
  String tag = _defaultTag,
}) async {
  return DialogQueueManager().showQueueDialog(
    context: context,
    shouldShow: shouldShow,
    show: show,
    tag: tag,
  );
}

3. 使用示例

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Queue Dialog Example',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Queue Dialog Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            showQueueDialog(
              context: context,
              shouldShow: (context) async {
                // 可以在这里添加条件逻辑
                return true;
              },
              show: (context) async {
                await showDialog(
                  context: context,
                  builder: (context) => AlertDialog(
                    title: Text('Queue Dialog'),
                    content: Text('This is a queued dialog.'),
                    actions: [
                      TextButton(
                        onPressed: () => Navigator.pop(context),
                        child: Text('Close'),
                      ),
                    ],
                  ),
                );
              },
            );
          },
          child: Text('Show Queue Dialog'),
        ),
      ),
    );
  }
}

四、代码说明

1. 单例模式

使用 DialogQueueManager 类封装队列管理逻辑,确保全局只有一个实例。通过 factory 构造函数和 _internal 私有构造函数实现单例模式。

2. 线程安全

使用 synchronized 包中的 _lock 对象,确保对 _dialogQueue_displayingDialog 的操作是线程安全的。

3. mounted 检查

在每次使用 context 前,都进行 mounted 检查,确保在 widget 被销毁后不会继续操作 context

4. 循环代替递归

使用 while (true) 循环代替递归调用,避免栈溢出问题。

5. 独立的 showQueueDialog 函数

提供了一个独立的 showQueueDialog 函数,可以在任何地方调用,而不仅仅是 StatefulWidgetState 中。

五、总结

通过上述实现,我们构建了一个通用的、线程安全的弹窗队列管理系统。这个系统不仅支持弹窗的按序显示,还支持条件判断,决定是否显示某个弹窗。通过提供独立的 showQueueDialog 函数,我们确保了这个系统可以在任何地方使用,而不仅仅是 StatefulWidgetState 中。这种方式更加灵活,适用于更多的场景,能够有效简化弹窗的管理逻辑,提高代码的可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值