This commit is contained in:
toly
2023-11-08 09:35:29 +08:00
parent 88cd6fb3b4
commit 8fb4bf57d6
78 changed files with 4344 additions and 544 deletions

View File

@@ -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<void> Function(List<int> src, SortCallback callback);
typedef SortCallback = Future<void> Function(List<int> dist);
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,
'mergeSort': mergeSort,
};
Map<String, String> sortNameMap = {
'insertion': '插入排序',
'bubble': '冒泡排序',
'cocktail': '鸡尾酒排序(双向冒泡排序)',
'comb': '梳排序',
'pigeonHole': '鸽巢排序',
'shell': '希尔排序',
'selection': '选择排序',
'gnome': '侏儒排序',
'cycle': '循环排序',
'heap': '堆排序',
'quick': '快速排序',
'mergeSort': '归并排序',
};

View File

@@ -0,0 +1,19 @@
import '../functions.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);
}
}
}

View File

@@ -0,0 +1,52 @@
import '../functions.dart';
///鸡尾酒排序(双向冒泡排序)
Future<void> cocktailSort(List<int> 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;
}
}

View File

@@ -0,0 +1,34 @@
import '../functions.dart';
///梳排序Comb Sort
Future<void> combSort(List<int> 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;
}

View File

@@ -0,0 +1,54 @@
import '../functions.dart';
///循环排序
Future<void> cycleSort(List<int> 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);
}
}
}

View File

@@ -0,0 +1,22 @@
import '../functions.dart';
///地精排序 (侏儒排序)
Future<void> gnomeSort(List<int> 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;
}

View File

@@ -0,0 +1,38 @@
import '../functions.dart';
///堆排序
Future<void> heapSort(List<int> 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<void> heapify(List<int> 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);
}

View File

@@ -0,0 +1,19 @@
import '../functions.dart';
///插入排序
Future<void> insertionSort(List<int> 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);
}
}

View File

@@ -0,0 +1,79 @@
import '../functions.dart';
//快速排序
Future<void> mergeSort(List<int> src, SortCallback callback) async {
await _mergeSort(src,callback,0,src.length-1);
}
///归并排序
Future<void> _mergeSort(List<int> src, SortCallback callback,int leftIndex, int rightIndex) async {
// 定义一个名为 merge 的异步函数,用于合并两个有序子数组
Future<void> 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);
}
}

View File

@@ -0,0 +1,37 @@
import '../functions.dart';
///奇偶排序(Odd-Even Sort)
Future<void> oddEvenSort(List<int> 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;
}

View File

@@ -0,0 +1,33 @@
import '../functions.dart';
///鸽巢排序
Future<void> pigeonHoleSort(List<int> 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<int> 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);
}
}
}

View File

@@ -0,0 +1,65 @@
import '../functions.dart';
//快速排序
Future<void> quickSort(List<int> src, SortCallback callback) async {
await _quickSort(src,callback,0,src.length-1);
}
///快速排序
Future<void> _quickSort(List<int> src, SortCallback callback,int leftIndex,int rightIndex) async {
// 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置
Future<int> _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
}
}

View File

@@ -0,0 +1,18 @@
import '../functions.dart';
///选择排序
Future<void> selectionSort(List<int> 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);
}
}
}

View File

@@ -0,0 +1,21 @@
import '../functions.dart';
///希尔排序
Future<void> shellSort(List<int> 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);
}
}
}

View File

@@ -0,0 +1,35 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class SortConfig {
final int count;
final int seed;
final Duration duration;
final String name;
final int colorIndex;
SortConfig({
this.count = 100,
this.duration = const Duration(microseconds: 1500),
this.seed = -1,
this.colorIndex = 0,
this.name = 'insertion',
});
SortConfig copyWith({
int? count,
int? seed,
int? colorIndex,
Duration? duration,
String? name,
}) =>
SortConfig(
count:count??this.count,
seed:seed??this.seed,
duration:duration??this.duration,
name:name??this.name,
colorIndex:colorIndex??this.colorIndex,
);
}

View File

@@ -0,0 +1,101 @@
import 'dart:math';
import 'package:flutter/material.dart';
import '../functions.dart';
import 'sort_config.dart';
enum SortStatus{
none, // 未操作
sorting, // 排序中
sorted, // 排序完成
}
List<MaterialColor> kColorSupport = [
Colors.blue,
Colors.lightBlue,
Colors.cyan,
Colors.red,
Colors.pink,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.indigo,
Colors.purple,
Colors.deepPurple,
];
class SortState with ChangeNotifier{
SortState(){
reset();
}
SortStatus status = SortStatus.none;
List<int> data = [];
List<int> stepData = [];
SortConfig _config = SortConfig();
SortConfig get config => _config;
Random random = Random();
set config(SortConfig config){
_config = config;
reset();
notifyListeners();
}
void selectName(String name){
if(name==config.name) return;
config = config.copyWith(name: name);
}
void selectColor(int colorIndex){
if(colorIndex==config.colorIndex) return;
config = config.copyWith(colorIndex: colorIndex);
}
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();
Stopwatch stopwatch = Stopwatch()..start();
SortFunction? sortFunction = sortFunctionMap[config.name];
if(sortFunction!=null){
await sortFunction(data,(arr) async {
await Future.delayed(config.duration);
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<SortState> {
const SortStateScope({
required super.notifier,
required super.child,
super.key,
});
static SortState of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import '../../functions.dart';
import '../../provider/state.dart';
class CodePage extends StatelessWidget {
const CodePage({super.key});
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
leading: BackButton(),
titleTextStyle: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.bold,
),
centerTitle: true,
title: Text(sortNameMap[state.config.name]!+'代码实现'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text('代码'*1000),
));
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class DataPainter extends CustomPainter{
final List<int> data;
final MaterialColor color;
DataPainter( {required this.data,required this.color,});
@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<data.length;i++){
int value = data[i];
if (value < 1000 * .10) {
paint.color = color.shade50;
} else if (value < 1000 * .20) {
paint.color = color.shade100;
} else if (value < 1000 * .30) {
paint.color = color.shade200;
} else if (value < 1000 * .40) {
paint.color = color.shade300;
} else if (value < 1000 * .50) {
paint.color = color.shade400;
} else if (value < 1000 * .60) {
paint.color = color.shade500;
} else if (value < 1000 * .70) {
paint.color = color.shade600;
} else if (value < 1000 * .80) {
paint.color = color.shade700;
} else if (value < 1000 * .90) {
paint.color = color.shade800;
} else {
paint.color = color.shade900;
}
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;
}
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import '../../provider/state.dart';
import 'data_painter.dart';
class SortPlayer extends StatelessWidget {
const SortPlayer({super.key});
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
List<int> numbers = state.data;
MaterialColor color = kColorSupport[state.config.colorIndex];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: CustomPaint(
painter: DataPainter(data: numbers,color: color),
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
),
);
}
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
class ColorPicker extends StatelessWidget {
final List<MaterialColor> colors;
final ValueChanged<int> onSelected;
final int activeIndex;
const ColorPicker({
super.key,
required this.colors,
required this.activeIndex,
required this.onSelected,
});
@override
Widget build(BuildContext context) {
return Wrap(
children: colors
.asMap()
.keys
.map((int index) => MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: ()=>onSelected(index),
child: Container(
width: 32,
height: 32,
color: colors[index],
child: activeIndex == index
? const Icon(
Icons.check,
color: Colors.white,
)
: null,
),
),
))
.toList(),
);
}
}

View File

@@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import '../../provider/state.dart';
import 'color_picker.dart';
class SortSettings extends StatefulWidget {
const SortSettings({super.key,});
@override
State<SortSettings> createState() => _SortSettingsState();
}
class _SortSettingsState extends State<SortSettings> {
final TextEditingController _count = TextEditingController();
final TextEditingController _duration = TextEditingController();
final TextEditingController _seed = TextEditingController();
int _colorIndex = 0;
@override
void initState() {
super.initState();
}
@override
void 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();
_colorIndex = state.config.colorIndex;
}
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
leading: Align(
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: (){
Navigator.of(context).pop();
},
child: Container(
width: 28,
height: 28,
margin: EdgeInsets.only(right: 8,left: 8),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color(0xffE3E5E7),
borderRadius: BorderRadius.circular(6)
),
child: Icon(Icons.arrow_back_ios_new,size: 18,)),
),
),
),
// leading: BackButton(),
actions: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
splashRadius: 20,
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),
colorIndex: _colorIndex
);
Navigator.of(context).pop();
}, icon: Icon(Icons.check)),
)],
iconTheme: IconThemeData(color: Colors.black),
titleTextStyle: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.bold,
),
centerTitle: true,
title: Text('排序算法配置'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.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,
)),
],
),
const SizedBox(height: 20,),
Row(
children: [
Text('选择颜色:'),
const SizedBox(
width: 20,
),
Expanded(
child: ColorPicker(
colors: kColorSupport,
onSelected: (index){
setState(() {
_colorIndex = index;
});
},
activeIndex: _colorIndex,
),),
],
),
Spacer(),
// 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('确定设置'))
],
),
),
);
}
}

View File

@@ -0,0 +1,57 @@
import 'package:flutter/cupertino.dart';
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;
String text = '';
Color color;
switch (state.status) {
case SortStatus.none:
icon = Icons.not_started_outlined;
color = Colors.green;
action = state.sort;
text = '点击启动';
break;
case SortStatus.sorting:
icon = Icons.stop_circle_outlined;
color = Colors.grey;
action = null;
text = '排序中...';
break;
case SortStatus.sorted:
icon = CupertinoIcons.repeat;
color = Colors.black;
action = state.reset;
text = '点击重置';
break;
}
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: action,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Icon(
icon,
color: color,
size: 18,
),
const SizedBox(width: 4,),
Text(text,style: TextStyle(fontSize: 12,fontWeight: FontWeight.bold,color: color),),
],
),
),
);
}
}

View File

@@ -0,0 +1,230 @@
import 'dart:ffi';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../player/sort_player.dart';
import '../../../../app/navigation/router/app_router_delegate.dart';
import '../settings/sort_setting.dart';
import 'sort_button.dart';
import '../../functions.dart';
import '../../provider/state.dart';
class SortPage extends StatefulWidget {
const SortPage({super.key});
@override
State<SortPage> createState() => _SortPageState();
}
class _SortPageState extends State<SortPage> {
@override
Widget build(BuildContext context) {
return Material(
child: Row(
children: [
SizedBox(
width: 220,
child: SortRailPanel(),
),
VerticalDivider(
width: 1,
),
Expanded(
child: SortNavigatorScope(),
)
],
),
);
}
}
class SortRailPanel extends StatelessWidget {
const SortRailPanel({super.key});
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
const SortButton(),
const Spacer(),
const MouseRegion(
cursor: SystemMouseCursors.click,
child: Icon(
CupertinoIcons.chevron_left_slash_chevron_right,
size: 18,
),
),
const SizedBox(
width: 8,
),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
router.changePath('/sort/settings');
},
child: const Icon(
CupertinoIcons.settings,
size: 18,
)),
),
],
),
),
const Divider(
height: 1,
),
Expanded(
child: SortSelectorPanel(
active: state.config.name,
options: sortNameMap.values.toList(),
onSelected: (name) {
state.selectName(name);
router.changePath('/sort');
},
),
),
],
);
}
}
class SortNavigatorScope extends StatefulWidget {
const SortNavigatorScope({super.key});
@override
State<SortNavigatorScope> createState() => _SortNavigatorScopeState();
}
class _SortNavigatorScopeState extends State<SortNavigatorScope> {
@override
void initState() {
router.addListener(_update);
super.initState();
}
@override
void dispose() {
router.removeListener(_update);
super.dispose();
}
@override
Widget build(BuildContext context) {
String path = router.path;
List<Page> pages = buildPagesByPath(context, path);
return Navigator(
onPopPage: _onPopPage,
pages: pages,
);
}
bool _onPopPage(Route<dynamic> route, result) {
return route.didPop(result);
}
List<Page> buildPagesByPath(BuildContext context, String path) {
if (path == '/sort/settings') {
return [
const MaterialPage(key: ValueKey('/sort/player'), child: SortPlayer()),
const MaterialPage(
key: ValueKey('/sort/settings'),
child: SortSettings(),
),
];
}
return [
const MaterialPage(
key: ValueKey('/sort/player'),
child: SortPlayer(),
)
];
}
void _update() {
setState(() {});
}
}
class SortSelectorPanel extends StatelessWidget {
final String active;
final ValueChanged<String> onSelected;
final List<String> options;
const SortSelectorPanel(
{super.key,
required this.active,
required this.options,
required this.onSelected});
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 8),
itemExtent: 46,
itemCount: sortNameMap.length,
itemBuilder: _buildByIndex,
);
}
Widget? _buildByIndex(BuildContext context, int index) {
String key = sortNameMap.keys.toList()[index];
bool selected = sortNameMap.keys.toList()[index] == active;
return SortItemTile(
selected: selected,
onTap: () => onSelected(key),
title: options[index],
);
}
}
class SortItemTile extends StatefulWidget {
final String title;
final VoidCallback onTap;
final bool selected;
const SortItemTile(
{super.key,
required this.title,
required this.selected,
required this.onTap});
@override
State<SortItemTile> createState() => _SortItemTileState();
}
class _SortItemTileState extends State<SortItemTile> {
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: widget.onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: widget.selected ? const Color(0xffE6F0FF) : null),
padding: const EdgeInsets.only(left: 12),
alignment: Alignment.centerLeft,
child: Text(
widget.title,
style: TextStyle(
fontSize: 14,
fontWeight: widget.selected ? FontWeight.bold : null),
),
),
),
),
);
}
}