From 9313882748a14d566e3a71c493dd9e430be27312 Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Tue, 24 Oct 2023 12:17:11 +0800 Subject: [PATCH 1/3] v5 --- .../popable/drop_selectable_widget.dart | 198 +++++ lib/components/toly_ui/toly_ui.dart | 3 +- lib/v4/pages/sort/sort_page.dart | 4 +- .../router/app_router_delegate.dart | 13 +- .../app/navigation/views/app_navigation.dart | 5 + .../navigation/views/app_navigation_rail.dart | 1 + .../navigation/views/app_router_editor.dart | 32 + lib/v5/app/navigation/views/app_top_bar.dart | 4 +- lib/v5/pages/sort/functions.dart | 46 + lib/v5/pages/sort/functions/bubble.dart | 19 + lib/v5/pages/sort/functions/cocktail.dart | 52 ++ lib/v5/pages/sort/functions/comb.dart | 34 + lib/v5/pages/sort/functions/cycle.dart | 54 ++ lib/v5/pages/sort/functions/gnome.dart | 22 + lib/v5/pages/sort/functions/heap.dart | 38 + lib/v5/pages/sort/functions/insertion.dart | 19 + lib/v5/pages/sort/functions/merage.dart | 79 ++ lib/v5/pages/sort/functions/oddEven.dart | 37 + lib/v5/pages/sort/functions/pigeonHole.dart | 33 + lib/v5/pages/sort/functions/quick.dart | 65 ++ lib/v5/pages/sort/functions/selection.dart | 18 + lib/v5/pages/sort/functions/shell.dart | 21 + lib/v5/pages/sort/provider/sort_config.dart | 18 + lib/v5/pages/sort/sort_page.dart | 803 ++---------------- lib/v5/pages/sort/sort_setting.dart | 91 ++ pubspec.lock | 9 +- pubspec.yaml | 2 - 27 files changed, 988 insertions(+), 732 deletions(-) create mode 100644 lib/components/toly_ui/popable/drop_selectable_widget.dart create mode 100644 lib/v5/pages/sort/functions.dart create mode 100644 lib/v5/pages/sort/functions/bubble.dart create mode 100644 lib/v5/pages/sort/functions/cocktail.dart create mode 100644 lib/v5/pages/sort/functions/comb.dart create mode 100644 lib/v5/pages/sort/functions/cycle.dart create mode 100644 lib/v5/pages/sort/functions/gnome.dart create mode 100644 lib/v5/pages/sort/functions/heap.dart create mode 100644 lib/v5/pages/sort/functions/insertion.dart create mode 100644 lib/v5/pages/sort/functions/merage.dart create mode 100644 lib/v5/pages/sort/functions/oddEven.dart create mode 100644 lib/v5/pages/sort/functions/pigeonHole.dart create mode 100644 lib/v5/pages/sort/functions/quick.dart create mode 100644 lib/v5/pages/sort/functions/selection.dart create mode 100644 lib/v5/pages/sort/functions/shell.dart create mode 100644 lib/v5/pages/sort/provider/sort_config.dart create mode 100644 lib/v5/pages/sort/sort_setting.dart diff --git a/lib/components/toly_ui/popable/drop_selectable_widget.dart b/lib/components/toly_ui/popable/drop_selectable_widget.dart new file mode 100644 index 0000000..5e30c67 --- /dev/null +++ b/lib/components/toly_ui/popable/drop_selectable_widget.dart @@ -0,0 +1,198 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +typedef OnDropSelected = void Function(int index); + +class DropSelectableWidget extends StatefulWidget { + final List data; + final OnDropSelected? onDropSelected; + final Color disableColor; + final double iconSize; + final double height; + final double width; + final double fontSize; + + const DropSelectableWidget( + {Key? key, + this.data = const [], + this.onDropSelected, + this.disableColor = Colors.black, + this.iconSize = 24, + this.height = 30, + this.width = 200, + this.fontSize = 14, + }) + : super(key: key); + + @override + _DropSelectableWidgetState createState() => _DropSelectableWidgetState(); +} + +class _DropSelectableWidgetState extends State + with SingleTickerProviderStateMixin { + late FocusNode _node; + bool _focused = false; + late FocusAttachment _nodeAttachment; + OverlayEntry? _overlayEntry; + late AnimationController _ctrl; + late Animation animation; + final LayerLink layerLink = LayerLink(); + + int _selectedIndex = 0; + + @override + void initState() { + super.initState(); + + _ctrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + + animation = Tween(begin: 0, end: pi).animate(_ctrl); + _node = FocusNode() + ..addListener(() { + if (_node.hasFocus != _focused) { + if (!_focused) { + _ctrl.forward(); + _showOverlay(); + } else { + _hideOverlay(); + _ctrl.reverse(); + } + setState(() { + _focused = _node.hasFocus; + }); + } + }); + _nodeAttachment = _node.attach(context); + } + + @override + void dispose() { + _node.dispose(); + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _nodeAttachment.reparent(); + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_focused) { + _node.unfocus(); + } else { + _node.requestFocus(); + } + }, + child: CompositedTransformTarget( + link: layerLink, + child: buildTarget(), + ), + ); + } + + void _showOverlay() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context)?.insert(_overlayEntry!); + } + + void _hideOverlay() { + _overlayEntry?.remove(); + } + + Widget buildTarget() { + return Container( + width: widget.width, + height: widget.height, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: _focused ? Colors.blue : widget.disableColor, + )), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.data.isNotEmpty ? widget.data[_selectedIndex] : "暂无数据",style: TextStyle( + height: 1, + fontSize: widget.fontSize + ),), + AnimatedBuilder( + animation: animation, + builder: (_, child) => Transform.rotate( + angle: animation.value, + child: child, + ), + child: Icon( + Icons.keyboard_arrow_down, + size: widget.iconSize, + ), + ), + ], + ), + ); + } + + OverlayEntry _createOverlayEntry() => OverlayEntry( + builder: (BuildContext context) => UnconstrainedBox( + child: CompositedTransformFollower( + link: layerLink, + targetAnchor: Alignment.bottomCenter, + followerAnchor: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Material( + shape: const RoundedRectangleBorder( + side: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(5))), + elevation: 1, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Container( + height: 200, + // alignment: Alignment.center, + decoration: const BoxDecoration( + color: Color(0xffDAE3FF), + ), + // padding: const EdgeInsets.only(top: 5), + width: widget.width, + child: CupertinoScrollbar( + child: ListView.builder( + padding: EdgeInsets.zero, + // shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: _buildItem), + ), + ), + ), + ), + ), + ), + ), + ); + + Widget _buildItem(BuildContext context, int index) { + return Material( + child: InkWell( + onTap: () { + if (_selectedIndex != index) widget.onDropSelected?.call(index); + _selectedIndex = index; + _overlayEntry?.markNeedsBuild(); + _node.unfocus(); + }, + child: Container( + padding: const EdgeInsets.all(8), + color: index == _selectedIndex + ? Colors.blue.withOpacity(0.2) + : Colors.transparent, + child: Text(widget.data[index],style: TextStyle(fontSize: widget.fontSize),)), + ), + ); + } +} \ No newline at end of file diff --git a/lib/components/toly_ui/toly_ui.dart b/lib/components/toly_ui/toly_ui.dart index 5d49a81..9d9a7f6 100644 --- a/lib/components/toly_ui/toly_ui.dart +++ b/lib/components/toly_ui/toly_ui.dart @@ -1,4 +1,5 @@ export 'navigation/menu_meta.dart'; export 'navigation/toly_breadcrumb.dart'; -export 'navigation/toly_navigation_rail.dart'; \ No newline at end of file +export 'navigation/toly_navigation_rail.dart'; +export 'popable/drop_selectable_widget.dart'; \ No newline at end of file diff --git a/lib/v4/pages/sort/sort_page.dart b/lib/v4/pages/sort/sort_page.dart index 8172cc0..d440071 100644 --- a/lib/v4/pages/sort/sort_page.dart +++ b/lib/v4/pages/sort/sort_page.dart @@ -328,7 +328,6 @@ class _SortPageState extends State { // 从最后一个非叶子节点开始,构建最大堆 for (int i = numbers.length ~/ 2; i >= 0; i--) { await heapify(numbers, numbers.length, i); - streamController.add(numbers); } // 依次取出最大堆的根节点(最大值),并进行堆化 @@ -361,6 +360,7 @@ class _SortPageState extends State { } await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 + streamController.add(numbers); } ///插入排序 @@ -448,7 +448,7 @@ class _SortPageState extends State { ///快速排序 quickSort(int leftIndex, int rightIndex) async { // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 - Future _partition(int left, int right) async { + Future _partition(int left, int right) async{ // 选择中间位置的元素作为基准元素 int p = (left + (right - left) / 2).toInt(); diff --git a/lib/v5/app/navigation/router/app_router_delegate.dart b/lib/v5/app/navigation/router/app_router_delegate.dart index 3e51873..a437843 100644 --- a/lib/v5/app/navigation/router/app_router_delegate.dart +++ b/lib/v5/app/navigation/router/app_router_delegate.dart @@ -7,6 +7,7 @@ import '../../../pages/color/color_detail_page.dart'; import '../../../pages/color/color_page.dart'; import '../../../pages/empty/empty_page.dart'; import '../../../pages/settings/settings_page.dart'; +import '../../../pages/user/user_page.dart'; import '../../../pages/counter/counter_page.dart'; import '../../../pages/sort/sort_page.dart'; import '../transition/fade_transition_page.dart'; @@ -15,6 +16,7 @@ import '../../../pages/color/color_add_page.dart'; const List kDestinationsPaths = [ '/color', '/counter', + '/sort', '/user', '/settings', ]; @@ -29,8 +31,9 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { int? get activeIndex { if(path.startsWith('/color')) return 0; if(path.startsWith('/counter')) return 1; - if(path.startsWith('/user')) return 2; - if(path.startsWith('/settings')) return 3; + if(path.startsWith('/sort')) return 2; + if(path.startsWith('/user')) return 3; + if(path.startsWith('/settings')) return 4; return null; } @@ -95,9 +98,13 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { child = const CounterPage(); } if (path == kDestinationsPaths[2]) { - child = const SortPage(); + child = LayoutBuilder( + builder: (_,cts)=> SortPage(size: Size(cts.maxWidth,cts.maxHeight),)); } if (path == kDestinationsPaths[3]) { + child = const UserPage(); + } + if (path == kDestinationsPaths[4]) { child = const SettingPage(); } return [ diff --git a/lib/v5/app/navigation/views/app_navigation.dart b/lib/v5/app/navigation/views/app_navigation.dart index adcab6c..f03c9e8 100644 --- a/lib/v5/app/navigation/views/app_navigation.dart +++ b/lib/v5/app/navigation/views/app_navigation.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:iroute/v5/pages/sort/provider/sort_config.dart'; +import '../../../pages/sort/sort_setting.dart'; import '../router/app_router_delegate.dart'; import 'app_navigation_rail.dart'; import 'app_top_bar.dart'; @@ -10,6 +12,9 @@ class AppNavigation extends StatelessWidget { Widget build(BuildContext context) { double px1 = 1/View.of(context).devicePixelRatio; return Scaffold( + endDrawer: Drawer( + child: SortSettings(config: sortConfig.value,), + ), body: Row( children: [ const AppNavigationRail(), diff --git a/lib/v5/app/navigation/views/app_navigation_rail.dart b/lib/v5/app/navigation/views/app_navigation_rail.dart index 1ab9714..38aa912 100644 --- a/lib/v5/app/navigation/views/app_navigation_rail.dart +++ b/lib/v5/app/navigation/views/app_navigation_rail.dart @@ -14,6 +14,7 @@ class _AppNavigationRailState extends State { final List deskNavBarMenus = const [ MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined), MenuMeta(label: '计数器', icon: Icons.add_chart), + MenuMeta(label: '排序', icon: Icons.sort), MenuMeta(label: '我的', icon: Icons.person), MenuMeta(label: '设置', icon: Icons.settings), ]; diff --git a/lib/v5/app/navigation/views/app_router_editor.dart b/lib/v5/app/navigation/views/app_router_editor.dart index 10d5701..b100a2a 100644 --- a/lib/v5/app/navigation/views/app_router_editor.dart +++ b/lib/v5/app/navigation/views/app_router_editor.dart @@ -1,6 +1,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; +import 'package:iroute/components/toly_ui/popable/drop_selectable_widget.dart'; +import 'package:iroute/v5/pages/sort/provider/sort_config.dart'; +import '../../../pages/sort/functions.dart'; import '../router/app_router_delegate.dart'; class AppRouterEditor extends StatefulWidget { @@ -36,6 +39,35 @@ class _AppRouterEditorState extends State { @override Widget build(BuildContext context) { + + return Row( + children: [ + DropSelectableWidget( + fontSize: 12, + data: sortNameMap.values.toList(), + iconSize: 20, + height: 30, + width: 200, + disableColor: const Color(0xff1F425F), + onDropSelected: (int index) async { +sortName.value=sortNameMap.keys.toList()[index]; + // curveAnim = CurvedAnimation( + // parent: _ctrl, curve: maps.values.toList()[index]); + // _startAnim(); + }, + ), + const SizedBox(width: 10,), + GestureDetector( + onTap: (){ + Scaffold.of(context).openEndDrawer(); + // showDialog( + // useRootNavigator: false, + // context: context, builder: (ctx)=>AlertDialog()); + }, + child: const Icon(Icons.settings)) + ], + ); + return Stack( alignment: Alignment.centerRight, children: [ diff --git a/lib/v5/app/navigation/views/app_top_bar.dart b/lib/v5/app/navigation/views/app_top_bar.dart index 1b95110..84890eb 100644 --- a/lib/v5/app/navigation/views/app_top_bar.dart +++ b/lib/v5/app/navigation/views/app_top_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; +import '../../../pages/sort/functions.dart'; import '../router/app_router_delegate.dart'; import 'app_router_editor.dart'; @@ -20,7 +21,7 @@ class AppTopBar extends StatelessWidget { child: Row(children: [ const Spacer(), SizedBox( - width: 250, + // width: 200, child: AppRouterEditor( onSubmit: (path) => router.path = path, )), @@ -52,6 +53,7 @@ Map kRouteLabelMap = { '/color/detail': '颜色详情', '/counter': '计数器', '/user': '我的', + '/sort': '可视化排序', '/settings': '系统设置', }; diff --git a/lib/v5/pages/sort/functions.dart b/lib/v5/pages/sort/functions.dart new file mode 100644 index 0000000..6ae1176 --- /dev/null +++ b/lib/v5/pages/sort/functions.dart @@ -0,0 +1,46 @@ +import 'functions/bubble.dart'; +import 'functions/cocktail.dart'; +import 'functions/comb.dart'; +import 'functions/cycle.dart'; +import 'functions/gnome.dart'; +import 'functions/heap.dart'; +import 'functions/insertion.dart'; +import 'functions/merage.dart'; +import 'functions/pigeonHole.dart'; +import 'functions/quick.dart'; +import 'functions/selection.dart'; +import 'functions/shell.dart'; + +typedef SortFunction = Future Function(List src, SortCallback callback); +typedef SortCallback = Future Function(List dist); + +Map sortFunctionMap = { + 'insertion': insertionSort, + 'bubble': bubbleSort, + 'cocktail': cocktailSort, + 'comb': combSort, + 'pigeonHole': pigeonHoleSort, + 'shell': shellSort, + 'selection': selectionSort, + 'gnome': gnomeSort, + 'cycle': cycleSort, + 'heap': heapSort, + 'quick': quickSort, + 'mergeSort': mergeSort, +}; + +Map sortNameMap = { + 'insertion': '插入排序', + 'bubble': '冒泡排序', + 'cocktail': '鸡尾酒排序(双向冒泡排序)', + 'comb': '梳排序', + 'pigeonHole': '鸽巢排序', + 'shell': '希尔排序', + 'selection': '选择排序', + 'gnome': '侏儒排序', + 'cycle': '循环排序', + 'heap': '堆排序', + 'quick': '快速排序', + 'mergeSort': '归并排序', +}; + diff --git a/lib/v5/pages/sort/functions/bubble.dart b/lib/v5/pages/sort/functions/bubble.dart new file mode 100644 index 0000000..3cf3c99 --- /dev/null +++ b/lib/v5/pages/sort/functions/bubble.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///冒泡排序 +Future bubbleSort(List 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); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/cocktail.dart b/lib/v5/pages/sort/functions/cocktail.dart new file mode 100644 index 0000000..8c2d18c --- /dev/null +++ b/lib/v5/pages/sort/functions/cocktail.dart @@ -0,0 +1,52 @@ +import '../functions.dart'; + +///鸡尾酒排序(双向冒泡排序) +Future cocktailSort(List src, SortCallback callback ) async { + bool swapped = true; // 表示是否进行了交换 + int start = 0; // 当前未排序部分的起始位置 + int end = src.length; // 当前未排序部分的结束位置 + + // 开始排序循环,只有当没有进行交换时才会退出循环 + while (swapped == true) { + swapped = false; + + // 从左往右遍历需要排序的部分 + for (int i = start; i < end - 1; ++i) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + + // 如果没有进行交换,则说明已经排好序,退出循环 + if (swapped == false) break; + // 重设为false,准备进行下一轮排序 + swapped = false; + // 将end设置为上一轮排序的最后一个元素的位置 + end = end - 1; + + // 从右往左遍历需要排序的部分 + for (int i = end - 1; i >= start; i--) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + // 将start向右移一位,准备下一轮排序 + start = start + 1; + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/comb.dart b/lib/v5/pages/sort/functions/comb.dart new file mode 100644 index 0000000..821f4a9 --- /dev/null +++ b/lib/v5/pages/sort/functions/comb.dart @@ -0,0 +1,34 @@ +import '../functions.dart'; + +///梳排序(Comb Sort) +Future combSort(List src, SortCallback callback) async{ + int gap = src.length; + + bool swapped = true; + + // 当间隔不为1或存在交换时执行循环 + while (gap != 1 || swapped == true) { + // 通过缩小间隔来逐步将元素归位 + gap = getNextGap(gap); + swapped = false; + for (int i = 0; i < src.length - gap; i++) { + // 如果当前元素大于间隔位置上的元素,则交换它们的位置 + if (src[i] > src[i + gap]) { + int temp = src[i]; + src[i] = src[i + gap]; + src[i + gap] = temp; + swapped = true; + } + + // 实现一个延迟,以便在 UI 上展示排序的动画效果。 + await callback(src); + } + } +} + +int getNextGap(int gap) { + // 根据当前间隔值计算下一个间隔值 + gap = (gap * 10) ~/ 13; + if (gap < 1) return 1; + return gap; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/cycle.dart b/lib/v5/pages/sort/functions/cycle.dart new file mode 100644 index 0000000..4bef6eb --- /dev/null +++ b/lib/v5/pages/sort/functions/cycle.dart @@ -0,0 +1,54 @@ +import '../functions.dart'; + +///循环排序 +Future cycleSort(List src, SortCallback callback) async { + int writes = 0; + for (int cycleStart = 0; cycleStart <= src.length - 2; cycleStart++) { + int item = src[cycleStart]; + int pos = cycleStart; + + // 在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos++; + } + + // 如果当前元素已经在正确位置上,则跳过此次迭代 + if (pos == cycleStart) { + continue; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (pos != cycleStart) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 循环将位于当前位置的元素放置到正确的位置上 + while (pos != cycleStart) { + pos = cycleStart; + // 继续在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos += 1; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (item != src[pos]) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 添加延迟操作以展示排序过程 + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/gnome.dart b/lib/v5/pages/sort/functions/gnome.dart new file mode 100644 index 0000000..5e08fc3 --- /dev/null +++ b/lib/v5/pages/sort/functions/gnome.dart @@ -0,0 +1,22 @@ +import '../functions.dart'; + +///地精排序 (侏儒排序) +Future gnomeSort(List src, SortCallback callback) async { + int index = 0; + while (index < src.length) { + // 当 index 小于数组长度时执行循环 + if (index == 0) index++; + if (src[index] >= src[index - 1]) { + // 如果当前元素大于等于前面的元素,则将 index 加1 + index++; + } else { + // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) + int temp = src[index]; + src[index] = src[index - 1]; + src[index - 1] = temp; + index--; + } + await callback(src); + } + return; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/heap.dart b/lib/v5/pages/sort/functions/heap.dart new file mode 100644 index 0000000..9f5410b --- /dev/null +++ b/lib/v5/pages/sort/functions/heap.dart @@ -0,0 +1,38 @@ +import '../functions.dart'; + +///堆排序 +Future heapSort(List src, SortCallback callback) async { + // 从最后一个非叶子节点开始,构建最大堆 + for (int i = src.length ~/ 2; i >= 0; i--) { + await heapify(src,callback, src.length, i); + } + + // 依次取出最大堆的根节点(最大值),并进行堆化 + for (int i = src.length - 1; i >= 0; i--) { + int temp = src[0]; + src[0] = src[i]; + src[i] = temp; + await heapify(src, callback,i, 0); + } +} + +Future heapify(List src, SortCallback callback, int n, int i) async{ + int largest = i; + int l = 2 * i + 1; // 左子节点索引 + int r = 2 * i + 2; // 右子节点索引 + + // 如果左子节点存在并且大于父节点,则更新最大值索引 + if (l < n && src[l] > src[largest]) largest = l; + + // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 + if (r < n && src[r] > src[largest]) largest = r; + + // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 + if (largest != i) { + int temp = src[i]; + src[i] = src[largest]; + src[largest] = temp; + heapify(src,callback, n, largest); + } + await callback(src); +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/insertion.dart b/lib/v5/pages/sort/functions/insertion.dart new file mode 100644 index 0000000..b1c7814 --- /dev/null +++ b/lib/v5/pages/sort/functions/insertion.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///插入排序 +Future insertionSort(List src, SortCallback callback) async { + for (int i = 1; i < src.length; i++) { + int temp = src[i]; // 将当前元素存储到临时变量 temp 中 + int j = i - 1; // j 表示已排序部分的最后一个元素的索引 + + // 在已排序部分从后往前查找,找到合适位置插入当前元素 + while (j >= 0 && temp < src[j]) { + src[j + 1] = src[j]; // 当前元素比已排序部分的元素小,将元素后移一位 + --j; // 向前遍历 + // 更新排序结果回调 + await callback(src); + } + src[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 + await callback(src); + } +} diff --git a/lib/v5/pages/sort/functions/merage.dart b/lib/v5/pages/sort/functions/merage.dart new file mode 100644 index 0000000..12135b4 --- /dev/null +++ b/lib/v5/pages/sort/functions/merage.dart @@ -0,0 +1,79 @@ +import '../functions.dart'; + +//快速排序 +Future mergeSort(List src, SortCallback callback) async { + await _mergeSort(src,callback,0,src.length-1); +} + +///归并排序 +Future _mergeSort(List src, SortCallback callback,int leftIndex, int rightIndex) async { + // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 + Future merge(int leftIndex, int middleIndex, int rightIndex) async { + // 计算左侧子数组和右侧子数组的大小 + int leftSize = middleIndex - leftIndex + 1; + int rightSize = rightIndex - middleIndex; + + // 创建左侧子数组和右侧子数组 + List leftList = List.generate(leftSize, (index) => 0); + List rightList = List.generate(rightSize, (index) => 0); + + // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 + for (int i = 0; i < leftSize; i++) { + leftList[i] = src[leftIndex + i]; + } + for (int j = 0; j < rightSize; j++) { + rightList[j] = src[middleIndex + j + 1]; + } + + // 初始化游标和索引 + int i = 0, j = 0; + int k = leftIndex; + + // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 + while (i < leftSize && j < rightSize) { + if (leftList[i] <= rightList[j]) { + src[k] = leftList[i]; + i++; + } else { + src[k] = rightList[j]; + j++; + } + + await callback(src); + + k++; + } + + // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 + while (i < leftSize) { + src[k] = leftList[i]; + i++; + k++; + + await callback(src); + } + + while (j < rightSize) { + src[k] = rightList[j]; + j++; + k++; + + await callback(src); + } + } + + // 如果左索引小于右索引,则递归地对数组进行归并排序 + if (leftIndex < rightIndex) { + // 计算中间索引位置 + int middleIndex = (rightIndex + leftIndex) ~/ 2; + + // 分别对左侧子数组和右侧子数组进行归并排序 + await _mergeSort(src,callback,leftIndex, middleIndex); + await _mergeSort(src,callback,middleIndex + 1, rightIndex); + + await callback(src); + + // 合并两个有序子数组 + await merge(leftIndex, middleIndex, rightIndex); + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/oddEven.dart b/lib/v5/pages/sort/functions/oddEven.dart new file mode 100644 index 0000000..bd6f548 --- /dev/null +++ b/lib/v5/pages/sort/functions/oddEven.dart @@ -0,0 +1,37 @@ +import '../functions.dart'; + +///奇偶排序(Odd-Even Sort) +Future oddEvenSort(List src, SortCallback callback) async { + bool isSorted = false; + + while (!isSorted) { + // 当 isSorted 为 false 时执行循环 + isSorted = true; // 先假设数组已经排好序 + + for (int i = 1; i <= src.length - 2; i = i + 2) { + // 对奇数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false + await callback(src); + } + } + + for (int i = 0; i <= src.length - 2; i = i + 2) { + // 对偶数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; + await callback(src); + } + } + } + + return; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/pigeonHole.dart b/lib/v5/pages/sort/functions/pigeonHole.dart new file mode 100644 index 0000000..83bbce1 --- /dev/null +++ b/lib/v5/pages/sort/functions/pigeonHole.dart @@ -0,0 +1,33 @@ +import '../functions.dart'; + +///鸽巢排序 +Future pigeonHoleSort(List src, SortCallback callback ) async{ + int min = src[0]; + int max = src[0]; + int range, i, j, index; + + // 找到数组中的最大值和最小值 + for (int a = 0; a < src.length; a++) { + if (src[a] > max) max = src[a]; + if (src[a] < min) min = src[a]; + } + + // 计算鸽巢的个数 + range = max - min + 1; + List p = List.generate(range, (i) => 0); + + // 将数字分配到各个鸽巢中 + for (i = 0; i < src.length; i++) { + p[src[i] - min]++; + } + + index = 0; + + // 将鸽巢中的数字取出,重新放回到数组中 + for (j = 0; j < range; j++) { + while (p[j]-- > 0) { + src[index++] = j + min; + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/quick.dart b/lib/v5/pages/sort/functions/quick.dart new file mode 100644 index 0000000..25b9685 --- /dev/null +++ b/lib/v5/pages/sort/functions/quick.dart @@ -0,0 +1,65 @@ +import '../functions.dart'; + +//快速排序 +Future quickSort(List src, SortCallback callback) async { + await _quickSort(src,callback,0,src.length-1); +} + +///快速排序 +Future _quickSort(List src, SortCallback callback,int leftIndex,int rightIndex) async { + // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 + Future _partition(int left, int right) async { + // 选择中间位置的元素作为基准元素 + int p = (left + (right - left) / 2).toInt(); + + // 交换基准元素和最右边的元素 + var temp = src[p]; + src[p] = src[right]; + src[right] = temp; + await callback(src); + + // 初始化游标 cursor + int cursor = left; + + // 遍历数组并根据基准元素将元素交换到左侧或右侧 + for (int i = left; i < right; i++) { + if (cf(src[i], src[right]) <= 0) { + // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 + var temp = src[i]; + src[i] = src[cursor]; + src[cursor] = temp; + cursor++; + await callback(src); + } + } + + // 将基准元素放置在游标位置 + temp = src[right]; + src[right] = src[cursor]; + src[cursor] = temp; + + await callback(src); + + return cursor; // 返回基准元素的索引位置 + } + + // 如果左索引小于右索引,则递归地对数组进行快速排序 + if (leftIndex < rightIndex) { + int p = await _partition(leftIndex, rightIndex); + + await _quickSort(src,callback,leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 + + await _quickSort(src,callback, p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 + } +} + +// 比较函数,用于判断两个元素的大小关系 +cf(int a, int b) { + if (a < b) { + return -1; // 若 a 小于 b,则返回 -1 + } else if (a > b) { + return 1; // 若 a 大于 b,则返回 1 + } else { + return 0; // 若 a 等于 b,则返回 0 + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/selection.dart b/lib/v5/pages/sort/functions/selection.dart new file mode 100644 index 0000000..185dae2 --- /dev/null +++ b/lib/v5/pages/sort/functions/selection.dart @@ -0,0 +1,18 @@ +import '../functions.dart'; + +///选择排序 +Future selectionSort(List src, SortCallback callback ) async { + for (int i = 0; i < src.length; i++) { + for (int j = i + 1; j < src.length; j++) { + // 遍历未排序部分,内层循环控制变量 j + if (src[i] > src[j]) { + // 判断当前元素是否比后续元素小 + int temp = src[j]; + // 交换当前元素和后续较小的元素 + src[j] = src[i]; + src[i] = temp; + } + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/functions/shell.dart b/lib/v5/pages/sort/functions/shell.dart new file mode 100644 index 0000000..232f872 --- /dev/null +++ b/lib/v5/pages/sort/functions/shell.dart @@ -0,0 +1,21 @@ +import '../functions.dart'; + +///希尔排序 +Future shellSort(List src, SortCallback callback) async{ + //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 + for (int gap = src.length ~/ 2; gap > 0; gap ~/= 2) { + //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 + for (int i = gap; i < src.length; i += 1) { + //将当前遍历到的元素赋值给它 + int temp = src[i]; + //内部使用一个 for 循环来实现插入排序。 + //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 + int j; + for (j = i; j >= gap && src[j - gap] > temp; j -= gap) { + src[j] = src[j - gap]; + } + src[j] = temp; + await callback(src); + } + } +} \ No newline at end of file diff --git a/lib/v5/pages/sort/provider/sort_config.dart b/lib/v5/pages/sort/provider/sort_config.dart new file mode 100644 index 0000000..33b7523 --- /dev/null +++ b/lib/v5/pages/sort/provider/sort_config.dart @@ -0,0 +1,18 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class SortConfig { + final int count; + final int seed; + final Duration duration; + + SortConfig(this.count, this.duration,this.seed); +} + +final ValueNotifier sortConfig = ValueNotifier( + SortConfig(-1, const Duration(microseconds: 1500),-1), +); + + +final ValueNotifier sortName = ValueNotifier('quick'); diff --git a/lib/v5/pages/sort/sort_page.dart b/lib/v5/pages/sort/sort_page.dart index 8172cc0..6d7fab6 100644 --- a/lib/v5/pages/sort/sort_page.dart +++ b/lib/v5/pages/sort/sort_page.dart @@ -3,8 +3,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'functions.dart'; +import 'provider/sort_config.dart'; + class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); + final Size size; + const SortPage({Key? key, required this.size}) : super(key: key); @override State createState() => _SortPageState(); @@ -16,7 +20,8 @@ class _SortPageState extends State { //订阅流 StreamController> streamController = StreamController(); - String currentSort = 'bubble'; + + String get currentSort => sortName.value; //柱子的数量 -> 生成排序数组的长度 double sampleSize = 0; @@ -27,562 +32,15 @@ class _SortPageState extends State { //是否在排序中 bool isSorting = false; - //排序动画更新的速度 - int speed = 0; - - static int duration = 1500; - - String getTitle() { - switch (currentSort) { - case "bubble": - return "Bubble Sort"; - case "coctail": - return "Coctail Sort"; - case "comb": - return "Comb Sort"; - case "pigeonhole": - return "Pigeonhole Sort"; - case "shell": - return "Shell Sort"; - case "selection": - return "Selection Sort"; - case "cycle": - return "Cycle Sort"; - case "heap": - return "Heap Sort"; - case "insertion": - return "Insertion Sort"; - case "gnome": - return "Gnome Sort"; - case "oddeven": - return "OddEven Sort"; - case "quick": - return "Quick Sort"; - case "merge": - return "Merge Sort"; - } - return ""; - } reset() { isSorted = false; - numbers = []; - for (int i = 0; i < sampleSize; ++i) { - numbers.add(Random().nextInt(500)); - } + setSize(sortConfig.value.count); streamController.add(numbers); } Duration getDuration() { - return Duration(microseconds: duration); - } - - ///动画时间 - changeSpeed() { - if (speed >= 3) { - speed = 0; - duration = 1500; - } else { - speed++; - duration = duration ~/ 2; - } - setState(() {}); - } - - ///冒泡排序 - bubbleSort() async { - //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 - for (int i = 0; i < numbers.length; ++i) { - //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 - for (int j = 0; j < numbers.length - i - 1; ++j) { - //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 - if (numbers[j] > numbers[j + 1]) { - int temp = numbers[j]; - numbers[j] = numbers[j + 1]; - numbers[j + 1] = temp; - } - //实现一个延迟,以便在ui上展示排序的动画效果 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); - } - } - } - - ///鸡尾酒排序(双向冒泡排序) - cocktailSort() async { - bool swapped = true; // 表示是否进行了交换 - int start = 0; // 当前未排序部分的起始位置 - int end = numbers.length; // 当前未排序部分的结束位置 - - // 开始排序循环,只有当没有进行交换时才会退出循环 - while (swapped == true) { - swapped = false; - - // 从左往右遍历需要排序的部分 - for (int i = start; i < end - 1; ++i) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - // 如果没有进行交换,则说明已经排好序,退出循环 - if (swapped == false) break; - // 重设为false,准备进行下一轮排序 - swapped = false; - // 将end设置为上一轮排序的最后一个元素的位置 - end = end - 1; - - // 从右往左遍历需要排序的部分 - for (int i = end - 1; i >= start; i--) { - // 对每两个相邻元素进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果前面的元素大于后面的元素,则交换它们的位置 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - swapped = true; // 进行了交换 - } - - // 实现动画效果,延迟一段时间后更新数组状态 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - // 将start向右移一位,准备下一轮排序 - start = start + 1; - } - } - - ///梳排序(Comb Sort) - combSort() async { - int gap = numbers.length; - - bool swapped = true; - - // 当间隔不为1或存在交换时执行循环 - while (gap != 1 || swapped == true) { - // 通过缩小间隔来逐步将元素归位 - gap = getNextGap(gap); - swapped = false; - for (int i = 0; i < numbers.length - gap; i++) { - // 如果当前元素大于间隔位置上的元素,则交换它们的位置 - if (numbers[i] > numbers[i + gap]) { - int temp = numbers[i]; - numbers[i] = numbers[i + gap]; - numbers[i + gap] = temp; - swapped = true; - } - - // 实现一个延迟,以便在 UI 上展示排序的动画效果。 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - int getNextGap(int gap) { - // 根据当前间隔值计算下一个间隔值 - gap = (gap * 10) ~/ 13; - if (gap < 1) return 1; - return gap; - } - - ///鸽巢排序 - pigeonHole() async { - int min = numbers[0]; - int max = numbers[0]; - int range, i, j, index; - - // 找到数组中的最大值和最小值 - for (int a = 0; a < numbers.length; a++) { - if (numbers[a] > max) max = numbers[a]; - if (numbers[a] < min) min = numbers[a]; - } - - // 计算鸽巢的个数 - range = max - min + 1; - List p = List.generate(range, (i) => 0); - - // 将数字分配到各个鸽巢中 - for (i = 0; i < numbers.length; i++) { - p[numbers[i] - min]++; - } - - index = 0; - - // 将鸽巢中的数字取出,重新放回到数组中 - for (j = 0; j < range; j++) { - while (p[j]-- > 0) { - numbers[index++] = j + min; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///希尔排序 - shellSort() async { - //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 - for (int gap = numbers.length ~/ 2; gap > 0; gap ~/= 2) { - //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 - for (int i = gap; i < numbers.length; i += 1) { - //将当前遍历到的元素赋值给它 - int temp = numbers[i]; - //内部使用一个 for 循环来实现插入排序。 - //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 - int j; - for (j = i; j >= gap && numbers[j - gap] > temp; j -= gap) { - numbers[j] = numbers[j - gap]; - } - numbers[j] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///选择排序 - selectionSort() async { - for (int i = 0; i < numbers.length; i++) { - for (int j = i + 1; j < numbers.length; j++) { - // 遍历未排序部分,内层循环控制变量 j - if (numbers[i] > numbers[j]) { - // 判断当前元素是否比后续元素小 - int temp = numbers[j]; - // 交换当前元素和后续较小的元素 - numbers[j] = numbers[i]; - numbers[i] = temp; - } - - await Future.delayed(getDuration(), () {}); - - streamController.add(numbers); - } - } - } - - ///循环排序 - cycleSort() async { - int writes = 0; - for (int cycleStart = 0; cycleStart <= numbers.length - 2; cycleStart++) { - int item = numbers[cycleStart]; - int pos = cycleStart; - - // 在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos++; - } - - // 如果当前元素已经在正确位置上,则跳过此次迭代 - if (pos == cycleStart) { - continue; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (pos != cycleStart) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 循环将位于当前位置的元素放置到正确的位置上 - while (pos != cycleStart) { - pos = cycleStart; - // 继续在未排序部分中寻找比当前元素小的元素个数 - for (int i = cycleStart + 1; i < numbers.length; i++) { - if (numbers[i] < item) pos += 1; - } - - // 将当前元素放置到正确的位置上,并记录写操作次数 - while (item == numbers[pos]) { - pos += 1; - } - if (item != numbers[pos]) { - int temp = item; - item = numbers[pos]; - numbers[pos] = temp; - writes++; - } - - // 添加延迟操作以展示排序过程 - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - ///堆排序 - heapSort() async { - // 从最后一个非叶子节点开始,构建最大堆 - for (int i = numbers.length ~/ 2; i >= 0; i--) { - await heapify(numbers, numbers.length, i); - streamController.add(numbers); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - for (int i = numbers.length - 1; i >= 0; i--) { - int temp = numbers[0]; - numbers[0] = numbers[i]; - numbers[i] = temp; - await heapify(numbers, i, 0); - streamController.add(numbers); - } - } - - heapify(List arr, int n, int i) async { - int largest = i; - int l = 2 * i + 1; // 左子节点索引 - int r = 2 * i + 2; // 右子节点索引 - - // 如果左子节点存在并且大于父节点,则更新最大值索引 - if (l < n && arr[l] > arr[largest]) largest = l; - - // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 - if (r < n && arr[r] > arr[largest]) largest = r; - - // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 - if (largest != i) { - int temp = numbers[i]; - numbers[i] = numbers[largest]; - numbers[largest] = temp; - heapify(arr, n, largest); - } - - await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 - } - - ///插入排序 - insertionSort() async { - for (int i = 1; i < numbers.length; i++) { - int temp = numbers[i]; // 将当前元素存储到临时变量 temp 中 - int j = i - 1; // j 表示已排序部分的最后一个元素的索引 - - // 在已排序部分从后往前查找,找到合适位置插入当前元素 - while (j >= 0 && temp < numbers[j]) { - numbers[j + 1] = numbers[j]; // 当前元素比已排序部分的元素小,将元素后移一位 - --j; // 向前遍历 - await Future.delayed(getDuration()); - streamController.add(numbers); // 更新排序结果 - } - - numbers[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 - await Future.delayed(getDuration(), () {}); - streamController.add(numbers); // 更新排序结果 - } - } - - ///地精排序 (侏儒排序) - gnomeSort() async { - int index = 0; - - while (index < numbers.length) { - // 当 index 小于数组长度时执行循环 - if (index == 0) index++; - if (numbers[index] >= numbers[index - 1]) { - // 如果当前元素大于等于前面的元素,则将 index 加1 - index++; - } else { - // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) - int temp = numbers[index]; - numbers[index] = numbers[index - 1]; - numbers[index - 1] = temp; - index--; - } - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - return; - } - - ///奇偶排序(Odd-Even Sort) - oddEvenSort() async { - bool isSorted = false; - - while (!isSorted) { - // 当 isSorted 为 false 时执行循环 - isSorted = true; // 先假设数组已经排好序 - - for (int i = 1; i <= numbers.length - 2; i = i + 2) { - // 对奇数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - for (int i = 0; i <= numbers.length - 2; i = i + 2) { - // 对偶数索引位置进行比较 - if (numbers[i] > numbers[i + 1]) { - // 如果当前元素大于后面的元素,则交换它们的值 - int temp = numbers[i]; - numbers[i] = numbers[i + 1]; - numbers[i + 1] = temp; - isSorted = false; - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - } - - return; - } - - ///快速排序 - quickSort(int leftIndex, int rightIndex) async { - // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 - Future _partition(int left, int right) async { - // 选择中间位置的元素作为基准元素 - int p = (left + (right - left) / 2).toInt(); - - // 交换基准元素和最右边的元素 - var temp = numbers[p]; - numbers[p] = numbers[right]; - numbers[right] = temp; - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 初始化游标 cursor - int cursor = left; - - // 遍历数组并根据基准元素将元素交换到左侧或右侧 - for (int i = left; i < right; i++) { - if (cf(numbers[i], numbers[right]) <= 0) { - // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 - var temp = numbers[i]; - numbers[i] = numbers[cursor]; - numbers[cursor] = temp; - cursor++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 将基准元素放置在游标位置 - temp = numbers[right]; - numbers[right] = numbers[cursor]; - numbers[cursor] = temp; - - await Future.delayed(getDuration()); - streamController.add(numbers); - - return cursor; // 返回基准元素的索引位置 - } - - // 如果左索引小于右索引,则递归地对数组进行快速排序 - if (leftIndex < rightIndex) { - int p = await _partition(leftIndex, rightIndex); - - await quickSort(leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 - - await quickSort(p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 - } - } - - // 比较函数,用于判断两个元素的大小关系 - cf(int a, int b) { - if (a < b) { - return -1; // 若 a 小于 b,则返回 -1 - } else if (a > b) { - return 1; // 若 a 大于 b,则返回 1 - } else { - return 0; // 若 a 等于 b,则返回 0 - } - } - - ///归并排序 - mergeSort(int leftIndex, int rightIndex) async { - // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 - Future merge(int leftIndex, int middleIndex, int rightIndex) async { - // 计算左侧子数组和右侧子数组的大小 - int leftSize = middleIndex - leftIndex + 1; - int rightSize = rightIndex - middleIndex; - - // 创建左侧子数组和右侧子数组 - List leftList = List.generate(leftSize, (index) => 0); - List rightList = List.generate(rightSize, (index) => 0); - - // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 - for (int i = 0; i < leftSize; i++) { - leftList[i] = numbers[leftIndex + i]; - } - for (int j = 0; j < rightSize; j++) { - rightList[j] = numbers[middleIndex + j + 1]; - } - - // 初始化游标和索引 - int i = 0, j = 0; - int k = leftIndex; - - // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 - while (i < leftSize && j < rightSize) { - if (leftList[i] <= rightList[j]) { - numbers[k] = leftList[i]; - i++; - } else { - numbers[k] = rightList[j]; - j++; - } - - await Future.delayed(getDuration()); - streamController.add(numbers); - - k++; - } - - // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 - while (i < leftSize) { - numbers[k] = leftList[i]; - i++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - - while (j < rightSize) { - numbers[k] = rightList[j]; - j++; - k++; - - await Future.delayed(getDuration()); - streamController.add(numbers); - } - } - - // 如果左索引小于右索引,则递归地对数组进行归并排序 - if (leftIndex < rightIndex) { - // 计算中间索引位置 - int middleIndex = (rightIndex + leftIndex) ~/ 2; - - // 分别对左侧子数组和右侧子数组进行归并排序 - await mergeSort(leftIndex, middleIndex); - await mergeSort(middleIndex + 1, rightIndex); - - await Future.delayed(getDuration()); - streamController.add(numbers); - - // 合并两个有序子数组 - await merge(leftIndex, middleIndex, rightIndex); - } + return sortConfig.value.duration; } checkAndResetIfSorted() async { @@ -601,50 +59,15 @@ class _SortPageState extends State { Stopwatch stopwatch = Stopwatch()..start(); - switch (currentSort) { - case "bubble": - await bubbleSort(); - break; - case "coctail": - await cocktailSort(); - break; - case "comb": - await combSort(); - break; - case "pigeonhole": - await pigeonHole(); - break; - case "shell": - await shellSort(); - break; - case "selection": - await selectionSort(); - break; - case "cycle": - await cycleSort(); - break; - case "heap": - await heapSort(); - break; - case "insertion": - await insertionSort(); - break; - case "gnome": - await gnomeSort(); - break; - case "oddeven": - await oddEvenSort(); - break; - case "quick": - await quickSort(0, sampleSize.toInt() - 1); - break; - case "merge": - await mergeSort(0, sampleSize.toInt() - 1); - break; + SortFunction? sortFunction = sortFunctionMap[currentSort]; + if(sortFunction!=null){ + await sortFunction(numbers,(arr) async { + await Future.delayed(getDuration()); + streamController.add(arr); + }); } stopwatch.stop(); - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); setState(() { isSorting = false; @@ -652,29 +75,37 @@ class _SortPageState extends State { }); } - setSort(String type) { - setState(() { - currentSort = type; - }); - } - @override void initState() { super.initState(); // reset(); + print(widget.size); + setSize(sortConfig.value.count); + sortConfig.addListener(_onSortConfigChange); + sortName.addListener(reset); } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { + Random random = Random(); + void setSize(int count){ + int s = count; + numbers.clear(); + if(count==-1){ + s = widget.size.width~/2; + } + if(sortConfig.value.seed!=-1){ + random = Random(sortConfig.value.seed); + } + for (int i = 0; i < s; i++) { + //随机往数组中填值 - numbers.add(Random().nextInt(500)); + numbers.add(random.nextInt(widget.size.height.toInt())); + + //numbers.add(Random().nextInt(); } setState(() {}); } + @override void dispose() { streamController.close(); @@ -684,128 +115,67 @@ class _SortPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text( - "当前选择的是:${getTitle()}", - style: const TextStyle(fontSize: 14), - ), - actions: [ - PopupMenuButton( - initialValue: currentSort, - itemBuilder: (ctx) { - return const [ - PopupMenuItem( - value: 'bubble', - child: Text("Bubble Sort — 冒泡排序"), - ), - PopupMenuItem( - value: 'coctail', - child: Text("Coctail Sort — 鸡尾酒排序(双向冒泡排序)"), - ), - PopupMenuItem( - value: 'comb', - child: Text("Comb Sort — 梳排序"), - ), - PopupMenuItem( - value: 'pigeonhole', - child: Text("pigeonhole Sort — 鸽巢排序"), - ), - PopupMenuItem( - value: 'shell', - child: Text("shell Sort — 希尔排序"), - ), - PopupMenuItem( - value: 'selection', - child: Text("Selection Sort — 选择排序"), - ), - PopupMenuItem( - value: 'cycle', - child: Text("CycleSort — 循环排序"), - ), - PopupMenuItem( - value: 'heap', - child: Text("HeapSort — 堆排序"), - ), - PopupMenuItem( - value: 'insertion', - child: Text("InsertionSort — 插入排序"), - ), - PopupMenuItem( - value: 'gnome', - child: Text("GnomeSort — 地精排序 (侏儒排序)"), - ), - PopupMenuItem( - value: 'oddeven', - child: Text("OddEvenSort — 奇偶排序"), - ), - PopupMenuItem( - value: 'quick', - child: Text("QuickSort — 快速排序"), - ), - PopupMenuItem( - value: 'merge', - child: Text("MergeSort — 归并排序"), - ), - ]; - }, - onSelected: (String value) { - reset(); - setSort(value); - }, - ) - ], + floatingActionButton: FloatingActionButton( + child: Icon(Icons.run_circle_outlined), + onPressed: (){ + sort(); + }, ), body: StreamBuilder( initialData: numbers, stream: streamController.stream, builder: (context, snapshot) { List numbers = snapshot.data as List; - int counter = 0; return Row( - children: numbers.map((int num) { - counter++; + children: numbers.asMap().keys.map((int index) { return CustomPaint( painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, + height: widget.size.height, + width: widget.size.width/numbers.length, + value: numbers[index], + index: index, ), ); }).toList(), ); }, ), - bottomNavigationBar: BottomAppBar( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: isSorting - ? null - : () { - reset(); - setSort(currentSort); - }, - child: const Text("重置")), - ElevatedButton( - onPressed: isSorting ? null : sort, child: const Text("开始排序")), - ElevatedButton( - onPressed: isSorting ? null : changeSpeed, - child: Text( - "${speed + 1}x", - style: const TextStyle(fontSize: 20), - ), - ), - ], - ), - ), + // bottomNavigationBar: BottomAppBar( + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: isSorting + // ? null + // : () { + // reset(); + // setSort(currentSort); + // }, + // child: const Text("重置")), + // ElevatedButton( + // onPressed: isSorting ? null : sort, child: const Text("开始排序")), + // ElevatedButton( + // onPressed: isSorting ? null : changeSpeed, + // child: Text( + // "${speed + 1}x", + // style: const TextStyle(fontSize: 20), + // ), + // ), + // ], + // ), + // ), ); } + + void _onSortConfigChange() { + setSize(sortConfig.value.count); + } } class BarPainter extends CustomPainter { //宽度 final double width; + final double height; //高度(数组中对应的值) final int value; @@ -813,28 +183,31 @@ class BarPainter extends CustomPainter { //位置索引 final int index; - BarPainter({required this.width, required this.value, required this.index}); + BarPainter({required this.width, required this.height,required this.value, required this.index}); @override void paint(Canvas canvas, Size size) { Paint paint = Paint(); + // double rate = value/height; + // print(rate); + // paint.color = Colors.blue.withOpacity(rate); if (value < 500 * .10) { paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { + } else if (value < height * .20) { paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { + } else if (value < height * .30) { paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { + } else if (value < height * .40) { paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { + } else if (value < height * .50) { paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { + } else if (value < height * .60) { paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { + } else if (value < height * .70) { paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { + } else if (value < height * .80) { paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { + } else if (value < height * .90) { paint.color = Colors.blue.shade900; } else { paint.color = const Color(0xFF011E51); @@ -844,9 +217,9 @@ class BarPainter extends CustomPainter { paint.strokeCap = StrokeCap.round; canvas.drawLine( - Offset(index * width, 0), + Offset(index * width+width/2, 0), Offset( - index * width, + index * width+width/2, value.ceilToDouble(), ), paint); diff --git a/lib/v5/pages/sort/sort_setting.dart b/lib/v5/pages/sort/sort_setting.dart new file mode 100644 index 0000000..b6c2302 --- /dev/null +++ b/lib/v5/pages/sort/sort_setting.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; + +import 'provider/sort_config.dart'; + +class SortSettings extends StatefulWidget { + + final SortConfig config; + + const SortSettings({super.key,required this.config}); + + @override + State createState() => _SortSettingsState(); +} + +class _SortSettingsState extends State { + + late TextEditingController _count = TextEditingController( + text:sortConfig.value.count.toString() + ); + late TextEditingController _duration = TextEditingController( + text:sortConfig.value.duration.inMicroseconds.toString() + ); + late TextEditingController _seed = TextEditingController( + text:sortConfig.value.seed.toString() + ); + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.white, + leading: BackButton(), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + centerTitle: true, + title: Text('排序算法配置'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Text('数据数量(个数):'), + const SizedBox(width: 20,), + Expanded(child: TextField( + controller: _count, + )), + ], + ), + Row( + children: [ + Text('时间间隔(微秒):'), + const SizedBox(width: 20,), + Expanded(child: TextField( + controller: _duration, + )), + ], + ), + Row( + children: [ + Text('随机种子:'), + const SizedBox(width: 20,), + Expanded(child: TextField( + controller: _seed, + )), + ], + ), + Spacer(), + ElevatedButton(onPressed: (){ + + sortConfig.value = SortConfig(int.parse(_count.text), Duration( + microseconds: int.parse(_duration.text), + ),int.parse(_seed.text)); + + Navigator.of(context).pop(); + }, child: Text('确定设置')) + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index c1a95d4..a70e83a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -269,13 +269,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.6.0" - toly_menu: - dependency: "direct main" - description: - path: "E:\\Projects\\Flutter\\packages\\toly_menu" - relative: false - source: path - version: "0.0.1" url_launcher: dependency: "direct main" description: @@ -373,5 +366,5 @@ packages: source: hosted version: "0.3.7" sdks: - dart: ">=3.1.1 <4.0.0" + dart: ">=3.1.0 <4.0.0" flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index d4f622c..edd2d6c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,8 +43,6 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - toly_menu: - path: E:\Projects\Flutter\packages\toly_menu dev_dependencies: flutter_test: From 9c91d7d7f339ffc4e381a84cd4639499fda5e8eb Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Tue, 24 Oct 2023 13:53:28 +0800 Subject: [PATCH 2/3] v5-old --- .../router/app_router_delegate.dart | 2 +- .../app/navigation/views/app_navigation.dart | 2 +- .../navigation/views/app_router_editor.dart | 2 +- lib/v5/app/unit_app.dart | 32 ++++---- .../sort/{provider => bloc}/sort_config.dart | 5 +- lib/v5/pages/sort/bloc/state.dart | 77 +++++++++++++++++++ lib/v5/pages/sort/sort_setting.dart | 4 +- lib/v5/pages/sort/views/data_painter.dart | 60 +++++++++++++++ lib/v5/pages/sort/{ => views}/sort_page.dart | 28 +++---- 9 files changed, 175 insertions(+), 37 deletions(-) rename lib/v5/pages/sort/{provider => bloc}/sort_config.dart (65%) create mode 100644 lib/v5/pages/sort/bloc/state.dart create mode 100644 lib/v5/pages/sort/views/data_painter.dart rename lib/v5/pages/sort/{ => views}/sort_page.dart (91%) diff --git a/lib/v5/app/navigation/router/app_router_delegate.dart b/lib/v5/app/navigation/router/app_router_delegate.dart index a437843..10cd84c 100644 --- a/lib/v5/app/navigation/router/app_router_delegate.dart +++ b/lib/v5/app/navigation/router/app_router_delegate.dart @@ -9,7 +9,7 @@ import '../../../pages/empty/empty_page.dart'; import '../../../pages/settings/settings_page.dart'; import '../../../pages/user/user_page.dart'; import '../../../pages/counter/counter_page.dart'; -import '../../../pages/sort/sort_page.dart'; +import '../../../pages/sort/views/sort_page.dart'; import '../transition/fade_transition_page.dart'; import '../../../pages/color/color_add_page.dart'; diff --git a/lib/v5/app/navigation/views/app_navigation.dart b/lib/v5/app/navigation/views/app_navigation.dart index f03c9e8..10c84f4 100644 --- a/lib/v5/app/navigation/views/app_navigation.dart +++ b/lib/v5/app/navigation/views/app_navigation.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:iroute/v5/pages/sort/provider/sort_config.dart'; +import 'package:iroute/v5/pages/sort/bloc/sort_config.dart'; import '../../../pages/sort/sort_setting.dart'; import '../router/app_router_delegate.dart'; import 'app_navigation_rail.dart'; diff --git a/lib/v5/app/navigation/views/app_router_editor.dart b/lib/v5/app/navigation/views/app_router_editor.dart index b100a2a..ddbe2f2 100644 --- a/lib/v5/app/navigation/views/app_router_editor.dart +++ b/lib/v5/app/navigation/views/app_router_editor.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; import 'package:iroute/components/toly_ui/popable/drop_selectable_widget.dart'; -import 'package:iroute/v5/pages/sort/provider/sort_config.dart'; +import 'package:iroute/v5/pages/sort/bloc/sort_config.dart'; import '../../../pages/sort/functions.dart'; import '../router/app_router_delegate.dart'; diff --git a/lib/v5/app/unit_app.dart b/lib/v5/app/unit_app.dart index 60d28f8..e8d89a7 100644 --- a/lib/v5/app/unit_app.dart +++ b/lib/v5/app/unit_app.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../pages/sort/bloc/state.dart'; import 'navigation/router/app_router_delegate.dart'; import 'navigation/views/app_navigation.dart'; import 'navigation/views/app_navigation_rail.dart'; @@ -8,20 +9,23 @@ class UnitApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData( - fontFamily: "宋体", - scaffoldBackgroundColor: Colors.white, - appBarTheme: const AppBarTheme( - elevation: 0, - iconTheme: IconThemeData(color: Colors.black), - titleTextStyle: TextStyle( - color: Colors.black, - fontSize: 18, - fontWeight: FontWeight.bold, - ))), - debugShowCheckedModeBanner: false, - home: AppNavigation() + return SortStateScope( + notifier: SortState(), + child: MaterialApp( + theme: ThemeData( + fontFamily: "宋体", + scaffoldBackgroundColor: Colors.white, + appBarTheme: const AppBarTheme( + elevation: 0, + iconTheme: IconThemeData(color: Colors.black), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.bold, + ))), + debugShowCheckedModeBanner: false, + home: AppNavigation() + ), ); } } diff --git a/lib/v5/pages/sort/provider/sort_config.dart b/lib/v5/pages/sort/bloc/sort_config.dart similarity index 65% rename from lib/v5/pages/sort/provider/sort_config.dart rename to lib/v5/pages/sort/bloc/sort_config.dart index 33b7523..2983815 100644 --- a/lib/v5/pages/sort/provider/sort_config.dart +++ b/lib/v5/pages/sort/bloc/sort_config.dart @@ -6,12 +6,13 @@ class SortConfig { final int count; final int seed; final Duration duration; + final String name; - SortConfig(this.count, this.duration,this.seed); + SortConfig(this.count, this.duration,this.seed,this.name); } final ValueNotifier sortConfig = ValueNotifier( - SortConfig(-1, const Duration(microseconds: 1500),-1), + SortConfig(-1, const Duration(microseconds: 1500),-1,'quick'), ); diff --git a/lib/v5/pages/sort/bloc/state.dart b/lib/v5/pages/sort/bloc/state.dart new file mode 100644 index 0000000..8f9664a --- /dev/null +++ b/lib/v5/pages/sort/bloc/state.dart @@ -0,0 +1,77 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import '../functions.dart'; +import 'sort_config.dart'; + +enum SortStatus{ + none, // 未操作 + sorting, // 排序中 + sorted, // 排序完成 +} + +class SortState with ChangeNotifier{ + + SortStatus status = SortStatus.none; + + List data = []; + + SortConfig _config = SortConfig(-1, const Duration(microseconds: 1500),-1,'quick'); + SortConfig get config => _config; + Random random = Random(); + + set config(SortConfig config){ + _config = config; + reset(_zoneSize); + notifyListeners(); + } + + Size _zoneSize = Size.zero; + + void reset(Size zoneSize){ + _zoneSize = zoneSize; + status = SortStatus.sorting; + int count = config.count; + if(count==-1){ + count = zoneSize.width~/2; + } + if(config.seed!=-1){ + random = Random(config.seed); + } + for (int i = 0; i < count; i++) { + //随机往数组中填值 + data.add(random.nextInt(zoneSize.height.toInt())); + } + } + + void sort() async{ + status = SortStatus.sorting; + notifyListeners(); + Stopwatch stopwatch = Stopwatch()..start(); + SortFunction? sortFunction = sortFunctionMap[config.name]; + if(sortFunction!=null){ + await sortFunction(data,(arr) async { + await Future.delayed(config.duration); + data = arr; + notifyListeners(); + }); + } + status = SortStatus.sorted; + notifyListeners(); + stopwatch.stop(); + print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); + } +} + +/// Provides the current [SortState] to descendant widgets in the tree. +class SortStateScope extends InheritedNotifier { + const SortStateScope({ + required super.notifier, + required super.child, + super.key, + }); + + static SortState of(BuildContext context) => + context.dependOnInheritedWidgetOfExactType()!.notifier!; +} \ No newline at end of file diff --git a/lib/v5/pages/sort/sort_setting.dart b/lib/v5/pages/sort/sort_setting.dart index b6c2302..df06c1a 100644 --- a/lib/v5/pages/sort/sort_setting.dart +++ b/lib/v5/pages/sort/sort_setting.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'provider/sort_config.dart'; +import 'bloc/sort_config.dart'; class SortSettings extends StatefulWidget { @@ -79,7 +79,7 @@ class _SortSettingsState extends State { sortConfig.value = SortConfig(int.parse(_count.text), Duration( microseconds: int.parse(_duration.text), - ),int.parse(_seed.text)); + ),int.parse(_seed.text),sortConfig.value.name); Navigator.of(context).pop(); }, child: Text('确定设置')) diff --git a/lib/v5/pages/sort/views/data_painter.dart b/lib/v5/pages/sort/views/data_painter.dart new file mode 100644 index 0000000..881d967 --- /dev/null +++ b/lib/v5/pages/sort/views/data_painter.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + + + +class DataPainter extends CustomPainter{ + + final List data; + + DataPainter({required this.data}); + + @override + void paint(Canvas canvas, Size size) { + double itemWidth = size.width/data.length; + double height = size.height; + + Paint paint = Paint(); + paint.strokeWidth = itemWidth; + paint.strokeCap = StrokeCap.round; + + for(int i=0;i { if(count==-1){ s = widget.size.width~/2; } + if(sortConfig.value.seed!=-1){ random = Random(sortConfig.value.seed); } - for (int i = 0; i < s; i++) { + for (int i = 0; i < s; i++) { //随机往数组中填值 numbers.add(random.nextInt(widget.size.height.toInt())); - - //numbers.add(Random().nextInt(); } setState(() {}); } @@ -114,6 +115,9 @@ class _SortPageState extends State { @override Widget build(BuildContext context) { + + List numbers = SortStateScope.of(context).data; + return Scaffold( floatingActionButton: FloatingActionButton( child: Icon(Icons.run_circle_outlined), @@ -126,17 +130,9 @@ class _SortPageState extends State { stream: streamController.stream, builder: (context, snapshot) { List numbers = snapshot.data as List; - return Row( - children: numbers.asMap().keys.map((int index) { - return CustomPaint( - painter: BarPainter( - height: widget.size.height, - width: widget.size.width/numbers.length, - value: numbers[index], - index: index, - ), - ); - }).toList(), + return CustomPaint( + size: widget.size, + painter: DataPainter(data: numbers), ); }, ), From 39c38ca97501ad66f6ac0f13d14307f6e6a067e5 Mon Sep 17 00:00:00 2001 From: toly <1981462002@qq.com> Date: Tue, 24 Oct 2023 15:52:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?v5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../popable/drop_selectable_widget.dart | 1 + .../router/app_router_delegate.dart | 3 +- .../app/navigation/views/app_navigation.dart | 3 +- .../navigation/views/app_router_editor.dart | 85 +++---- lib/v5/app/unit_app.dart | 2 +- lib/v5/pages/sort/bloc/sort_config.dart | 19 -- lib/v5/pages/sort/provider/sort_config.dart | 31 +++ .../pages/sort/{bloc => provider}/state.dart | 23 +- lib/v5/pages/sort/sort_setting.dart | 74 +++--- lib/v5/pages/sort/views/data_painter.dart | 20 +- lib/v5/pages/sort/views/sort_bar.dart | 46 ++++ lib/v5/pages/sort/views/sort_button.dart | 40 ++++ lib/v5/pages/sort/views/sort_page.dart | 220 +----------------- windows/runner/main.cpp | 2 +- 14 files changed, 231 insertions(+), 338 deletions(-) delete mode 100644 lib/v5/pages/sort/bloc/sort_config.dart create mode 100644 lib/v5/pages/sort/provider/sort_config.dart rename lib/v5/pages/sort/{bloc => provider}/state.dart (80%) create mode 100644 lib/v5/pages/sort/views/sort_bar.dart create mode 100644 lib/v5/pages/sort/views/sort_button.dart diff --git a/lib/components/toly_ui/popable/drop_selectable_widget.dart b/lib/components/toly_ui/popable/drop_selectable_widget.dart index 5e30c67..da8e4e8 100644 --- a/lib/components/toly_ui/popable/drop_selectable_widget.dart +++ b/lib/components/toly_ui/popable/drop_selectable_widget.dart @@ -113,6 +113,7 @@ class _DropSelectableWidgetState extends State decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), border: Border.all( + width: _focused ?1:1/View.of(context).devicePixelRatio, color: _focused ? Colors.blue : widget.disableColor, )), child: Row( diff --git a/lib/v5/app/navigation/router/app_router_delegate.dart b/lib/v5/app/navigation/router/app_router_delegate.dart index 10cd84c..6910126 100644 --- a/lib/v5/app/navigation/router/app_router_delegate.dart +++ b/lib/v5/app/navigation/router/app_router_delegate.dart @@ -98,8 +98,7 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { child = const CounterPage(); } if (path == kDestinationsPaths[2]) { - child = LayoutBuilder( - builder: (_,cts)=> SortPage(size: Size(cts.maxWidth,cts.maxHeight),)); + child = SortPage(); } if (path == kDestinationsPaths[3]) { child = const UserPage(); diff --git a/lib/v5/app/navigation/views/app_navigation.dart b/lib/v5/app/navigation/views/app_navigation.dart index 10c84f4..e1d002d 100644 --- a/lib/v5/app/navigation/views/app_navigation.dart +++ b/lib/v5/app/navigation/views/app_navigation.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:iroute/v5/pages/sort/bloc/sort_config.dart'; import '../../../pages/sort/sort_setting.dart'; import '../router/app_router_delegate.dart'; import 'app_navigation_rail.dart'; @@ -13,7 +12,7 @@ class AppNavigation extends StatelessWidget { double px1 = 1/View.of(context).devicePixelRatio; return Scaffold( endDrawer: Drawer( - child: SortSettings(config: sortConfig.value,), + child: SortSettings(), ), body: Row( children: [ diff --git a/lib/v5/app/navigation/views/app_router_editor.dart b/lib/v5/app/navigation/views/app_router_editor.dart index ddbe2f2..9173948 100644 --- a/lib/v5/app/navigation/views/app_router_editor.dart +++ b/lib/v5/app/navigation/views/app_router_editor.dart @@ -2,8 +2,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; import 'package:iroute/components/toly_ui/popable/drop_selectable_widget.dart'; -import 'package:iroute/v5/pages/sort/bloc/sort_config.dart'; import '../../../pages/sort/functions.dart'; +import '../../../pages/sort/views/sort_bar.dart'; +import '../../../pages/sort/views/sort_button.dart'; import '../router/app_router_delegate.dart'; class AppRouterEditor extends StatefulWidget { @@ -28,6 +29,9 @@ class _AppRouterEditorState extends State { void _onRouteChange() { _controller.text=router.path; + setState(() { + + }); } @override @@ -39,58 +43,37 @@ class _AppRouterEditorState extends State { @override Widget build(BuildContext context) { + print(router.path); + if(router.path=='/sort'){ + return SortBar(); + } - return Row( - children: [ - DropSelectableWidget( - fontSize: 12, - data: sortNameMap.values.toList(), - iconSize: 20, - height: 30, - width: 200, - disableColor: const Color(0xff1F425F), - onDropSelected: (int index) async { -sortName.value=sortNameMap.keys.toList()[index]; - // curveAnim = CurvedAnimation( - // parent: _ctrl, curve: maps.values.toList()[index]); - // _startAnim(); - }, - ), - const SizedBox(width: 10,), - GestureDetector( - onTap: (){ - Scaffold.of(context).openEndDrawer(); - // showDialog( - // useRootNavigator: false, - // context: context, builder: (ctx)=>AlertDialog()); - }, - child: const Icon(Icons.settings)) - ], - ); - - return Stack( - alignment: Alignment.centerRight, - children: [ - SizedBox( - child: CupertinoTextField( - controller: _controller, - style: TextStyle(fontSize: 14), - padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32), - placeholder: '输入路由地址导航', - onSubmitted: widget.onSubmit, - decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)), + return SizedBox( + width: 250, + child: Stack( + alignment: Alignment.centerRight, + children: [ + SizedBox( + child: CupertinoTextField( + controller: _controller, + style: TextStyle(fontSize: 14), + padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32), + placeholder: '输入路由地址导航', + onSubmitted: widget.onSubmit, + decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)), + ), ), - ), - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: HoverIconButton( - icon: Icons.directions_outlined, - defaultColor: Color(0xff68696B), - onPressed:()=>widget.onSubmit?.call(_controller.text), - size: 20 - ), - ) - ], + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: HoverIconButton( + icon: Icons.directions_outlined, + defaultColor: Color(0xff68696B), + onPressed:()=>widget.onSubmit?.call(_controller.text), + size: 20 + ), + ) + ], + ), ); } } diff --git a/lib/v5/app/unit_app.dart b/lib/v5/app/unit_app.dart index e8d89a7..36e67ab 100644 --- a/lib/v5/app/unit_app.dart +++ b/lib/v5/app/unit_app.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import '../pages/sort/bloc/state.dart'; +import '../pages/sort/provider/state.dart'; import 'navigation/router/app_router_delegate.dart'; import 'navigation/views/app_navigation.dart'; import 'navigation/views/app_navigation_rail.dart'; diff --git a/lib/v5/pages/sort/bloc/sort_config.dart b/lib/v5/pages/sort/bloc/sort_config.dart deleted file mode 100644 index 2983815..0000000 --- a/lib/v5/pages/sort/bloc/sort_config.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -class SortConfig { - final int count; - final int seed; - final Duration duration; - final String name; - - SortConfig(this.count, this.duration,this.seed,this.name); -} - -final ValueNotifier sortConfig = ValueNotifier( - SortConfig(-1, const Duration(microseconds: 1500),-1,'quick'), -); - - -final ValueNotifier sortName = ValueNotifier('quick'); diff --git a/lib/v5/pages/sort/provider/sort_config.dart b/lib/v5/pages/sort/provider/sort_config.dart new file mode 100644 index 0000000..2ad50ed --- /dev/null +++ b/lib/v5/pages/sort/provider/sort_config.dart @@ -0,0 +1,31 @@ +import 'dart:ui'; + +import 'package:flutter/material.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, + ); +} + diff --git a/lib/v5/pages/sort/bloc/state.dart b/lib/v5/pages/sort/provider/state.dart similarity index 80% rename from lib/v5/pages/sort/bloc/state.dart rename to lib/v5/pages/sort/provider/state.dart index 8f9664a..e60e208 100644 --- a/lib/v5/pages/sort/bloc/state.dart +++ b/lib/v5/pages/sort/provider/state.dart @@ -13,35 +13,37 @@ enum SortStatus{ class SortState with ChangeNotifier{ + SortState(){ + reset(); + } + SortStatus status = SortStatus.none; List data = []; + List stepData = []; - SortConfig _config = SortConfig(-1, const Duration(microseconds: 1500),-1,'quick'); + SortConfig _config = SortConfig(); SortConfig get config => _config; Random random = Random(); set config(SortConfig config){ _config = config; - reset(_zoneSize); + reset(); notifyListeners(); } - Size _zoneSize = Size.zero; - void reset(Size zoneSize){ - _zoneSize = zoneSize; - status = SortStatus.sorting; + void reset(){ + data.clear(); + status = SortStatus.none; + notifyListeners(); int count = config.count; - if(count==-1){ - count = zoneSize.width~/2; - } if(config.seed!=-1){ random = Random(config.seed); } for (int i = 0; i < count; i++) { //随机往数组中填值 - data.add(random.nextInt(zoneSize.height.toInt())); + data.add(random.nextInt(1000)); } } @@ -53,7 +55,6 @@ class SortState with ChangeNotifier{ if(sortFunction!=null){ await sortFunction(data,(arr) async { await Future.delayed(config.duration); - data = arr; notifyListeners(); }); } diff --git a/lib/v5/pages/sort/sort_setting.dart b/lib/v5/pages/sort/sort_setting.dart index df06c1a..3b61da8 100644 --- a/lib/v5/pages/sort/sort_setting.dart +++ b/lib/v5/pages/sort/sort_setting.dart @@ -1,34 +1,38 @@ import 'package:flutter/material.dart'; -import 'bloc/sort_config.dart'; +import 'provider/sort_config.dart'; +import 'provider/state.dart'; class SortSettings extends StatefulWidget { - final SortConfig config; - - const SortSettings({super.key,required this.config}); + const SortSettings({super.key,}); @override State createState() => _SortSettingsState(); } class _SortSettingsState extends State { + late TextEditingController _count = + TextEditingController(); + late TextEditingController _duration = TextEditingController(); + late TextEditingController _seed = + TextEditingController(); - late TextEditingController _count = TextEditingController( - text:sortConfig.value.count.toString() - ); - late TextEditingController _duration = TextEditingController( - text:sortConfig.value.duration.inMicroseconds.toString() - ); - late TextEditingController _seed = TextEditingController( - text:sortConfig.value.seed.toString() - ); @override void initState() { - // TODO: implement initState super.initState(); } + @override + void didChangeDependencies() { + print('========_SortSettingsState#didChangeDependencies============='); + super.didChangeDependencies(); + SortState state = SortStateScope.of(context); + _count.text = state.config.count.toString(); + _duration.text = state.config.duration.inMicroseconds.toString(); + _seed.text = state.config.seed.toString(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -50,8 +54,11 @@ class _SortSettingsState extends State { Row( children: [ Text('数据数量(个数):'), - const SizedBox(width: 20,), - Expanded(child: TextField( + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( controller: _count, )), ], @@ -59,8 +66,11 @@ class _SortSettingsState extends State { Row( children: [ Text('时间间隔(微秒):'), - const SizedBox(width: 20,), - Expanded(child: TextField( + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( controller: _duration, )), ], @@ -68,21 +78,29 @@ class _SortSettingsState extends State { Row( children: [ Text('随机种子:'), - const SizedBox(width: 20,), - Expanded(child: TextField( + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( controller: _seed, )), ], ), Spacer(), - ElevatedButton(onPressed: (){ - - sortConfig.value = SortConfig(int.parse(_count.text), Duration( - microseconds: int.parse(_duration.text), - ),int.parse(_seed.text),sortConfig.value.name); - - Navigator.of(context).pop(); - }, child: Text('确定设置')) + ElevatedButton( + onPressed: () { + SortState state = SortStateScope.of(context); + state.config =state.config.copyWith( + count: int.parse(_count.text), + duration: Duration( + microseconds: int.parse(_duration.text), + ), + seed: int.parse(_seed.text) + ); + Navigator.of(context).pop(); + }, + child: Text('确定设置')) ], ), ), diff --git a/lib/v5/pages/sort/views/data_painter.dart b/lib/v5/pages/sort/views/data_painter.dart index 881d967..d23e2f5 100644 --- a/lib/v5/pages/sort/views/data_painter.dart +++ b/lib/v5/pages/sort/views/data_painter.dart @@ -19,23 +19,23 @@ class DataPainter extends CustomPainter{ for(int i=0;iAlertDialog()); + }, + child: const Icon(Icons.settings)) + ], + ); + } +} diff --git a/lib/v5/pages/sort/views/sort_button.dart b/lib/v5/pages/sort/views/sort_button.dart new file mode 100644 index 0000000..ec2b30a --- /dev/null +++ b/lib/v5/pages/sort/views/sort_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +import '../provider/state.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.not_started_outlined; + color = Colors.green; + action = state.sort; + break; + case SortStatus.sorting: + icon = Icons.stop_circle_outlined; + 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, + ), + ); + } +} diff --git a/lib/v5/pages/sort/views/sort_page.dart b/lib/v5/pages/sort/views/sort_page.dart index ab554b3..01224c3 100644 --- a/lib/v5/pages/sort/views/sort_page.dart +++ b/lib/v5/pages/sort/views/sort_page.dart @@ -1,228 +1,22 @@ -import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; -import '../bloc/state.dart'; -import '../functions.dart'; -import '../bloc/sort_config.dart'; +import '../provider/state.dart'; import 'data_painter.dart'; -class SortPage extends StatefulWidget { - final Size size; - const SortPage({Key? key, required this.size}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - - String get currentSort => sortName.value; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - bool isSorting = false; - - - reset() { - isSorted = false; - setSize(sortConfig.value.count); - streamController.add(numbers); - } - - Duration getDuration() { - return sortConfig.value.duration; - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - Stopwatch stopwatch = Stopwatch()..start(); - - SortFunction? sortFunction = sortFunctionMap[currentSort]; - if(sortFunction!=null){ - await sortFunction(numbers,(arr) async { - await Future.delayed(getDuration()); - streamController.add(arr); - }); - } - - stopwatch.stop(); - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - print(widget.size); - setSize(sortConfig.value.count); - sortConfig.addListener(_onSortConfigChange); - sortName.addListener(reset); - } - - Random random = Random(); - void setSize(int count){ - int s = count; - numbers.clear(); - if(count==-1){ - s = widget.size.width~/2; - } - - if(sortConfig.value.seed!=-1){ - random = Random(sortConfig.value.seed); - } - - for (int i = 0; i < s; i++) { - //随机往数组中填值 - numbers.add(random.nextInt(widget.size.height.toInt())); - } - setState(() {}); - } - - - @override - void dispose() { - streamController.close(); - super.dispose(); - } +class SortPage extends StatelessWidget{ @override Widget build(BuildContext context) { - - List numbers = SortStateScope.of(context).data; + SortState state = SortStateScope.of(context); + List numbers = state.data; return Scaffold( - floatingActionButton: FloatingActionButton( - child: Icon(Icons.run_circle_outlined), - onPressed: (){ - sort(); - }, + body: CustomPaint( + painter: DataPainter(data: numbers), + child: ConstrainedBox(constraints: BoxConstraints.expand()), ), - body: StreamBuilder( - initialData: numbers, - stream: streamController.stream, - builder: (context, snapshot) { - List numbers = snapshot.data as List; - return CustomPaint( - size: widget.size, - painter: DataPainter(data: numbers), - ); - }, - ), - // bottomNavigationBar: BottomAppBar( - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceAround, - // children: [ - // ElevatedButton( - // onPressed: isSorting - // ? null - // : () { - // reset(); - // setSort(currentSort); - // }, - // child: const Text("重置")), - // ElevatedButton( - // onPressed: isSorting ? null : sort, child: const Text("开始排序")), - // ElevatedButton( - // onPressed: isSorting ? null : changeSpeed, - // child: Text( - // "${speed + 1}x", - // style: const TextStyle(fontSize: 20), - // ), - // ), - // ], - // ), - // ), ); } - - void _onSortConfigChange() { - setSize(sortConfig.value.count); - } } -class BarPainter extends CustomPainter { - //宽度 - final double width; - final double height; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.height,required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - // double rate = value/height; - // print(rate); - // paint.color = Colors.blue.withOpacity(rate); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < height * .20) { - paint.color = Colors.blue.shade200; - } else if (value < height * .30) { - paint.color = Colors.blue.shade300; - } else if (value < height * .40) { - paint.color = Colors.blue.shade400; - } else if (value < height * .50) { - paint.color = Colors.blue.shade500; - } else if (value < height * .60) { - paint.color = Colors.blue.shade600; - } else if (value < height * .70) { - paint.color = Colors.blue.shade700; - } else if (value < height * .80) { - paint.color = Colors.blue.shade800; - } else if (value < height * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width+width/2, 0), - Offset( - index * width+width/2, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index c92ca90..ae006fd 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -26,7 +26,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); - Win32Window::Size size(640, 360); + Win32Window::Size size(1600, 1200); if (!window.Create(L"iroute", origin, size)) { return EXIT_FAILURE; }