FlutterUnit 周边 | 收录排序算法可视化
theme: cyanosis
1. FlutterUnit 更新:排序算法可视化
排序算法可视化是用视图层表现出算法执行过程中排序的过程,感谢 编程的平行世界 在 《十几种排序算法的可视化效果,快来看看!👀》》 一文中提供的算法支持。我进行了一些代码和交互上的优化,将其集成到了 FlutterUnit 中,大家可以在 release v2.9.3 下载全平台应用查看体验 ~

掘金目前已经支持插入视频了,但目前支持西瓜视频。下面通过两个视频来看一下效果:
移动端: 交互视频
jvideo
桌面端: 交互视频
jvideo
2. 交互界面介绍
在移动端,排序算法可视化被放在 知识/可视排序 页签下,左上角的绿色按钮点击后启动排序,从而驱动数字列表数据变化,更新主界面产生排序的动态效果。下拉可以展开排序算法列表,选择对应的算法进行排序:
| 标题 | | | --- | --- | |
|
|
在桌面端,排序算法可视化先放在 可视排序 侧栏导航下,以后可能会拓展其他的有趣案例。

另外排序有设置界面,可以设置配置参数。
个数表示数据的数量,每个数据对应主界面中的一个线条。
间隔时长是排序过程中每步间的停顿时间,单位是微秒。
随机种子是随机数的种子,不为 -1 的话,相同的种子,每次重置生成的随机数列表一致。便于比较不排序算法下,同一组数据表现。

3. 项目的结构
这里核心代码新建了一个 algorithm 的包来单独维护,其中 algorithm/sort 文件夹中盛放排序的具体算法。把它们分文件放置,更便于阅读。

在 algorithm/data_scope 中,用于维护界面中的数据;在 algorithm/views 中处理视图组件的构建。

最后在 pubspec.yaml 中通过 path 引入本地的包,就可以在主项目中使用 algorithm 包中的组件进行展示。比如下面,在侧栏导航中添加一个 可视排序 的菜单栏,对应 DeskSortPage 组件。
yaml algorithm: path: packages/algorithm

4. 代码实现细节:算法方面
可视化排序的思路是:在每次排序列表数据发生变化时,通过回调来通知处理。这里定义 SortCallback 类型方便维护,其返回 Future<void> 对象,可以回调排序过程中此时的列表数据。
dart typedef SortCallback = Future<void> Function(List<int> dist);
拿冒泡排序来说,定义 bubbleSort 函数,传入待排序的数字列表,每次循环完成,出发 callback 通知调用者。比如想要放慢排序的过程,每一步可以等待一定的时间,也就是设置中的间隔微秒数。
dart ///冒泡排序 Future<void> bubbleSort(List<int> src, SortCallback callback) async{ //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 for (int i = 0; i < src.length; ++i) { //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 for (int j = 0; j < src.length - i - 1; ++j) { //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 if (src[j] > src[j + 1]) { int temp = src[j]; src[j] = src[j + 1]; src[j + 1] = temp; } //实现一个延迟,以便在ui上展示排序的动画效果 await callback(src); } } }
另外排序的函数结构都是一致的,输入待排序列表与回调,可以通过 typedef 定义一个排序函数类型 SortFunction:
dart typedef SortFunction = Future<void> Function(List<int> src, SortCallback callback);
这样就可以维护排序的名称和排序函数间的映射关系:
dart Map<String, SortFunction> sortFunctionMap = { 'insertion': insertionSort, 'bubble': bubbleSort, 'cocktail': cocktailSort, 'comb': combSort, 'pigeonHole': pigeonHoleSort, 'shell': shellSort, 'selection': selectionSort, 'gnome': gnomeSort, 'cycle': cycleSort, 'heap': heapSort, 'quick': quickSort, 'merge': mergeSort, };
5. 代码实现细节:数据方面
数据方面的代码在 data_scope 包中,这里排序界面中的数据有三大类:
其一是待排序数字列表。
其二是配置的参数。
其三是排序状态。
配置参数包括四个,通过 SortConfig 类维护:
```dart class SortConfig { final int count; // 列表数字数量 final int seed; // 随机数种子 final Duration duration; // 间隔时长 final String name; // 算法名称
SortConfig({ this.count = 100, this.duration = const Duration(microseconds: 1500), this.seed = -1, this.name = 'insertion', });
SortConfig copyWith({ int? count, int? seed, Duration? duration, String? name, }) => SortConfig( count:count??this.count, seed:seed??this.seed, duration:duration??this.duration, name:name??this.name, ); } ```
排序状态通过 SortStatus 枚举定义:
dart enum SortStatus{ none, // 未操作 sorting, // 排序中 sorted, // 排序完成 }
排序界面整体的数据状态通过 SortState 维护,它继承自 ChangeNotifier,可以在数据变化时调用 notifyListeners 通知监听者,从而实现界面的更新。 SortState 调用 sort 方法触发排序,会根据排序算法名,从 sortFunctionMap 中拿到排序算法调用。每次回调时触发 notifyListeners 方法通知更新。
```dart class SortState with ChangeNotifier{
SortState(){ reset(); }
SortStatus status = SortStatus.none; List data = []; SortConfig _config = SortConfig(); SortConfig get config => _config; Random random = Random();
set config(SortConfig config){ _config = config; reset(); notifyListeners(); }
void reset(){ data.clear(); status = SortStatus.none; notifyListeners(); int count = config.count; if(config.seed!=-1){ random = Random(config.seed); } for (int i = 0; i < count; i++) { data.add(random.nextInt(1000)); } }
void sort() async{ status = SortStatus.sorting; notifyListeners(); SortFunction? sortFunction = sortFunctionMap[config.name]; if(sortFunction!=null){ await sortFunction(data,(arr) async { await Future.delayed(config.duration); notifyListeners(); }); } status = SortStatus.sorted; notifyListeners(); } } ```
6. 代码实现细节:界面方面
这里目前没有使用三方状态管理包,而是通过 Flutter 内部的 InheritedNotifier 完成子树共享可监听数据的任务。
```dart class SortStateScope extends InheritedNotifier { const SortStateScope({ required super.notifier, required super.child, super.key, });
static SortState of(BuildContext context) => context.dependOnInheritedWidgetOfExactType ()!.notifier!; } ```
如果某个组件是数据的依赖者,在可监听对象发生变化时,会通知其更新。拿 SortButton 来说,他需要依赖排序状态 SortStatus 数据来展示不同的图标,或响应不同的事件。使用 SortStateScope.of(context) 相当于依赖了数据,那么数据(SortState)在 notifyListeners 时,就会通知 SortButton 进行重新构建,这就是 InheritedNotifier 组件的功能。
| none | sorting |sorted | | --- | --- |--- | |
|
|
```dart class SortButton extends StatelessWidget { const SortButton({super.key});
@override Widget build(BuildContext context) { SortState state = SortStateScope.of(context); VoidCallback? action; IconData icon; Color color; switch (state.status) { case SortStatus.none: icon = Icons.notstartedoutlined; color = Colors.green; action = state.sort; break; case SortStatus.sorting: icon = Icons.stopcircleoutlined; color = Colors.grey; action = null; break; case SortStatus.sorted: icon = Icons.refresh; color = Colors.black; action = state.reset; break; }
return GestureDetector(onTap: action,child: Icon(icon,color: color,),
); } } ```
最后,主体界面通过 CustomPainter 对数字列表进行绘制,遍历数据根据数值大小绘制不同高度的线条。
```dart class DataPainter extends CustomPainter { final List data;
DataPainter({required this.data});
@override void paint(Canvas canvas, Size size) { canvas.clipRect(Offset.zero & size); double itemWidth = size.width / data.length;
Paint paint = Paint();
paint.strokeWidth = itemWidth;
paint.strokeCap = StrokeCap.round;for (int i = 0; i < data.length; i++) {int value = data[i];if (value < 1000 * .10) {paint.color = Colors.blue.shade100;} else if (value < 1000 * .20) {paint.color = Colors.blue.shade200;} else if (value < 1000 * .30) {paint.color = Colors.blue.shade300;} else if (value < 1000 * .40) {paint.color = Colors.blue.shade400;} else if (value < 1000 * .50) {paint.color = Colors.blue.shade500;} else if (value < 1000 * .60) {paint.color = Colors.blue.shade600;} else if (value < 1000 * .70) {paint.color = Colors.blue.shade700;} else if (value < 1000 * .80) {paint.color = Colors.blue.shade800;} else if (value < 1000 * .90) {paint.color = Colors.blue.shade900;} else {paint.color = const Color(0xFF011E51);}canvas.drawLine(Offset(i * itemWidth + itemWidth / 2, 0),Offset(i * itemWidth + itemWidth / 2,size.height * (value / 1000),),paint);
} }
@override bool shouldRepaint(covariant DataPainter oldDelegate) { return true; } } ```
整个核心的逻辑就是这些,有兴趣的可以自己查阅 FlutterUnit 中的相关代码,那么本文就到这里,谢谢观看,我们下次再见~
相关文章:
FlutterUnit 周边 | 收录排序算法可视化
theme: cyanosis 1. FlutterUnit 更新:排序算法可视化 排序算法可视化是用视图层表现出算法执行过程中排序的过程,感谢 编程的平行世界 在 《十几种排序算法的可视化效果,快来看看!👀》》 一文中提供的算法支持。我进行…...
代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
LeetCode T435 无重叠区间 题目链接:435. 无重叠区间 - 力扣(LeetCode) 题目思路: 这题思路和昨天的打气球类似,我们需要按照左区间或者右区间进行排序,然后哦判断第i个区间的左端点和第i-1个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...
发展高质量存储力,中国高科技力量聚浪成潮
中国信息通信研究院指出,在全球数字化转型与产业变革的浪潮下,算力正在成为改变全球竞争格局的关键力量。而根据最新的《算力基础设施高质量发展行动计划》,算力是集信息计算力、数据存储力和网络运载力于一体的新型生产力。当前,…...
修改svc的LoadBalancer的IP引发的惨案
文章目录 背景修改externalIPs的操作api-server报错日志挽救教训 背景 k8s集群没有接外部负载均衡,部署istio的时候ingressgateway一直pending。 于是手动修改了这个lb svc的externalIP,于是k8s就崩了,如何崩的,且听我还道来。 …...
2520. 统计能整除数字的位数
2520. 统计能整除数字的位数 class Solution {public int countDigits(int num) {int res 0;int o num;while (num > 0) {if (o % (num % 10) 0) {res 1;}num num / 10;}return res;} }...
BeanUtils.copyProperties的用法
常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b,想把a中的属性赋值到b,例如 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set,即 XxxQuery xxxQuer…...
【RabbitMQ 实战】12 镜像队列
一、镜像队列的概念 RabbitMQ的镜像队列是将消息副本存储在一组节点上,以提高可用性和可靠性。镜像队列将队列中的消息复制到一个或多个其他节点上,并使这些节点上的队列保持同步。当一个节点失败时,其他节点上的队列不受影响,因…...
PyCharm社区版安装
PyCharm社区版安装 到中国官网下载 https://www.jetbrains.com/zh-cn/pycharm/download/?sectionwindows 首次创建项目,会自动下载安装Python 3.9 社区版的区别 社区版的区别...
【LeetCode每日一题合集】2023.10.16-2023.10.22(只出现一次的数字Ⅲ)
文章目录 260. 只出现一次的数字 III⭐(异或)🐂2652. 倍数求和解法1——枚举模拟解法2—— O ( 1 ) O(1) O(1)容斥原理相似题目——1201. 丑数 III(二分查找容斥原理) 2530. 执行 K 次操作后的最大分数解法1——贪心优…...
尚硅谷大数据项目《在线教育之实时数仓》笔记003
视频地址:尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第7章 数仓开发之ODS层 P015 第8章 数仓开发之DIM层 P016 P017 P018 P019 01、node001节点Linux命令 02、KafkaUtil.java 03、DimSinkApp.java P020 P021 P022 P023 第7章 数…...
【Linux】部署单体项目以及前后端分离项目(项目部署)
一、简介 以下就是Linux部署单机项目和前后端分离项目的优缺点,希望对你有所帮助。 1、Linux部署单机项目: 优点: 1.简化了系统管理:由于所有服务都在同一台机器上运行,因此可以简化系统管理和维护。 2.提高了性能&a…...
设计模式之门面模式
前言 什么是门面模式 门面模式是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,让子系统更容易使用。这种模式常用于将一个复杂的子系统封装成一个简单的接口,使得客户端可以方…...
Postman的使用
Postman的使用 Postman断言Postman常用断言1、断言响应状态码2、断言包含某个字符串3、断言JSON数据4、Postman断言工作原理 Postman关联Postman自动关联创建环境 3、Postman参数化CSV文件JSON文件1、用例集的导入导出2、环境导出 Postman断言 让Postman工具代替人自动判断预期…...
QGIS008:QGIS拓扑检查、修改及验证
摘要:本文介绍使用QGIS拓扑检查器和几何图形检查器检查图层的拓扑错误,修改拓扑错误,并对修改后的图层进行错误验证。 实验数据: 链接:https://pan.baidu.com/s/1Vy2s-KYS-XJevqHNdavv9A?pwdf06o 提取码:…...
安装DBD-Oracle报错处理
cd DBD-Oracle-1.83 perl Makefile.PL make && make install make编译报错如下: /bin/ld: 找不到 -lnsl collect2: 错误:ld 返回 1 make: *** [Makefile:524:blib/arch/auto/DBD/Oracle/Oracle.so] 错误 1 [rootlocalhost DBD-Ora…...
【机器学习】KNN算法-鸢尾花种类预测
KNN算法-鸢尾花种类预测 文章目录 KNN算法-鸢尾花种类预测1. 数据集介绍2. KNN优缺点: K最近邻(K-Nearest Neighbors,KNN)算法是一种用于模式识别和分类的简单但强大的机器学习算法。它的工作原理非常直观:给定一个新数…...
LuatOS-SOC接口文档(air780E)--lora - lora驱动模块
常量 常量 类型 解释 lora.SLEEP number SLEEP模式 lora.STANDBY number STANDBY模式 lora.init(ic, loraconfig,spiconfig) lora初始化 参数 传入值类型 解释 string lora 型号,当前支持: llcc68 sx1268 table lora配置参数,与具体设备…...
Compose 自定义 - 绘制 Draw
一、概念 所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸,请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。 二、Modifier 修饰符绘制 官方页面 在修饰的可组合项之上或之下绘制。 .drawWithCon…...
c#学习相关系列之构造函数
目录 一、构造函数的作用 二、构造函数的特征 三、三种构造函数介绍 1、实例构造函数 2、静态构造函数 3、私有构造函数 一、构造函数的作用 构造函数用来创建对象,并且可以在构造函数中对此对象进行初始化。构造函数具有与类相同的名称,它通常用来…...
CS224W1.3——图表示的选择
文章目录 1. 图网络构成2. 选择一个合适的表示3. 图结构实例3.1 二部图3.2 图的表示 4. 节点和边的属性 这小节主要讲图表示的选择。 1. 图网络构成 对于每个实体,我们创建节点 N N N,对于每个关系,我们创建边 E E E,对于整体而言…...
物理引导的机器学习工作流:气候建模的融合创新与实践
1. 项目概述:当气候建模遇见机器学习如果你像我一样,在气候模拟这个领域摸爬滚打超过十年,就会深刻体会到一种“甜蜜的负担”:我们构建的地球系统模型(ESM)越来越精细,物理过程越来越复杂&#…...
2026年HR招聘偏好白皮书:这5项附加技能出现频率暴涨
2026 年的招聘市场,正在从“看你会什么岗位技能”,转向“看你能不能把岗位做得更智能”。HR筛简历时,越来越关注候选人的AI应用能力、数据化思维和业务落地能力。人社部近年发布的新职业中,已经出现生成式人工智能系统应用员、人工…...
Graphin高级应用:结合GISDK构建配置化图分析模块的完整指南
Graphin高级应用:结合GISDK构建配置化图分析模块的完整指南 【免费下载链接】Graphin 🌌 A React toolkit for graph visualization based on G6. 项目地址: https://gitcode.com/gh_mirrors/gr/Graphin 在当今数据驱动的时代,图可视化…...
真可用!美团数字人模型开源,MV、电商等统统拿下
美团开源的数字人视频生成框架 LongCat-Video-Avatar 刚刚更新到 1.5 版本。是真能用。这版更新把音频编码器换了,推理步数砍到8步,在770人、13240条主观评分的大规模评测里,雷达图面积全面领先。音频编码器换血,8步出图LongCat-V…...
php有什么版本,php语言有几个版本
php有什么版本,php语言有几个版本PHP的大版本主要分四支:PHP4/PHP5/PHP6/PHP7 其中,PHP4由于太古老、对OO支持不力已基本被淘汰,请无视PHP4。 PHP6由于基本没有生产线上的应用,还基本只是一款概念产品,很多功能已在PHP…...
终极指南:用D2DX让《暗黑破坏神2》在现代电脑上焕发新生
终极指南:用D2DX让《暗黑破坏神2》在现代电脑上焕发新生 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 还在为经…...
基于C#实现(WinForm)P2P聊天程序
♻️ 资源 大小: 29.8MB ➡️ 资源下载:https://download.csdn.net/download/s1t16/87430269 p2p聊天程序 一、功能介绍 1.1 登录 用户凭用户名和密码登录系统,可以更换服务器 IP 和端口,以防网络不畅通,连接服务…...
Cesium动态数据可视化实战:CallbackProperty结合setInterval打造实时运动轨迹
Cesium动态数据可视化实战:CallbackProperty结合setInterval打造实时运动轨迹 在三维地理信息系统中,实时数据可视化一直是开发者面临的挑战之一。想象一下,当我们需要在地球表面追踪一架正在飞行的无人机,或者监控城市中数百辆出…...
5步完美解决Windows 10 PL2303驱动兼容性问题:完整实施方案指南
5步完美解决Windows 10 PL2303驱动兼容性问题:完整实施方案指南 【免费下载链接】pl2303-win10 Windows 10 driver for end-of-life PL-2303 chipsets. 项目地址: https://gitcode.com/gh_mirrors/pl/pl2303-win10 在Windows 10系统中使用PL2303 USB转串口设…...
当卫星在天上“读懂”人间:ICLR 2025 论文深度解读师玉娇、昃向辉的CS2S
把一张卫星图变成一张街景照片,就像把一个俯视棋盘拼成一面看台——不仅要摆对每一枚棋子,还要看懂整场比赛想象这样一个场景:你在城市规划部门工作,需要快速生成某条街道在不同季节、不同天气条件下的真实渲染效果,以…...
