Flutter 队列任务的实现

目录
  • 前言
  • 队列
    • 添加任务进队列
    • 移除队列指定任务
    • 判断是否包含对应任务
  • 执行队列任务
  • 任务条件
    • 添加任务时加入条件
    • 执行任务前判断条件是否满足
  • 使用和总结

前言

在电商的应用中,最常见的就是在首页或完成某事件之后,弹出一堆的活动/广告。假如重叠弹出,很丑,给用户的体验也不好,所以一般都会依次依条件的弹出。

下面讲讲我是怎么实现一个方便的队列任务管理

队列

任务队列,那当然要有个队列。这个队列的任务内容应该是返回FutureFunction,因为我需要得到他处理完成的结果,比如等待弹窗关闭时return,才表示这个任务被完成。

typedef TaskEventFunction = Future Function();

class TaskQueue {
    List<TaskEventFunction> _actionQueue = [];
}

添加任务进队列

然后是加入队列的方法add

void add(TaskEventFunction task) {
  _actionQueue.add(task);
}

然后想到,队列是不是最好去重?或者可选去重。

那一个Function如何去重呢,我们可以使用它的hashCode,同一个FunctionhashCode相同

taskQueue.add(testFunction);

Future testFunction() async {
  await Future.delayed(const Duration(milliseconds: 1000));
  return Future.value(true);
}

即我们可以按上面这样定义,同一个类同一个实例下同一个Function,就是相同的hashCode。若是以下这种写法则hashCode不一样,每次add都相当于一个新的Function

taskQueue.add(() async {
  await Future.delayed(const Duration(milliseconds: 1000));
  return Future.value(true);
});

假如不需要去重,可以用第二种。也可以主动指定taskId来选择哪些需要去重(即使内容不一样),哪些不需要。

修改一下add,增加taskId。同时hashCode应该作为主要的key,修改一下队列类型。 最终如下:

/// 任务编号队列
List<String> _actionQueue = [];

/// 任务队列
Map<String, TaskEventFunction> _actionMap = {};

/// 指定taskId 为 -1 则不用去重
String add(TaskEventFunction task, {
  String? taskId,
}) {
  String? id = taskId;
  id ??= task.hashCode.toString();
  bool isContains = false;
  if (taskId != '-1') {
    isContains = _actionQueue.contains(id);
  } else {
    id = task.hashCode.toString();
  }
  if (!isContains) {
    _actionQueue.add(id);
    _actionMap[id] = task;
  }
  return id;
}

-1时则不去重,也把最终的taskId返回。

移除队列指定任务

有添加任务的方法,那也需有移除任务的方法

/// 这里需注意,add的时taskId为-1,那就直接把task传入,add时有返回,可以对应
bool remove(TaskEventFunction task, {String? taskId}) {
  String? id = taskId;
  id ??= task.hashCode.toString();
  if (_actionQueue.contains(id)) {
    _actionMap.remove(id);
    return _actionQueue.remove(id);
  }
  return false;
}

taskId/hashCode为准。

判断是否包含对应任务

使用者可以自己判断是否已包含对应的任务

/// 是否队列中包含该任务
/// [task] 与 [taskId] 应该只传一个
bool containers({TaskEventFunction? task, String? taskId}) {
  assert(task != null || taskId != null);
  String? id = taskId;
  id ??= task.hashCode.toString();
  return _actionQueue.contains(taskId);
}

执行队列任务

任务队列的进出基本成型,开始处理任务。很简单,取出任务,然后执行任务,最后移除任务

void startLoop() async {
  if (dealing || _actionQueue.isEmpty) {
    return;
  }
  dealing = true;
  String taskId = _actionQueue.first;
  TaskEventFunction? callback = _actionMap[taskId];
  if (callback == null) {
    _actionQueue.remove(taskId);
    return;
  }

  try {
    await callback();
  } catch (e) {
    log('_actionQueue 出错 $e');
  } finally {
    _actionQueue.remove(taskId);
    _actionMap.remove(taskId);
    dealing = false;
    if (_actionQueue.isNotEmpty) {
      startLoop();
    }
  }
}

这里加了个dealing,表示任务正在处理中的状态,正在处理的话,不允许执行下一个任务。

在执行完成(或失败)后,自动触发下一个任务。add中也可以加入startLoop,使添加任务后自动启动任务循环。

任务条件

基本的任务队列已经完成。不过每个任务其实一般都会有个条件,确认符合当前场景才执行。比如说的确是新人且在首页,才显示新人优惠弹窗。

条件可以是整个任务队列统一的条件,也可以是某个任务特定的条件

typedef TaskConditionFunction = FutureOr<bool> Function();

这里类型用FutureOr<bool>,有可能需要等待一下才能确认条件。任务对应的条件,也根据taskId来存储。

/// 任务条件
Map<String, TaskConditionFunction> _actionCondition = {};

/// 总条件
TaskConditionFunction? _condition;

添加任务时加入条件

TaskEventFunction add(TaskEventFunction task, {
  String? taskId,
  TaskConditionFunction? condition,
}) {
    ...

    if (condition != null && !_actionCondition.containsKey(id)) {
      _actionCondition[id] = condition;
    }

}

设置总条件,或补充条件

/// 设置允许执行条件
void setAcceptConditions(TaskConditionFunction condition,
    {String? taskId}) {
  if (taskId == null) {
    _condition = condition;
  } else {
    _actionCondition[taskId] = condition;
  }
}

执行任务前判断条件是否满足

    bool canNext = await _nextConditions(taskId);
    if (canNext) {
      try {
        await callback();
      } catch (e) {
        log('_actionQueue 出错 $e');
      } finally {
        _actionQueue.remove(taskId);
        _actionMap.remove(taskId);
        dealing = false;
        if (_actionQueue.isNotEmpty) {
          startLoop();
        }
      }
    } else {
      // 不满足条件一般后续的也不会执行
      dealing = false;
    }
Future<bool> _nextConditions([String? id]) async {
  String taskId = id ?? _actionQueue.first;
  bool canNext = true;
  var taskCondition = _actionCondition[taskId];
  if (_condition != null) {
    canNext = await _condition!.call();
  }
  if (canNext && taskCondition != null) {
    canNext = await taskCondition();
  }

  return Future.value(canNext);
}

原则上应该既满足总条件也满足任务条件

使用和总结

实例化TaskQueue

TaskQueue taskQueue = TaskQueue();

添加任务并开始任务

taskQueue.add(testFunction, condition: () async {
  await Future.delayed(const Duration(milliseconds: 200));
  return Future.value(true);
});
taskQueue.startLoop();

Future testFunction() async {
  return showDialog(...);
}

注意设置条件要返回bool

还可以加入类似延时等操作,跟条件一样配置即可。太久的操作不要像上面一样写在condition中,可能执行完之后又不满足了,根据具体情况考虑。

一般来说类似弹窗的returnawait showDialog就可以等待弹窗页面结束,再进行下一个。

跨页面还是当前页面的控制,只需要考虑是全局实例TaskQueue还是页面内实例TaskQueue

这样一个使用简单好用的任务队列就实现好了,更多相关Flutter 队列任务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Flutter WebView 预加载实现方法(Http Server)

    目录 背景 分析 HttpServer 接下来? 资源配置 下载解压与本地存储 版本管理与更新 获取LocalServer Url并加载Webview 兜底措施 统一管理 展示与分析 总结 Demo 背景 WebView是在APP中,可以很方便的展示web页面,并且与web交互APP的数据.方便,并且更新内容无需APP发布新版本,只需要将最新的web代码部署完成,用户重新刷新即可. 在WebView中,经常能够听到的一个需求就是:减少首次白屏时间,加快加载速度.因为加载web页面,必然会受到网络

  • Flutter实现切换应用时隐藏应用预览

    如果您要显示敏感数据,例如.钱包金额,或者只是当登录表单显示插入的密码清晰时(想想眼睛图标..),当您不在应用程序中时,您必须隐藏敏感数据. 许多应用程序已经这样做了,无论是在 Android 还是 iOS 上. 今天我们将看看如何在不使用任何包的情况下完成这项任务,并自己编写所有代码.别担心,这很容易. 因此,请继续了解当您从一个应用切换到另一个应用时如何隐藏应用预览. 开始使用 现在,让我们开始编码吧! 创建一个新的基本 Flutter 应用程序.(我想你知道该怎么做^^). 安卓 在 An

  • Flutter添加页面过渡动画实现步骤

    目录 正文 使用插件探索不同的转换 步骤 1: 在 pubspec.yaml 中添加页面动画转换 步骤 2: 在 PageOne 上导入库 步骤3.添加以下导航代码行 其他类型转换的完整代码: 总结 正文 大家好,在这篇文章中,我们将学习如何添加动画,同时从一个页面到其他在 Flutter.我们将覆盖不同类型的动画和实现基本动画 Flutter 使用包页动画过渡. 动画在提升用户体验方面起着至关重要的作用,但动画到底是什么呢? 设计语言,例如 Material,定义了在路线(或屏幕)之间转换时的

  • 基于flutter sound插件实现录音与播放功能

    目录 插件介绍: 插件信息: 插件使用前的准备工作 设置麦克风权限描述 权限管理插件 permission_handler 音频硬件配置插件 audio_session 动画插件 常用的方法 录音常见的方法 初始化录音对象 开启录音 麦克风权限 开始录音 结束录音 播放常见的方法 初始化播放器 初始化操作 开始播放 结束播放 动画实现 加载GIF动画 加载动画文件 上传文件 上传音频文件 总结 插件介绍: flutter_sound这个插件可以实现iOS和Android平台的录音和播放功能.即可

  • 基于Flutter实现多边形和多角星组件

    目录 前言 组件功能 1.原理 2.找点 小结: 前言 开发中,免不了会用到多边形.多角星等图案,比较常用的多边形比如雷达图.多角星比如评价星级的五角星等,本篇文章就使用Flutter绘制封装一个这样的组件. 组件功能 正多边形 正多角星 支持同时绘制多边形和多角星 角的饱满度调整 1.原理 五角星为例: 可以看到,在一个五角星中,一共有10个点,有5个点平均分布在大圆上面,有5个点平均分布在小圆上面,相当于对360度进行了10等分,那么我们只需要将这10个点找到,用线连起来即可. 2.找点 既

  • flutter项目引入iconfont阿里巴巴图标

    目录 下载图标 使用图标 注意 下载图标 这里直接去iconfont阿里巴巴矢量图标库,选好自己需要的图标,点击如下图所示[添加到库] 然后选择头像左侧的购物车图标 然后点击下载代码 引入图标 解压完打开文件夹可以看到如下文件列表,我们将.ttf文件放在项目的静态资源文件夹中[我直接放在asset文件夹下] 接着我们在项目的pubspec.yaml文件夹下引入字体文件,并设置字体族名称 fonts: - family: Iconfont fonts: - asset: asset/fonts/i

  • Flutter获取ListView当前正在显示的Widget信息(应用场景)

    目录 一.概述 二.应用场景 1.获取最顶部的子部件信息 2.视频列表自动播放 3.模块定位 三.使用 1.基本使用 2.手动触发 3.子部件信息 一.概述 Flutter 中的 ListView 相信大家都用的很熟了,不过有没有人遇到过一些这样的需求: 详情页滚动到某一指定模块后,停止滚动并根据该指定模块的大小弹出全屏新手引导 详情页在滚动过程中,顶部的模块定位导航栏需要及时更新指示器下标 视频列表在滚动过程中,适当位置的子部件会自动进行播放视频 等等 在日常开发过程中这种类似的功能需求还是蛮

  • Android利用Flutter path绘制粽子的示例代码

    目录 前言 绘制 基本轮廓 粽叶 嘴巴 眼睛 腮红 手&脚 头巾 咸甜是一家 发声 动画控制嘴巴开合 用到的技术点 总结 前言 大家好,端午将至,首先提前祝小伙伴端午安康,端午作为中华民族的非常重要的传统节日,粽子那是必不可少的,但是你真的知道粽子的历史吗? 今天跟随本篇文章用Flutter path画一个会科普节日的的粽子吧- 绘制 基本轮廓 首先我们需要将粽子的基本轮廓绘制出来,通过图片可以看到粽子的轮廓是一个圆圆的三角形状, 本篇文章所有的图形都是用纯Path路径制作,这里我们可以将粽子的

  • 详解Flutter自定义应用程序内键盘的实现方法

    目录 创建关键小部件 文本键 Backspace键 将按键组成键盘 在应用程序中使用键盘 处理文本输入 处理退格 防止系统键盘显示 在系统键盘和自定义键盘之间切换 完整代码 本文将向您展示如何创建自定义键盘小部件,用于在您自己的应用程序中的Flutter TextField中输入文本.使用案例包括特殊字符或语言的文本输入,其中系统键盘可能不存在或用户可能没有安装正确的键盘. 我们今天将制作一个更简单的版本: 注意 :本文不会告诉您如何构建用户在任何应用程序中安装和使用的系统键盘.这只是一种基于小

  • Flutter 队列任务的实现

    目录 前言 队列 添加任务进队列 移除队列指定任务 判断是否包含对应任务 执行队列任务 任务条件 添加任务时加入条件 执行任务前判断条件是否满足 使用和总结 前言 在电商的应用中,最常见的就是在首页或完成某事件之后,弹出一堆的活动/广告.假如重叠弹出,很丑,给用户的体验也不好,所以一般都会依次依条件的弹出. 下面讲讲我是怎么实现一个方便的队列任务管理. 队列 任务队列,那当然要有个队列.这个队列的任务内容应该是返回Future的Function,因为我需要得到他处理完成的结果,比如等待弹窗关闭时

  • Flutter 假异步的实现示例

    就像 android 有 handle 一样,消息队列这东西好像还真是系统必备,Flutter 也有自己的消息队列,只不过队列直接封装在了 Dart 的线程类型 Isolate 里面了,不过 Flutter 还是提供了 Futrue 这个 API 来专门来操作各种消息,以及实现基于消息队列的假异步 Flutter 的"异步"机制 这里的异步是加了引号的,可见此异步非真异步,而是假异步.Flutter 的 异步 不是开新线程,而是往所属线程的 消息队列 中添加任务,当然大家也可以按上文那

  • Flutter随机迷宫生成和解迷宫小游戏功能的源码

    此博客旨在帮助大家更好的了解图的遍历算法,通过Flutter移动端平台将图的遍历算法运用在迷宫生成和解迷宫上,让算法变成可视化且可以进行交互,最终做成一个可进行随机迷宫生成和解迷宫的APP小游戏.本人是应届毕业生,希望能与大家一起讨论和学习- 注:由于这是本人第一次写博客,难免排版或用词上有所欠缺,请大家多多包涵. 注:如需转载文章,请注明出处,谢谢. 一.项目介绍: 1.概述 项目名:方块迷宫 作者:沫小亮. 编程框架与语言:Flutter&Dart 开发环境:Android Studio 3

  • 详解Flutter中Dart集合使用教程

    目录 前言 优先使用集合的特有语法 不要使用.length 属性判断集合是不是为空 避免使用 forEach 迭代元素 不要使用 List.from(),除非你想要更改结果的类型 使用 whereType 过滤类型 避免使用 cast() 做强制转换 总结 前言 集合是应用程序中最为常见的数据结构,Dart 一共支持如下四种集合,其中核心的 List, Map 和 Set 在基础框架中,而 Queue 在 dart:collection 库定义. 列表:也就是 List类,可动态增长的数组: k

  • flutter Bloc 更新后事件同步与异步详解

    目录 前言 使用方式 Bloc 新形态用法 事件队列的阻塞属性? 前言 最近,小轰参与了公司 flutter 项目关于 Dart 2.0 的空安全升级工作.我们升级了所有依赖的三方库,其中就包括有 Bloc 库.作为一款使用率颇高的状态管理框架, Bloc 在版本迭代中进行了少许结构和细节的优化,下面是小轰对于 Bloc 新版本的使用总结. 使用方式 小轰使用的 Bloc 版本如下 flutter_bloc: ^7.3.1 通过最简单的例子来学习新知识 创建一个包含 加 减 操作的页面,使用 b

  • Bloc事件流是一个阻塞队列结论解析

    前言 在Flutter中有state的概念,我们使用Bloc进行状态管理,通过Bloc.addEvent的方式进行事件传递,状态变更.关于Bloc的基础用法,可以查阅Bloc官网相关资料,这里我们仅记录一下Bloc的队列等待. 新建一个Bloc类 class TestBloc extends Bloc<TestEvent, TestState> { TestBloc() : super(new TestState()); @override Stream<TestState> ma

  • Android flutter Dio锁的巧妙实现方法示例

    正文 看Dio库源码的时候,发现其拦截器管理的逻辑处用到了一个Lock,这个Lock巧妙地利用了Completer和Future的机制来实现,记录一下. /// Add lock/unlock API for interceptors. class Lock { Future? _lock; late Completer _completer; /// 标识拦截器是否被上锁 bool get locked => _lock != null; /// Lock the interceptor. /

  • Java数据结构之有效队列定义与用法示例

    本文实例讲述了Java数据结构之有效队列定义与用法.分享给大家供大家参考,具体如下: /** * @描述 有序对列 * 从任何位置插入数据都是有序的 * @项目名称 Java_DataStruct * @包名 com.java.stack * @类名 Queue * @author chenlin */ public class SequeQueue { private long[] arr; private int maxSize;// 最大空间 private int len;// 有效长度

  • Java用数组实现循环队列的示例

    复习了下数据结构,用Java的数组实现一下循环队列. 队列的类 //循环队列 class CirQueue{ private int QueueSize; private int front; private int rear; private int[] queueList ; public CirQueue(int QueueSize){ this.QueueSize = QueueSize; queueList = new int[QueueSize]; front = 0; rear =

随机推荐