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

@@ -6,17 +6,27 @@ import 'route_history_manager.dart';
import 'routes.dart';
import 'views/not_find_view.dart';
AppRouterDelegate router = AppRouterDelegate();
AppRouterDelegate router = AppRouterDelegate(
initial: IRouteConfig(uri: Uri.parse('/color')),
);
class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
String _path = '/color';
String get path => _path;
/// 核心数据,路由配置数据列表
final List<IRouteConfig> _configs = [];
String get path => current.uri.path;
IRouteConfig get current => _configs.last;
final IRoutePageBuilder? notFindPageBuilder;
AppRouterDelegate({this.notFindPageBuilder}) {
_historyManager.recode(IRouteConfig(uri: Uri.parse(path)));
AppRouterDelegate({
this.notFindPageBuilder,
required IRouteConfig initial,
}) {
_configs.add(initial);
_historyManager.recode(initial);
}
Page _defaultNotFindPageBuilder(_, __) => const MaterialPage(
@@ -45,48 +55,60 @@ class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
notifyListeners();
}
// final List<IRouteConfig> _pathStack = [];
bool get canPop => _configs.where((e) => e.routeStyle==RouteStyle.push).isNotEmpty;
final Map<String, Completer<dynamic>> _completerMap = {};
final Map<String, dynamic> _pathExtraMap = {};
final List<String> keepAlivePath = [];
FutureOr<dynamic> changeRoute(IRouteConfig config) {
String value = config.uri.path;
if (_path == value) null;
if (current == config) null;
_handleChangeStyle(config);
if (config.forResult) {
_completerMap[value] = Completer();
}
if (config.keepAlive) {
if (keepAlivePath.contains(value)) {
keepAlivePath.remove(value);
}
keepAlivePath.add(value);
}
if (config.extra != null) {
_pathExtraMap[value] = config.extra;
}
if (config.recordHistory) {
_historyManager.recode(config);
}
_path = value;
notifyListeners();
if (config.forResult) {
return _completerMap[value]!.future;
}
}
void _handleChangeStyle(IRouteConfig config){
switch (config.routeStyle) {
case RouteStyle.push:
if (_configs.contains(config)) {
_configs.remove(config);
}
_configs.add(config);
break;
case RouteStyle.replace:
List<IRouteConfig> liveRoutes = _configs.where((e) => e.keepAlive&&e!=config).toList();
_configs.clear();
_configs.addAll([...liveRoutes,config]);
break;
}
}
FutureOr<dynamic> changePath(
String value, {
bool forResult = false,
Object? extra,
bool keepAlive = false,
bool recordHistory = true,
RouteStyle style = RouteStyle.replace,
}) {
return changeRoute(IRouteConfig(
uri: Uri.parse(value),
forResult: forResult,
extra: extra,
routeStyle: style,
keepAlive: keepAlive,
recordHistory: recordHistory,
));
@@ -96,46 +118,47 @@ class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
Widget build(BuildContext context) {
return Navigator(
onPopPage: _onPopPage,
pages: _buildPages(context, path),
pages: _buildPages(context, _configs),
);
}
List<Page> _buildPages(BuildContext context, String path) {
List<Page> _buildPages(BuildContext context, List<IRouteConfig> configs) {
IRouteConfig top = configs.last;
List<IRouteConfig> bottoms = _configs.sublist(0,_configs.length-1).toList();
List<Page> pages = [];
List<Page> topPages = _buildPageByPathFromTree(context, path);
if (keepAlivePath.isNotEmpty) {
for (String alivePath in keepAlivePath) {
if (alivePath != path) {
pages.addAll(_buildPageByPathFromTree(context, alivePath));
}
}
/// 去除和 topPages 中重复的界面
pages.removeWhere(
(element) => topPages.map((e) => e.key).contains(element.key));
}
List<Page> topPages = _buildPageByPathFromTree(context, top);
pages = _buildLivePageByPathList(context, bottoms, top, topPages);
pages.addAll(topPages);
return pages;
}
List<Page> _buildPageByPathFromTree(BuildContext context, String path) {
List<Page> _buildLivePageByPathList(
BuildContext context,
List<IRouteConfig> paths,
IRouteConfig curConfig,
List<Page> curPages,
) {
List<Page> pages = [];
if (paths.isNotEmpty) {
for (IRouteConfig path in paths) {
if (path != curConfig) {
pages.addAll(_buildPageByPathFromTree(context, path));
}
}
/// 去除和 curPages 中重复的界面
pages.removeWhere((page) => curPages.map((e) => e.key).contains(page.key));
}
return pages;
}
List<Page> _buildPageByPathFromTree(
BuildContext context, IRouteConfig config) {
List<Page> result = [];
List<IRouteNode> iRoutes = rootRoute.find(path);
List<IRouteNode> iRoutes = rootRoute.find(config.path);
if (iRoutes.isNotEmpty) {
for (int i = 0; i < iRoutes.length; i++) {
IRouteNode iroute = iRoutes[i];
String path = iroute.path;
Object? extra = _pathExtraMap[path];
bool keepAlive = keepAlivePath.contains(path);
bool forResult = _completerMap.containsKey(path);
IRouteConfig config = IRouteConfig(
uri: Uri.parse(path),
extra: extra,
keepAlive: keepAlive,
forResult: forResult,
);
config = config.copyWith(path: iroute.path);
Page? page;
if (iroute is NotFindNode) {
page = (notFindPageBuilder ?? _defaultNotFindPageBuilder)(context, config);
@@ -145,6 +168,9 @@ class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
if (page != null) {
result.add(page);
}
if(iroute is CellIRoute){
break;
}
}
}
return result;
@@ -156,12 +182,29 @@ class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
return true;
}
void backStack() {
if (_configs.isNotEmpty) {
_configs.removeLast();
if (_configs.isNotEmpty) {
changeRoute(_configs.last);
} else {
changeRoute(current);
}
}
}
bool _onPopPage(Route route, result) {
if (_completerMap.containsKey(path)) {
_completerMap[path]?.complete(result);
_completerMap.remove(path);
}
changePath(backPath(path), recordHistory: false);
if (_configs.isNotEmpty) {
_configs.removeLast();
notifyListeners();
} else {
changePath(backPath(path), recordHistory: false);
}
return route.didPop(result);
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'iroute_config.dart';
typedef IRoutePageBuilder = Page? Function(
@@ -24,7 +23,9 @@ abstract class IRouteNode {
Page? createPage(BuildContext context, IRouteConfig config);
List<IRouteNode> find(String input,) {
List<IRouteNode> find(
String input,
) {
return findNodes(this, Uri.parse(input), 0, '/', []);
}
@@ -41,15 +42,16 @@ abstract class IRouteNode {
}
String target = parts[deep];
if (node.children.isNotEmpty) {
target = prefix + target;
List<IRouteNode> nodes = node.children.where((e) => e.path == target).toList();
target = prefix + target;
List<IRouteNode> nodes =
node.children.where((e) => e.path == target).toList();
bool match = nodes.isNotEmpty;
if (match) {
IRouteNode matched = nodes.first;
result.add(matched);
String nextPrefix = '${matched.path}/';
findNodes(matched, uri, ++deep, nextPrefix, result);
}else{
} else {
result.add(NotFindNode(path: target));
return result;
}
@@ -92,8 +94,8 @@ class IRoute extends IRouteNode {
}
/// 未知路由
class NotFindNode extends IRouteNode{
NotFindNode({required super.path, super.children= const[]});
class NotFindNode extends IRouteNode {
NotFindNode({required super.path, super.children = const []});
@override
Page? createPage(BuildContext context, IRouteConfig config) {
@@ -101,15 +103,14 @@ class NotFindNode extends IRouteNode{
}
}
class CellIRouter extends IRoute {
final CellBuilder cellBuilder;
CellIRouter(
{required super.path,
super.pageBuilder,
super.children,
required this.cellBuilder});
class CellIRoute extends IRoute {
const CellIRoute({
required super.path,
super.pageBuilder,
super.children,
super.widget,
});
}
typedef CellBuilder = Widget Function(BuildContext context, Widget child);
typedef CellBuilder = Widget Function(BuildContext context,IRouteConfig config, CellIRoute cell);

View File

@@ -1,10 +1,17 @@
import 'package:flutter/material.dart';
enum RouteStyle{
push,
replace,
}
class IRouteConfig {
final Object? extra;
final bool forResult;
final Uri uri;
final bool keepAlive;
final RouteStyle routeStyle;
final bool recordHistory;
const IRouteConfig({
@@ -12,6 +19,7 @@ class IRouteConfig {
required this.uri,
this.forResult = false,
this.keepAlive = false,
this.routeStyle = RouteStyle.replace,
this.recordHistory = false,
});
@@ -22,14 +30,42 @@ class IRouteConfig {
bool? forResult,
bool? keepAlive,
bool? recordHistory,
String? path,
}) =>
IRouteConfig(
extra: extra ?? this.extra,
forResult: forResult ?? this.forResult,
keepAlive: keepAlive ?? this.keepAlive,
recordHistory: recordHistory ?? this.recordHistory,
uri: uri,
uri: path!=null?Uri.parse(path):uri,
);
ValueKey get pageKey => ValueKey(path);
ValueKey get pageKey => ValueKey(hashCode);
@override
String toString() {
return 'IRouteConfig{extra: $extra, forResult: $forResult, uri: $uri, keepAlive: $keepAlive, routeStyle: $routeStyle, recordHistory: $recordHistory}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is IRouteConfig &&
runtimeType == other.runtimeType &&
extra == other.extra &&
forResult == other.forResult &&
uri == other.uri &&
keepAlive == other.keepAlive &&
routeStyle == other.routeStyle &&
recordHistory == other.recordHistory;
@override
int get hashCode =>
extra.hashCode ^
forResult.hashCode ^
uri.hashCode ^
keepAlive.hashCode ^
routeStyle.hashCode ^
recordHistory.hashCode;
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import '../../../pages/sort/views/player/sort_player.dart';
import 'iroute_config.dart';
import 'iroute.dart';
import '../../../pages/color/color_add_page.dart';
@@ -7,8 +8,8 @@ import '../../../pages/color/color_page.dart';
import '../../../pages/counter/counter_page.dart';
import '../../../pages/user/user_page.dart';
import '../../../pages/settings/settings_page.dart';
import '../../../pages/sort/views/sort_page.dart';
import '../../../pages/sort/views/sort_page/sort_page.dart';
import '../../../pages/sort/views/settings/sort_setting.dart';
IRoute rootRoute = const IRoute(
path: 'root',
@@ -22,7 +23,20 @@ IRoute rootRoute = const IRoute(
],
),
IRoute(path: '/counter', widget: CounterPage()),
IRoute(path: '/sort', widget: SortPage()),
CellIRoute(
path: '/sort',
widget: SortPage(),
children: [
IRoute(
path: '/sort/settings',
widget: SortSettings(),
),
IRoute(
path: '/sort/player',
widget: SortPlayer(),
),
],
),
IRoute(path: '/user', widget: UserPage()),
IRoute(path: '/settings', widget: SettingPage()),
],

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import '../app_router_delegate.dart';
class RouteBackIndicator extends StatefulWidget {
const RouteBackIndicator({super.key});
@override
State<RouteBackIndicator> createState() => _RouteBackIndicatorState();
}
class _RouteBackIndicatorState extends State<RouteBackIndicator> {
@override
void initState() {
super.initState();
router.addListener(_onChange);
}
@override
void dispose() {
router.removeListener(_onChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
if(router.canPop){
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: router.backStack,
child: Container(
width: 26,
height: 26,
margin: EdgeInsets.only(right: 8),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Color(0xffE3E5E7),
borderRadius: BorderRadius.circular(6)
),
child: Icon(Icons.arrow_back_ios_new,size: 14,)),
),
);
}
return SizedBox();
}
void _onChange() {
setState(() {
});
}
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../router/app_router_delegate.dart';
import '../router/iroute_config.dart';
class AppNavigationRail extends StatefulWidget {
const AppNavigationRail({super.key});
@@ -10,13 +11,12 @@ class AppNavigationRail extends StatefulWidget {
}
class _AppNavigationRailState extends State<AppNavigationRail> {
final List<MenuMeta> deskNavBarMenus = const [
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined,path: '/color'),
MenuMeta(label: '计数器', icon: Icons.add_chart,path: '/counter'),
MenuMeta(label: '排序', icon: Icons.sort,path: '/sort'),
MenuMeta(label: '我的', icon: Icons.person,path: '/user'),
MenuMeta(label: '设置', icon: Icons.settings,path: '/settings'),
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined, path: '/color'),
MenuMeta(label: '计数器', icon: Icons.add_chart, path: '/counter'),
MenuMeta(label: '排序', icon: Icons.sort, path: '/sort'),
MenuMeta(label: '我的', icon: Icons.person, path: '/user'),
MenuMeta(label: '设置', icon: Icons.settings, path: '/settings'),
];
@override
@@ -42,7 +42,10 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
),
tail: Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Text('V0.0.7',style: TextStyle(color: Colors.white,fontSize: 12),),
child: Text(
'V0.0.8',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
backgroundColor: const Color(0xff3975c6),
onDestinationSelected: _onDestinationSelected,
@@ -53,21 +56,26 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
RegExp _segReg = RegExp(r'/\w+');
int? get activeIndex{
int? get activeIndex {
String path = router.path;
RegExpMatch? match = _segReg.firstMatch(path);
if(match==null) return null;
if (match == null) return null;
String? target = match.group(0);
int index = deskNavBarMenus.indexWhere((menu) => menu.path==target);
if(index==-1) return null;
int index = deskNavBarMenus.indexWhere((menu) => menu.path == target);
if (index == -1) return null;
return index;
}
}
void _onDestinationSelected(int index) {
String path = deskNavBarMenus[index].path!;
if(index==1){
router.changePath(path,keepAlive: true);
}else{
if (index == 1) {
router.changePath(path, keepAlive: true);
return;
}
if (index == 4) {
router.changePath(path, style: RouteStyle.push);
return;
} else {
router.changePath(path);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../../router/app_router_delegate.dart';
import '../../router/views/route_back_indicator.dart';
import 'app_router_editor.dart';
import 'history_view_icon.dart';
import 'route_history_button.dart';
@@ -17,6 +18,7 @@ class AppTopBar extends StatelessWidget {
child: Row(
children: [
const SizedBox(width: 16),
const RouteBackIndicator(),
const RouterIndicator(),
Expanded(
child: Row(children: [
@@ -58,6 +60,7 @@ Map<String, String> kRouteLabelMap = {
'/color/detail': '颜色详情',
'/counter': '计数器',
'/sort': '可视化排序算法',
'/sort/settings': '排序配置',
'/user': '我的',
'/settings': '系统设置',
};

View File

@@ -7,17 +7,20 @@ class SortConfig {
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,
}) =>
@@ -26,6 +29,7 @@ class SortConfig {
seed:seed??this.seed,
duration:duration??this.duration,
name:name??this.name,
colorIndex:colorIndex??this.colorIndex,
);
}

View File

@@ -1,6 +1,6 @@
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../functions.dart';
import 'sort_config.dart';
@@ -11,6 +11,20 @@ enum SortStatus{
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(){
@@ -37,6 +51,11 @@ class SortState with ChangeNotifier{
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;

View File

@@ -5,8 +5,9 @@ import 'package:flutter/material.dart';
class DataPainter extends CustomPainter{
final List<int> data;
final MaterialColor color;
DataPainter({required this.data});
DataPainter( {required this.data,required this.color,});
@override
void paint(Canvas canvas, Size size) {
@@ -17,28 +18,29 @@ class DataPainter extends CustomPainter{
paint.strokeWidth = itemWidth;
paint.strokeCap = StrokeCap.round;
for(int i=0;i<data.length;i++){
int value = data[i];
if (value < 1000 * .10) {
paint.color = Colors.blue.shade100;
paint.color = color.shade50;
} else if (value < 1000 * .20) {
paint.color = Colors.blue.shade200;
paint.color = color.shade100;
} else if (value < 1000 * .30) {
paint.color = Colors.blue.shade300;
paint.color = color.shade200;
} else if (value < 1000 * .40) {
paint.color = Colors.blue.shade400;
paint.color = color.shade300;
} else if (value < 1000 * .50) {
paint.color = Colors.blue.shade500;
paint.color = color.shade400;
} else if (value < 1000 * .60) {
paint.color = Colors.blue.shade600;
paint.color = color.shade500;
} else if (value < 1000 * .70) {
paint.color = Colors.blue.shade700;
paint.color = color.shade600;
} else if (value < 1000 * .80) {
paint.color = Colors.blue.shade800;
paint.color = color.shade700;
} else if (value < 1000 * .90) {
paint.color = Colors.blue.shade900;
paint.color = color.shade800;
} else {
paint.color = const Color(0xFF011E51);
paint.color = color.shade900;
}
canvas.drawLine(
Offset(i * itemWidth+itemWidth/2, 0),

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

@@ -1,48 +0,0 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../provider/state.dart';
import '../functions.dart';
import 'sort_button.dart';
class SortBar extends StatelessWidget {
const SortBar({super.key});
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
return Row(
children: [
const SortButton(),
const SizedBox(width: 10,),
DropSelectableWidget(
value: sortNameMap[state.config.name]!,
fontSize: 12,
data: sortNameMap.values.toList(),
iconSize: 20,
height: 28,
width: 200,
disableColor: const Color(0xff1F425F),
onDropSelected: (int index) async {
SortState state = SortStateScope.of(context);
state.config =state.config.copyWith(
name: 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))
],
);
}
}

View File

@@ -1,185 +0,0 @@
import 'dart:ffi';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'sort_setting.dart';
import 'code_page/code_page.dart';
import 'sort_button.dart';
import '../functions.dart';
import '../provider/state.dart';
import 'data_painter.dart';
class SortPage extends StatelessWidget {
const SortPage({super.key});
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
List<int> numbers = state.data;
return Scaffold(
body: Row(
children: [
SizedBox(
width: 220,
child: Column(
children: [
Container(
// color: Color(0xffF4F4F4),
padding: EdgeInsets.symmetric(horizontal: 12,vertical: 8),
child: Row(
children: [
SortButton(),
Spacer(),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: (){
(key.currentState as NavigatorState).push(MaterialPageRoute(builder: (_)=>CodePage()));
},
child: const Icon(CupertinoIcons.chevron_left_slash_chevron_right,size: 18,)),
),
SizedBox(width: 8,),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: (){
(key.currentState as NavigatorState).push(MaterialPageRoute(builder: (_)=>SortSettings()));
},
child: Icon(CupertinoIcons.settings,size: 18,)),
),
],
),
),
Divider(height: 1,),
Expanded(
child: SortSelectorPanel(
active: state.config.name,
options: sortNameMap.values.toList(),
onSelected: state.selectName,
),
),
],
),
),
VerticalDivider(width: 1,),
Expanded(
child: NavigatorScope(),
)
],
),
);
}
void _onSelected(String value) {
}
}
final GlobalKey key = GlobalKey();
class NavigatorScope extends StatefulWidget {
const NavigatorScope({super.key});
@override
State<NavigatorScope> createState() => _NavigatorScopeState();
}
class _NavigatorScopeState extends State<NavigatorScope> {
@override
Widget build(BuildContext context) {
SortState state = SortStateScope.of(context);
List<int> numbers = state.data;
return Navigator(
onPopPage: _onPopPage,
key: key,
pages: [
MaterialPage(child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: CustomPaint(
painter: DataPainter(data: numbers),
child: ConstrainedBox(constraints: BoxConstraints.expand()),
),
))
],
);
}
bool _onPopPage(Route<dynamic> route, result) {
return route.didPop(result);
}
}
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: 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?Color(0xffE6F0FF):null
),
padding: EdgeInsets.only(left: 12),
alignment: Alignment.centerLeft,
child: Text(
widget.title,
style: TextStyle(fontSize: 14,
fontWeight: widget.selected?FontWeight.bold:null
),
),
),
),
),
);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../provider/state.dart';
import '../../provider/state.dart';
class SortButton extends StatelessWidget {
const SortButton({super.key});
@@ -49,7 +49,6 @@ class SortButton extends StatelessWidget {
),
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),
),
),
),
),
);
}
}

View File

@@ -1,109 +0,0 @@
import 'package:flutter/material.dart';
import '../provider/sort_config.dart';
import '../provider/state.dart';
class SortSettings extends StatefulWidget {
const SortSettings({super.key,});
@override
State<SortSettings> createState() => _SortSettingsState();
}
class _SortSettingsState extends State<SortSettings> {
late TextEditingController _count =
TextEditingController();
late TextEditingController _duration = TextEditingController();
late TextEditingController _seed =
TextEditingController();
@override
void 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(
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: () {
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('确定设置'))
],
),
),
);
}
}