diff --git a/lib/components/toly_ui/button/hover_icon_button.dart b/lib/components/toly_ui/button/hover_icon_button.dart index 6db116e..33373f9 100644 --- a/lib/components/toly_ui/button/hover_icon_button.dart +++ b/lib/components/toly_ui/button/hover_icon_button.dart @@ -16,7 +16,7 @@ class HoverIconButton extends StatefulWidget { this.hoverColor, this.size = 24, this.defaultColor, - this.cursor = SystemMouseCursors.cell, + this.cursor = SystemMouseCursors.click, }); @override diff --git a/lib/components/toly_ui/navigation/menu_meta.dart b/lib/components/toly_ui/navigation/menu_meta.dart index 22c7dec..eb5fca7 100644 --- a/lib/components/toly_ui/navigation/menu_meta.dart +++ b/lib/components/toly_ui/navigation/menu_meta.dart @@ -4,6 +4,8 @@ class MenuMeta { // 标签 final String label; + final String? path; + // 图标数据 final IconData icon; @@ -12,6 +14,7 @@ class MenuMeta { const MenuMeta({ required this.label, + this.path, required this.icon, this.color, }); diff --git a/lib/main.dart b/lib/main.dart index 5cd4489..4d91ffb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; -import 'v7/app.dart'; +import 'v8/app.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/v5_/app/navigation/router/app_router_delegate.dart b/lib/v5_/app/navigation/router/app_router_delegate.dart deleted file mode 100644 index 222f5fe..0000000 --- a/lib/v5_/app/navigation/router/app_router_delegate.dart +++ /dev/null @@ -1,237 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/material.dart'; - -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 '../transition/fade_transition_page.dart'; -import '../../../pages/color/color_add_page.dart'; - -const List kDestinationsPaths = [ - '/color', - '/counter', - '/user', - '/settings', -]; - -AppRouterDelegate router = AppRouterDelegate(); - -class AppRouterDelegate extends RouterDelegate with ChangeNotifier { - - AppRouterDelegate({String initial = '/'}){ - // changePath('/color',keepAlive: true); - } - - String _path = '/color'; - - String get path => _path; - - 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; - return null; - } - - final Map> _completerMap = {}; - - Completer? completer; - - final Map> _alivePageMap = {}; - - void setPathKeepLive(String value){ - _alivePageMap[value] = _buildPageByPath(value); - path = value; - } - - final Map _pathExtraMap = {}; - - FutureOr changePath(String value,{bool forResult=false,Object? extra,bool keepAlive = false}){ - if(forResult){ - _completerMap[value] = Completer(); - } - if(keepAlive){ - _alivePageMap[value] = _buildPageByPath(value); - } - if(extra!=null){ - _pathExtraMap[value] = extra; - } - path = value; - if(forResult){ - return _completerMap[value]!.future; - } - } - - - set path(String value) { - if (_path == value) return; - _path = value; - notifyListeners(); - } - - @override - Widget build(BuildContext context) { - List pages = []; - if(_alivePageMap.containsKey(path)){ - for (String key in _alivePageMap.keys) { - if(path!=key){ - pages.addAll(_alivePageMap[key]!); - } - } - pages.addAll(_alivePageMap[path]!) ; - }else{ - for (var element in _alivePageMap.values) { - pages.addAll(element); - } - List currentPages = _buildPageByPath(path); - pages.removeWhere((element) { - return currentPages.map((e) => e.key).contains(element.key); - }); - // 移除 pages 中和 currentPages 具有相同key 的元素 - pages.addAll(_buildPageByPath(path)); - } - return Navigator( - onPopPage: _onPopPage, - pages: pages, - ); - } - - List _buildPageByPath(String path) { - Widget? child; - if(path.startsWith('/color')){ - return buildColorPages(path); - } - if (path == kDestinationsPaths[1]) { - child = const CounterPage(); - } - if (path == kDestinationsPaths[2]) { - child = const UserPage(); - } - if (path == kDestinationsPaths[3]) { - child = const SettingPage(); - } - return [ - FadeTransitionPage( - key: ValueKey(path), - child: child ?? const EmptyPage(), - ) - ]; - } - - List buildColorPages(String path){ - List result = []; - Uri uri = Uri.parse(path); - for (String segment in uri.pathSegments) { - if(segment == 'color'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color'), - child:ColorPage(), - )); - } - if(segment =='detail'){ - final Map queryParams = uri.queryParameters; - String? selectedColor = queryParams['color']; - - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: color), - )); - }else{ - Color? selectedColor = _pathExtraMap[path]; - if (selectedColor != null) { - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: selectedColor), - )); - _pathExtraMap.remove(path); - } - } - } - if(segment == 'add'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color/add'), - child:ColorAddPage(), - )); - } - - } - return result; - } - - @override - Future popRoute() async { - print('=======popRoute========='); - return true; - } - - bool _onPopPage(Route route, result) { - if(_completerMap.containsKey(path)){ - _completerMap[path]?.complete(result); - _completerMap.remove(path); - } - - path = backPath(path); - return route.didPop(result); - } - - String backPath(String path){ - Uri uri = Uri.parse(path); - if(uri.pathSegments.length==1) return path; - List parts = List.of(uri.pathSegments)..removeLast(); - return '/${parts.join('/')}'; - } - - @override - Future setNewRoutePath(configuration) async {} -} - - - -// class AppRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { -// -// List _value = ['/']; -// -// -// List get value => _value; -// -// set value(List value){ -// _value = value; -// notifyListeners(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Navigator( -// onPopPage: _onPopPage, -// pages: _value.map((e) => _pageMap[e]!).toList(), -// ); -// } -// -// final Map _pageMap = const { -// '/': MaterialPage(child: HomePage()), -// 'a': MaterialPage(child: PageA()), -// 'b': MaterialPage(child: PageB()), -// 'c': MaterialPage(child: PageC()), -// }; -// -// bool _onPopPage(Route route, result) { -// _value = List.of(_value)..removeLast(); -// notifyListeners(); -// return route.didPop(result); -// } -// -// @override -// GlobalKey? navigatorKey = GlobalKey(); -// -// @override -// Future setNewRoutePath(String configuration) async{ -// } -// } diff --git a/lib/v5_/app/navigation/router/iroute.dart b/lib/v5_/app/navigation/router/iroute.dart deleted file mode 100644 index ef2160d..0000000 --- a/lib/v5_/app/navigation/router/iroute.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../../pages/color/color_add_page.dart'; -import '../../../pages/color/color_detail_page.dart'; -import '../../../pages/color/color_page.dart'; -import '../transition/fade_transition_page.dart'; - -class IRoute { - final String path; - final IRoutePageBuilder builder; - final List children; - - const IRoute({ - required this.path, - this.children = const [], - required this.builder, - }); - - @override - String toString() { - return 'IRoute{path: $path, children: $children}'; - } - - List list() { - return []; - } -} - -typedef IRoutePageBuilder = Page? Function( - BuildContext context, IRouteData data); - -class IRouteData { - final Object? extra; - final bool forResult; - final Uri uri; - final bool keepAlive; - - IRouteData({ - required this.extra, - required this.uri, - required this.forResult, - required this.keepAlive, - }); -} - -List kDestinationsIRoutes = [ - IRoute( - path: '/color', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color'), - child: ColorPage(), - ); - }, - children: [ - IRoute( - path: '/color/detail', - builder: (ctx, data) { - final Map queryParams = data.uri.queryParameters; - String? selectedColor = queryParams['color']; - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - return FadeTransitionPage( - key: const ValueKey('/color/detail'), - child: ColorDetailPage(color: color), - ); - } - return null; - }, - ), - IRoute( - path: '/color/add', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color/add'), - child: ColorAddPage(), - ); - }), - ], - ), - IRoute( - path: '/counter', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/counter'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/user', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/user'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/settings', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/settings'), - child: ColorAddPage(), - ); - }), -]; diff --git a/lib/v5_/app/navigation/transition/fade_transition_page.dart b/lib/v5_/app/navigation/transition/fade_transition_page.dart deleted file mode 100644 index 552171b..0000000 --- a/lib/v5_/app/navigation/transition/fade_transition_page.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class FadeTransitionPage extends Page { - final Widget child; - final Duration duration; - - const FadeTransitionPage({ - super.key, - required this.child, - this.duration = const Duration(milliseconds: 300), - }); - - @override - Route createRoute(BuildContext context) => - PageBasedFadeTransitionRoute(this); -} - -class PageBasedFadeTransitionRoute extends PageRoute { - final FadeTransitionPage _page; - - PageBasedFadeTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => _page.duration; - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - var curveTween = CurveTween(curve: Curves.easeIn); - return FadeTransition( - opacity: animation.drive(curveTween), - child: (settings as FadeTransitionPage).child, - ); - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/v5_/app/navigation/views/app_navigation.dart b/lib/v5_/app/navigation/views/app_navigation.dart deleted file mode 100644 index adcab6c..0000000 --- a/lib/v5_/app/navigation/views/app_navigation.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import '../router/app_router_delegate.dart'; -import 'app_navigation_rail.dart'; -import 'app_top_bar.dart'; - -class AppNavigation extends StatelessWidget { - const AppNavigation({super.key}); - - @override - Widget build(BuildContext context) { - double px1 = 1/View.of(context).devicePixelRatio; - return Scaffold( - body: Row( - children: [ - const AppNavigationRail(), - Expanded( - child: Column( - children: [ - const AppTopBar(), - Divider(height: px1,), - Expanded( - child: Router( - routerDelegate: router, - backButtonDispatcher: RootBackButtonDispatcher(), - ), - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/v5_/app/navigation/views/app_router_editor.dart b/lib/v5_/app/navigation/views/app_router_editor.dart deleted file mode 100644 index b7a0670..0000000 --- a/lib/v5_/app/navigation/views/app_router_editor.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; -import '../router/app_router_delegate.dart'; - -class AppRouterEditor extends StatefulWidget { - final ValueChanged? onSubmit; - const AppRouterEditor({super.key, this.onSubmit}); - - @override - State createState() => _AppRouterEditorState(); -} - -class _AppRouterEditorState extends State { - - final TextEditingController _controller = TextEditingController(); - - - @override - void initState() { - super.initState(); - _onRouteChange(); - router.addListener(_onRouteChange); - } - - void _onRouteChange() { - _controller.text=router.path; - setState(() { - - }); - } - - @override - void dispose() { - _controller.dispose(); - router.removeListener(_onRouteChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - 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 - ), - ) - ], - ), - ); - } -} diff --git a/lib/v5_/app/navigation/views/app_top_bar.dart b/lib/v5_/app/navigation/views/app_top_bar.dart deleted file mode 100644 index e9f8acd..0000000 --- a/lib/v5_/app/navigation/views/app_top_bar.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; -import 'app_router_editor.dart'; - -class AppTopBar extends StatelessWidget { - const AppTopBar({super.key}); - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: Container( - alignment: Alignment.center, - height: 46, - child: Row( - children: [ - const SizedBox(width: 16), - const RouterIndicator(), - Expanded( - child: Row(children: [ - const Spacer(), - SizedBox( - // width: 200, - child: AppRouterEditor( - onSubmit: (path) => router.path = path, - )), - const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: VerticalDivider( - width: 32, - ), - ) - ])), - const WindowButtons() - ], - ), - ), - ); - } -} - -class RouterIndicator extends StatefulWidget { - const RouterIndicator({super.key}); - - @override - State createState() => _RouterIndicatorState(); -} - -Map kRouteLabelMap = { - '/color': '颜色板', - '/color/add': '添加颜色', - '/color/detail': '颜色详情', - '/counter': '计数器', - '/user': '我的', - '/settings': '系统设置', -}; - -class _RouterIndicatorState extends State { - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TolyBreadcrumb( - items: pathToBreadcrumbItems(router.path), - onTapItem: (item) { - if (item.to != null) { - router.path = item.to!; - } - }, - ); - } - - void _onRouterChange() { - setState(() {}); - } - - List pathToBreadcrumbItems(String path) { - Uri uri = Uri.parse(path); - List result = []; - String to = ''; - - String distPath = ''; - for (String segment in uri.pathSegments) { - distPath += '/$segment'; - } - - for (String segment in uri.pathSegments) { - to += '/$segment'; - String label = kRouteLabelMap[to] ?? '未知路由'; - result.add(BreadcrumbItem(to: to, label: label, active: to == distPath)); - } - return result; - } -} diff --git a/lib/v5_/app/unit_app.dart b/lib/v5_/app/unit_app.dart deleted file mode 100644 index 886f427..0000000 --- a/lib/v5_/app/unit_app.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'navigation/views/app_navigation.dart'; - - -class UnitApp extends StatelessWidget { - const UnitApp({super.key}); - - @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() - ); - } -} - - diff --git a/lib/v5_/pages/color/color_add_page.dart b/lib/v5_/pages/color/color_add_page.dart deleted file mode 100644 index c6ca0cd..0000000 --- a/lib/v5_/pages/color/color_add_page.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_colorpicker/flutter_colorpicker.dart'; - -class ColorAddPage extends StatefulWidget { - const ColorAddPage({super.key}); - - @override - State createState() => _ColorAddPageState(); -} - -class _ColorAddPageState extends State { - late Color _color; - - @override - void initState() { - super.initState(); - _color = randomColor; - } - - @override - Widget build(BuildContext context) { - String text = '# ${_color.value.toRadixString(16)}'; - return Scaffold( - bottomNavigationBar: Container( - margin: EdgeInsets.only(right:20,bottom: 20), - // color: Colors.redAccent, - child: Row( - textDirection:TextDirection.rtl, - children: [ - ElevatedButton(onPressed: (){ - Navigator.of(context).pop(_color); - - }, child: Text('添加')), - SizedBox(width: 12,), - OutlinedButton(onPressed: (){ - Navigator.of(context).pop(); - }, child: Text('取消')), - ], - ), - ), - body: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0,vertical: 16), - child: Row( - children: [ - Expanded(child: Text(text,style: TextStyle(color: _color,fontSize: 24,letterSpacing: 4),)), - Container( - margin: EdgeInsets.only(left: 10), - width: 40, - height: 40, - child: Icon( - Icons.sell_outlined, - color: Colors.white, - ), - decoration: BoxDecoration( - color: _color, - borderRadius: BorderRadius.circular(8), - ), - ), - ], - ), - ), - ColorPicker( - colorPickerWidth:200, - // enableAlpha: false, - displayThumbColor:true, - pickerColor: _color, - paletteType: PaletteType.hueWheel, - onColorChanged: changeColor, - - ), - ], - ), - ); - } - - Random _random = Random(); - - Color get randomColor { - return Color.fromARGB( - 255, - _random.nextInt(256), - _random.nextInt(256), - _random.nextInt(256), - ); - } - - void _selectColor() { - Navigator.of(context).pop(_color); - } - - void changeColor(Color value) { - _color = value; - setState(() { - - }); - } -} diff --git a/lib/v5_/pages/color/color_detail_page.dart b/lib/v5_/pages/color/color_detail_page.dart deleted file mode 100644 index 17fcd17..0000000 --- a/lib/v5_/pages/color/color_detail_page.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class ColorDetailPage extends StatelessWidget { - final Color color; - const ColorDetailPage({super.key, required this.color}); - - @override - Widget build(BuildContext context) { - - const TextStyle style = TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.white - ); - String text = '# ${color.value.toRadixString(16)}'; - return Scaffold( - // appBar: AppBar( - // systemOverlayStyle: SystemUiOverlayStyle( - // statusBarColor: Colors.transparent, - // statusBarIconBrightness: Brightness.light - // ), - // iconTheme: IconThemeData(color: Colors.white), - // titleTextStyle:TextStyle(color: Colors.white,fontSize: 18) , - // backgroundColor: color, - // title: Text('颜色界面',),), - body: Container( - alignment: Alignment.center, - color: color, - child: Text(text ,style: style,), - ), - ); - } -} diff --git a/lib/v5_/pages/color/color_page.dart b/lib/v5_/pages/color/color_page.dart deleted file mode 100644 index 6764129..0000000 --- a/lib/v5_/pages/color/color_page.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/project/colors_panel.dart'; -import '../../app/navigation/router/app_router_delegate.dart'; - -class ColorPage extends StatefulWidget { - const ColorPage({super.key}); - - @override - State createState() => _ColorPageState(); -} - -class _ColorPageState extends State { - final List _colors = [ - Colors.red, Colors.black, Colors.blue, Colors.green, Colors.orange, - Colors.pink, Colors.purple, Colors.indigo, Colors.amber, Colors.cyan, - Colors.redAccent, Colors.grey, Colors.blueAccent, Colors.greenAccent, Colors.orangeAccent, - Colors.pinkAccent, Colors.purpleAccent, Colors.indigoAccent, Colors.amberAccent, Colors.cyanAccent, - ]; - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar(title:const Text('颜色主页')), - floatingActionButton: FloatingActionButton( - onPressed: _toAddPage, - child: const Icon(Icons.add), - ), - body: Align( - alignment: Alignment.topCenter, - child: ColorsPanel( - colors: _colors, - onSelect: _selectColor, - ), - ), - ); - } - - void _selectColor(Color color){ - String value = color.value.toRadixString(16); - router.path = '/color/detail?color=$value'; - // router.setPathForData('/color/detail',color); - // router.setPathKeepLive('/color/detail?color=$value'); - - } - - void _toAddPage() async { - Color? color = await router.changePath('/color/add',forResult: true); - if (color != null) { - setState(() { - _colors.add(color); - }); - } - } -} \ No newline at end of file diff --git a/lib/v6_/app.dart b/lib/v6_/app.dart deleted file mode 100644 index c9460c2..0000000 --- a/lib/v6_/app.dart +++ /dev/null @@ -1 +0,0 @@ -export 'app/unit_app.dart'; \ No newline at end of file diff --git a/lib/v6_/app/navigation/router/app_router_delegate.dart b/lib/v6_/app/navigation/router/app_router_delegate.dart deleted file mode 100644 index 998a32e..0000000 --- a/lib/v6_/app/navigation/router/app_router_delegate.dart +++ /dev/null @@ -1,245 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/material.dart'; - -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/views/sort_page.dart'; -import '../transition/fade_transition_page.dart'; -import '../../../pages/color/color_add_page.dart'; - -const List kDestinationsPaths = [ - '/color', - '/counter', - '/sort', - '/user', - '/settings', -]; - -AppRouterDelegate router = AppRouterDelegate(); - -class AppRouterDelegate extends RouterDelegate with ChangeNotifier { - - AppRouterDelegate({String initial = '/'}){ - changePath('/color',keepAlive: true); - } - - String _path = '/color'; - - String get path => _path; - - int? get activeIndex { - if(path.startsWith('/color')) return 0; - if(path.startsWith('/counter')) return 1; - if(path.startsWith('/sort')) return 2; - if(path.startsWith('/user')) return 3; - if(path.startsWith('/settings')) return 4; - return null; - } - - final Map> _completerMap = {}; - - Completer? completer; - - final Map> _alivePageMap = {}; - - void setPathKeepLive(String value){ - _alivePageMap[value] = _buildPageByPath(value); - path = value; - } - - final Map _pathExtraMap = {}; - - FutureOr changePath(String value,{bool forResult=false,Object? extra,bool keepAlive = false}){ - if(forResult){ - _completerMap[value] = Completer(); - } - if(keepAlive){ - _alivePageMap[value] = _buildPageByPath(value); - } - if(extra!=null){ - _pathExtraMap[value] = extra; - } - path = value; - if(forResult){ - return _completerMap[value]!.future; - } - } - - - set path(String value) { - if (_path == value) return; - _path = value; - notifyListeners(); - } - - @override - Widget build(BuildContext context) { - List pages = []; - if(_alivePageMap.containsKey(path)){ - for (String key in _alivePageMap.keys) { - if(path!=key){ - pages.addAll(_alivePageMap[key]!); - } - } - pages.addAll(_alivePageMap[path]!) ; - }else{ - for (var element in _alivePageMap.values) { - pages.addAll(element); - } - List currentPages = _buildPageByPath(path); - pages.removeWhere((element) { - return currentPages.map((e) => e.key).contains(element.key); - }); - // 移除 pages 中和 currentPages 具有相同key 的元素 - pages.addAll(_buildPageByPath(path)); - } - - return Navigator( - onPopPage: _onPopPage, - pages: pages.toSet().toList(), - ); - } - - List _buildPageByPath(String path) { - Widget? child; - if(path.startsWith('/color')){ - return buildColorPages(path); - } - - if (path == kDestinationsPaths[1]) { - child = const CounterPage(); - } - if (path == kDestinationsPaths[2]) { - child = SortPage(); - } - if (path == kDestinationsPaths[3]) { - child = const UserPage(); - } - if (path == kDestinationsPaths[4]) { - child = const SettingPage(); - } - return [ - FadeTransitionPage( - key: ValueKey(path), - child: child ?? const EmptyPage(), - ) - ]; - } - - List buildColorPages(String path){ - List result = []; - Uri uri = Uri.parse(path); - for (String segment in uri.pathSegments) { - if(segment == 'color'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color'), - child:ColorPage(), - )); - } - if(segment =='detail'){ - final Map queryParams = uri.queryParameters; - String? selectedColor = queryParams['color']; - - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: color), - )); - }else{ - Color? selectedColor = _pathExtraMap[path]; - if (selectedColor != null) { - result.add( FadeTransitionPage( - key: const ValueKey('/color/detail'), - child:ColorDetailPage(color: selectedColor), - )); - _pathExtraMap.remove(path); - } - } - } - if(segment == 'add'){ - result.add( const FadeTransitionPage( - key: ValueKey('/color/add'), - child:ColorAddPage(), - )); - } - - } - return result; - } - - @override - Future popRoute() async { - print('=======popRoute========='); - return true; - } - - bool _onPopPage(Route route, result) { - if(_completerMap.containsKey(path)){ - _completerMap[path]?.complete(result); - _completerMap.remove(path); - } - - path = backPath(path); - return route.didPop(result); - } - - String backPath(String path){ - Uri uri = Uri.parse(path); - if(uri.pathSegments.length==1) return path; - List parts = List.of(uri.pathSegments)..removeLast(); - return '/${parts.join('/')}'; - } - - @override - Future setNewRoutePath(configuration) async {} -} - - - -// class AppRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { -// -// List _value = ['/']; -// -// -// List get value => _value; -// -// set value(List value){ -// _value = value; -// notifyListeners(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Navigator( -// onPopPage: _onPopPage, -// pages: _value.map((e) => _pageMap[e]!).toList(), -// ); -// } -// -// final Map _pageMap = const { -// '/': MaterialPage(child: HomePage()), -// 'a': MaterialPage(child: PageA()), -// 'b': MaterialPage(child: PageB()), -// 'c': MaterialPage(child: PageC()), -// }; -// -// bool _onPopPage(Route route, result) { -// _value = List.of(_value)..removeLast(); -// notifyListeners(); -// return route.didPop(result); -// } -// -// @override -// GlobalKey? navigatorKey = GlobalKey(); -// -// @override -// Future setNewRoutePath(String configuration) async{ -// } -// } diff --git a/lib/v6_/app/navigation/router/iroute.dart b/lib/v6_/app/navigation/router/iroute.dart deleted file mode 100644 index ef2160d..0000000 --- a/lib/v6_/app/navigation/router/iroute.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../../pages/color/color_add_page.dart'; -import '../../../pages/color/color_detail_page.dart'; -import '../../../pages/color/color_page.dart'; -import '../transition/fade_transition_page.dart'; - -class IRoute { - final String path; - final IRoutePageBuilder builder; - final List children; - - const IRoute({ - required this.path, - this.children = const [], - required this.builder, - }); - - @override - String toString() { - return 'IRoute{path: $path, children: $children}'; - } - - List list() { - return []; - } -} - -typedef IRoutePageBuilder = Page? Function( - BuildContext context, IRouteData data); - -class IRouteData { - final Object? extra; - final bool forResult; - final Uri uri; - final bool keepAlive; - - IRouteData({ - required this.extra, - required this.uri, - required this.forResult, - required this.keepAlive, - }); -} - -List kDestinationsIRoutes = [ - IRoute( - path: '/color', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color'), - child: ColorPage(), - ); - }, - children: [ - IRoute( - path: '/color/detail', - builder: (ctx, data) { - final Map queryParams = data.uri.queryParameters; - String? selectedColor = queryParams['color']; - if (selectedColor != null) { - Color color = Color(int.parse(selectedColor, radix: 16)); - return FadeTransitionPage( - key: const ValueKey('/color/detail'), - child: ColorDetailPage(color: color), - ); - } - return null; - }, - ), - IRoute( - path: '/color/add', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color/add'), - child: ColorAddPage(), - ); - }), - ], - ), - IRoute( - path: '/counter', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/counter'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/user', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/user'), - child: ColorAddPage(), - ); - }), - IRoute( - path: '/settings', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/settings'), - child: ColorAddPage(), - ); - }), -]; diff --git a/lib/v6_/app/navigation/transition/fade_transition_page.dart b/lib/v6_/app/navigation/transition/fade_transition_page.dart deleted file mode 100644 index 552171b..0000000 --- a/lib/v6_/app/navigation/transition/fade_transition_page.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class FadeTransitionPage extends Page { - final Widget child; - final Duration duration; - - const FadeTransitionPage({ - super.key, - required this.child, - this.duration = const Duration(milliseconds: 300), - }); - - @override - Route createRoute(BuildContext context) => - PageBasedFadeTransitionRoute(this); -} - -class PageBasedFadeTransitionRoute extends PageRoute { - final FadeTransitionPage _page; - - PageBasedFadeTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => _page.duration; - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - var curveTween = CurveTween(curve: Curves.easeIn); - return FadeTransition( - opacity: animation.drive(curveTween), - child: (settings as FadeTransitionPage).child, - ); - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/v6_/app/navigation/transition/no_transition_page.dart b/lib/v6_/app/navigation/transition/no_transition_page.dart deleted file mode 100644 index 291910b..0000000 --- a/lib/v6_/app/navigation/transition/no_transition_page.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class NoTransitionPage extends Page { - final Widget child; - - const NoTransitionPage({ - super.key, - required this.child, - }); - - @override - Route createRoute(BuildContext context) => NoTransitionRoute(this); -} - -class NoTransitionRoute extends PageRoute { - - final NoTransitionPage _page; - - NoTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => const Duration(milliseconds: 0); - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return (settings as NoTransitionPage).child; - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/v6_/app/navigation/views/app_navigation.dart b/lib/v6_/app/navigation/views/app_navigation.dart deleted file mode 100644 index e1d002d..0000000 --- a/lib/v6_/app/navigation/views/app_navigation.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../pages/sort/sort_setting.dart'; -import '../router/app_router_delegate.dart'; -import 'app_navigation_rail.dart'; -import 'app_top_bar.dart'; - -class AppNavigation extends StatelessWidget { - const AppNavigation({super.key}); - - @override - Widget build(BuildContext context) { - double px1 = 1/View.of(context).devicePixelRatio; - return Scaffold( - endDrawer: Drawer( - child: SortSettings(), - ), - body: Row( - children: [ - const AppNavigationRail(), - Expanded( - child: Column( - children: [ - const AppTopBar(), - Divider(height: px1,), - Expanded( - child: Router( - routerDelegate: router, - backButtonDispatcher: RootBackButtonDispatcher(), - ), - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/v6_/app/navigation/views/app_navigation_rail.dart b/lib/v6_/app/navigation/views/app_navigation_rail.dart deleted file mode 100644 index 29ea0b3..0000000 --- a/lib/v6_/app/navigation/views/app_navigation_rail.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; - -class AppNavigationRail extends StatefulWidget { - const AppNavigationRail({super.key}); - - @override - State createState() => _AppNavigationRailState(); -} - -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), - ]; - - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: TolyNavigationRail( - items: deskNavBarMenus, - leading: const Padding( - padding: EdgeInsets.symmetric(vertical: 18.0), - child: FlutterLogo(), - ), - tail: Padding( - padding: const EdgeInsets.only(bottom: 6.0), - child: Text('V0.0.4',style: TextStyle(color: Colors.white,fontSize: 12),), - ), - backgroundColor: const Color(0xff3975c6), - onDestinationSelected: _onDestinationSelected, - selectedIndex: router.activeIndex, - ), - ); - - } - - void _onDestinationSelected(int index) { - if(index==1){ - router.changePath(kDestinationsPaths[index],keepAlive: true); - }else{ - router.path = kDestinationsPaths[index]; - } - } - - void _onRouterChange() { - setState(() {}); - } -} diff --git a/lib/v6_/app/navigation/views/app_router_editor.dart b/lib/v6_/app/navigation/views/app_router_editor.dart deleted file mode 100644 index 9173948..0000000 --- a/lib/v6_/app/navigation/views/app_router_editor.dart +++ /dev/null @@ -1,79 +0,0 @@ -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 '../../../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 { - final ValueChanged? onSubmit; - const AppRouterEditor({super.key, this.onSubmit}); - - @override - State createState() => _AppRouterEditorState(); -} - -class _AppRouterEditorState extends State { - - final TextEditingController _controller = TextEditingController(); - - - @override - void initState() { - super.initState(); - _onRouteChange(); - router.addListener(_onRouteChange); - } - - void _onRouteChange() { - _controller.text=router.path; - setState(() { - - }); - } - - @override - void dispose() { - _controller.dispose(); - router.removeListener(_onRouteChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - print(router.path); - if(router.path=='/sort'){ - return SortBar(); - } - - 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 - ), - ) - ], - ), - ); - } -} diff --git a/lib/v6_/app/navigation/views/app_top_bar.dart b/lib/v6_/app/navigation/views/app_top_bar.dart deleted file mode 100644 index 84890eb..0000000 --- a/lib/v6_/app/navigation/views/app_top_bar.dart +++ /dev/null @@ -1,106 +0,0 @@ -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'; - -class AppTopBar extends StatelessWidget { - const AppTopBar({super.key}); - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: Container( - alignment: Alignment.center, - height: 46, - child: Row( - children: [ - const SizedBox(width: 16), - const RouterIndicator(), - Expanded( - child: Row(children: [ - const Spacer(), - SizedBox( - // width: 200, - child: AppRouterEditor( - onSubmit: (path) => router.path = path, - )), - const Padding( - padding: EdgeInsets.symmetric(vertical: 12.0), - child: VerticalDivider( - width: 32, - ), - ) - ])), - const WindowButtons() - ], - ), - ), - ); - } -} - -class RouterIndicator extends StatefulWidget { - const RouterIndicator({super.key}); - - @override - State createState() => _RouterIndicatorState(); -} - -Map kRouteLabelMap = { - '/color': '颜色板', - '/color/add': '添加颜色', - '/color/detail': '颜色详情', - '/counter': '计数器', - '/user': '我的', - '/sort': '可视化排序', - '/settings': '系统设置', -}; - -class _RouterIndicatorState extends State { - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TolyBreadcrumb( - items: pathToBreadcrumbItems(router.path), - onTapItem: (item) { - if (item.to != null) { - router.path = item.to!; - } - }, - ); - } - - void _onRouterChange() { - setState(() {}); - } - - List pathToBreadcrumbItems(String path) { - Uri uri = Uri.parse(path); - List result = []; - String to = ''; - - String distPath = ''; - for (String segment in uri.pathSegments) { - distPath += '/$segment'; - } - - for (String segment in uri.pathSegments) { - to += '/$segment'; - String label = kRouteLabelMap[to] ?? '未知路由'; - result.add(BreadcrumbItem(to: to, label: label, active: to == distPath)); - } - return result; - } -} diff --git a/lib/v6_/pages/color/color_add_page.dart b/lib/v6_/pages/color/color_add_page.dart deleted file mode 100644 index c6ca0cd..0000000 --- a/lib/v6_/pages/color/color_add_page.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_colorpicker/flutter_colorpicker.dart'; - -class ColorAddPage extends StatefulWidget { - const ColorAddPage({super.key}); - - @override - State createState() => _ColorAddPageState(); -} - -class _ColorAddPageState extends State { - late Color _color; - - @override - void initState() { - super.initState(); - _color = randomColor; - } - - @override - Widget build(BuildContext context) { - String text = '# ${_color.value.toRadixString(16)}'; - return Scaffold( - bottomNavigationBar: Container( - margin: EdgeInsets.only(right:20,bottom: 20), - // color: Colors.redAccent, - child: Row( - textDirection:TextDirection.rtl, - children: [ - ElevatedButton(onPressed: (){ - Navigator.of(context).pop(_color); - - }, child: Text('添加')), - SizedBox(width: 12,), - OutlinedButton(onPressed: (){ - Navigator.of(context).pop(); - }, child: Text('取消')), - ], - ), - ), - body: Column( - // mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0,vertical: 16), - child: Row( - children: [ - Expanded(child: Text(text,style: TextStyle(color: _color,fontSize: 24,letterSpacing: 4),)), - Container( - margin: EdgeInsets.only(left: 10), - width: 40, - height: 40, - child: Icon( - Icons.sell_outlined, - color: Colors.white, - ), - decoration: BoxDecoration( - color: _color, - borderRadius: BorderRadius.circular(8), - ), - ), - ], - ), - ), - ColorPicker( - colorPickerWidth:200, - // enableAlpha: false, - displayThumbColor:true, - pickerColor: _color, - paletteType: PaletteType.hueWheel, - onColorChanged: changeColor, - - ), - ], - ), - ); - } - - Random _random = Random(); - - Color get randomColor { - return Color.fromARGB( - 255, - _random.nextInt(256), - _random.nextInt(256), - _random.nextInt(256), - ); - } - - void _selectColor() { - Navigator.of(context).pop(_color); - } - - void changeColor(Color value) { - _color = value; - setState(() { - - }); - } -} diff --git a/lib/v6_/pages/color/color_detail_page.dart b/lib/v6_/pages/color/color_detail_page.dart deleted file mode 100644 index 17fcd17..0000000 --- a/lib/v6_/pages/color/color_detail_page.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class ColorDetailPage extends StatelessWidget { - final Color color; - const ColorDetailPage({super.key, required this.color}); - - @override - Widget build(BuildContext context) { - - const TextStyle style = TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.white - ); - String text = '# ${color.value.toRadixString(16)}'; - return Scaffold( - // appBar: AppBar( - // systemOverlayStyle: SystemUiOverlayStyle( - // statusBarColor: Colors.transparent, - // statusBarIconBrightness: Brightness.light - // ), - // iconTheme: IconThemeData(color: Colors.white), - // titleTextStyle:TextStyle(color: Colors.white,fontSize: 18) , - // backgroundColor: color, - // title: Text('颜色界面',),), - body: Container( - alignment: Alignment.center, - color: color, - child: Text(text ,style: style,), - ), - ); - } -} diff --git a/lib/v6_/pages/color/color_page.dart b/lib/v6_/pages/color/color_page.dart deleted file mode 100644 index 6764129..0000000 --- a/lib/v6_/pages/color/color_page.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/project/colors_panel.dart'; -import '../../app/navigation/router/app_router_delegate.dart'; - -class ColorPage extends StatefulWidget { - const ColorPage({super.key}); - - @override - State createState() => _ColorPageState(); -} - -class _ColorPageState extends State { - final List _colors = [ - Colors.red, Colors.black, Colors.blue, Colors.green, Colors.orange, - Colors.pink, Colors.purple, Colors.indigo, Colors.amber, Colors.cyan, - Colors.redAccent, Colors.grey, Colors.blueAccent, Colors.greenAccent, Colors.orangeAccent, - Colors.pinkAccent, Colors.purpleAccent, Colors.indigoAccent, Colors.amberAccent, Colors.cyanAccent, - ]; - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar(title:const Text('颜色主页')), - floatingActionButton: FloatingActionButton( - onPressed: _toAddPage, - child: const Icon(Icons.add), - ), - body: Align( - alignment: Alignment.topCenter, - child: ColorsPanel( - colors: _colors, - onSelect: _selectColor, - ), - ), - ); - } - - void _selectColor(Color color){ - String value = color.value.toRadixString(16); - router.path = '/color/detail?color=$value'; - // router.setPathForData('/color/detail',color); - // router.setPathKeepLive('/color/detail?color=$value'); - - } - - void _toAddPage() async { - Color? color = await router.changePath('/color/add',forResult: true); - if (color != null) { - setState(() { - _colors.add(color); - }); - } - } -} \ No newline at end of file diff --git a/lib/v6_/pages/counter/counter_page.dart b/lib/v6_/pages/counter/counter_page.dart deleted file mode 100644 index b5b2e17..0000000 --- a/lib/v6_/pages/counter/counter_page.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -class CounterPage extends StatefulWidget { - const CounterPage({super.key}); - - @override - State createState() => _CounterPageState(); -} - -class _CounterPageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } -} \ No newline at end of file diff --git a/lib/v6_/pages/empty/empty_page.dart b/lib/v6_/pages/empty/empty_page.dart deleted file mode 100644 index b05f56f..0000000 --- a/lib/v6_/pages/empty/empty_page.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -class EmptyPage extends StatelessWidget { - const EmptyPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar( - // title: Text('界面走丢了'), - // ), - body: Scaffold( - body: Center( - child: Wrap( - spacing: 16, - crossAxisAlignment: WrapCrossAlignment.center, - direction: Axis.vertical, - children: [ - Icon(Icons.nearby_error,size: 64, color: Colors.grey), - Text( - '404 界面丢失', - style: TextStyle(fontSize: 24, color: Colors.grey), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/v6_/pages/settings/settings_page.dart b/lib/v6_/pages/settings/settings_page.dart deleted file mode 100644 index 0b53503..0000000 --- a/lib/v6_/pages/settings/settings_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class SettingPage extends StatelessWidget { - const SettingPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('SettingPage'))); - } -} diff --git a/lib/v6_/pages/sort/sort_page.dart b/lib/v6_/pages/sort/sort_page.dart deleted file mode 100644 index d440071..0000000 --- a/lib/v6_/pages/sort/sort_page.dart +++ /dev/null @@ -1,859 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - String currentSort = 'bubble'; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - 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)); - } - 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); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - 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()); // 延迟操作,用于可视化排序过程 - streamController.add(numbers); - } - - ///插入排序 - 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); - } - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - 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; - } - - stopwatch.stop(); - - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - setSort(String type) { - setState(() { - currentSort = type; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { - //随机往数组中填值 - numbers.add(Random().nextInt(500)); - } - setState(() {}); - } - - @override - void dispose() { - streamController.close(); - super.dispose(); - } - - @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); - }, - ) - ], - ), - 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++; - return CustomPaint( - painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, - ), - ); - }).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), - ), - ), - ], - ), - ), - ); - } -} - -class BarPainter extends CustomPainter { - //宽度 - final double width; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { - paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { - paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { - paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { - paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { - paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { - paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { - paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width, 0), - Offset( - index * width, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/v6_/pages/sort/views/sort_page.dart b/lib/v6_/pages/sort/views/sort_page.dart deleted file mode 100644 index 01224c3..0000000 --- a/lib/v6_/pages/sort/views/sort_page.dart +++ /dev/null @@ -1,22 +0,0 @@ - -import 'package:flutter/material.dart'; - -import '../provider/state.dart'; -import 'data_painter.dart'; - -class SortPage extends StatelessWidget{ - - @override - Widget build(BuildContext context) { - SortState state = SortStateScope.of(context); - List numbers = state.data; - - return Scaffold( - body: CustomPaint( - painter: DataPainter(data: numbers), - child: ConstrainedBox(constraints: BoxConstraints.expand()), - ), - ); - } -} - diff --git a/lib/v6_/pages/user/user_page.dart b/lib/v6_/pages/user/user_page.dart deleted file mode 100644 index aba9710..0000000 --- a/lib/v6_/pages/user/user_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class UserPage extends StatelessWidget { - const UserPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('UserPage'))); - } -} diff --git a/lib/v6_result_/app.dart b/lib/v6_result_/app.dart deleted file mode 100644 index c9460c2..0000000 --- a/lib/v6_result_/app.dart +++ /dev/null @@ -1 +0,0 @@ -export 'app/unit_app.dart'; \ No newline at end of file diff --git a/lib/v6_result_/app/navigation/router/iroute.dart b/lib/v6_result_/app/navigation/router/iroute.dart deleted file mode 100644 index 5c417ae..0000000 --- a/lib/v6_result_/app/navigation/router/iroute.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../pages/color/color_add_page.dart'; -import '../../../pages/color/color_detail_page.dart'; -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 '../transition/fade_transition_page.dart'; - -class IRoute { - final String path; - final IRoutePageBuilder? builder; - final List children; - - const IRoute({ - required this.path, - this.children = const [], - this.builder, - }); - - @override - String toString() { - return 'IRoute{path: $path, children: $children}'; - } - - IRoute? match(String path) { - return matchRoute(this, path); - } - - List find(String input){ - String fixInput = input.substring(1); - List nodes = findNodes(this,fixInput,0,'/',[]); - if(nodes.isNotEmpty&&nodes.last.path!=input){ - return []; - } - return nodes; - } - - List findNodes(IRoute node,String input,int deep,String prefix,List result){ - String separator = '/'; - - List parts = input.split(separator); - if(deep>parts.length-1){ - return result; - } - String target = parts[deep]; - if(node.children.isNotEmpty){ - List nodes = node.children.where((e) => e.path==prefix+target).toList(); - bool match = nodes.isNotEmpty; - if(match){ - IRoute matched = nodes.first; - result.add(matched); - String nextPrefix = '${matched.path}$separator'; - findNodes(matched, input, ++deep,nextPrefix,result); - } - }else{ - return result; - } - return result; - } - - // List findNodes(IRoute node,String input,int deep,String prefix,List result){ - // String separator = '/'; - // List parts = input.split(separator); - // if(deep>parts.length-1){ - // return result; - // } - // String target = parts[deep]; - // if(node.children.isNotEmpty){ - // List nodes = node.children.where((e) => e.path==prefix+target).toList(); - // bool match = nodes.isNotEmpty; - // if(match){ - // IRoute matched = nodes.first; - // result.add(matched); - // String nextPrefix = '${matched.path}$separator'; - // findNodes(matched, input, ++deep,nextPrefix,result); - // } - // }else{ - // return result; - // } - // return result; - // } - - IRoute? matchRoute(IRoute route, String path) { - if (route.path == path) { - return route; - } else { - if (route.children.isNotEmpty) { - for (int i = 0; i < route.children.length; i++) { - IRoute current = route.children[i]; - IRoute? target = matchRoute(current, path); - if (target != null) { - return target; - } - } - } else { - return null; - } - } - return null; - } -} - -typedef IRoutePageBuilder = Page? Function( - BuildContext context, IRouteData data); - -class IRouteData { - final Object? extra; - final bool forResult; - final Uri uri; - final bool keepAlive; - - IRouteData({ - this.extra, - required this.uri, - this.forResult = false, - this.keepAlive = false, - }); -} - -IRoute root = IRoute(path: 'root', children: kDestinationsIRoutes); - -List kDestinationsIRoutes = [ - IRoute( - path: '/color', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color'), - child: ColorPage(), - ); - }, - children: [ - IRoute( - path: '/color/detail', - builder: (ctx, data) { - final Map queryParams = data.uri.queryParameters; - String? selectedColor = queryParams['color']; - Color color = Colors.black; - if (selectedColor != null) { - color = Color(int.parse(selectedColor, radix: 16)); - } else if (data.extra is Color) { - color = data.extra as Color; - } - return FadeTransitionPage( - key: const ValueKey('/color/detail'), - child: ColorDetailPage(color: color), - ); - }, - ), - IRoute( - path: '/color/add', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color/add'), - child: ColorAddPage(), - ); - }), - ], - ), - IRoute( - path: '/counter', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/counter'), - child: CounterPage(), - ); - }), - IRoute( - path: '/user', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/user'), - child: UserPage(), - ); - }), - IRoute( - path: '/settings', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/settings'), - child: SettingPage(), - ); - }), -]; diff --git a/lib/v6_result_/app/navigation/router/route_history.dart b/lib/v6_result_/app/navigation/router/route_history.dart deleted file mode 100644 index 02b5430..0000000 --- a/lib/v6_result_/app/navigation/router/route_history.dart +++ /dev/null @@ -1,6 +0,0 @@ -class RouteHistory{ - final String path; - final Object? extra; - - RouteHistory(this.path, { this.extra}); -} \ No newline at end of file diff --git a/lib/v6_result_/app/navigation/transition/no_transition_page.dart b/lib/v6_result_/app/navigation/transition/no_transition_page.dart deleted file mode 100644 index 291910b..0000000 --- a/lib/v6_result_/app/navigation/transition/no_transition_page.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021, the Flutter project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:flutter/material.dart'; - -class NoTransitionPage extends Page { - final Widget child; - - const NoTransitionPage({ - super.key, - required this.child, - }); - - @override - Route createRoute(BuildContext context) => NoTransitionRoute(this); -} - -class NoTransitionRoute extends PageRoute { - - final NoTransitionPage _page; - - NoTransitionRoute(this._page) : super(settings: _page); - - @override - Color? get barrierColor => null; - - @override - String? get barrierLabel => null; - - @override - Duration get transitionDuration => const Duration(milliseconds: 0); - - @override - bool get maintainState => true; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return (settings as NoTransitionPage).child; - } - - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) => - child; -} diff --git a/lib/v6_result_/app/navigation/views/app_navigation_rail.dart b/lib/v6_result_/app/navigation/views/app_navigation_rail.dart deleted file mode 100644 index 8ea8ace..0000000 --- a/lib/v6_result_/app/navigation/views/app_navigation_rail.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; - -class AppNavigationRail extends StatefulWidget { - const AppNavigationRail({super.key}); - - @override - State createState() => _AppNavigationRailState(); -} - -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.person), - MenuMeta(label: '设置', icon: Icons.settings), - ]; - - @override - void initState() { - super.initState(); - router.addListener(_onRouterChange); - } - - @override - void dispose() { - router.removeListener(_onRouterChange); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return DragToMoveWrap( - child: TolyNavigationRail( - items: deskNavBarMenus, - leading: const Padding( - padding: EdgeInsets.symmetric(vertical: 18.0), - child: FlutterLogo(), - ), - tail: Padding( - padding: const EdgeInsets.only(bottom: 6.0), - child: Text('V0.0.5',style: TextStyle(color: Colors.white,fontSize: 12),), - ), - backgroundColor: const Color(0xff3975c6), - onDestinationSelected: _onDestinationSelected, - selectedIndex: router.activeIndex, - ), - ); - - } - - void _onDestinationSelected(int index) { - if(index==1){ - router.changePath(kDestinationsPaths[index],keepAlive: true); - }else{ - router.path = kDestinationsPaths[index]; - } - } - - void _onRouterChange() { - setState(() {}); - } -} diff --git a/lib/v6_result_/app/unit_app.dart b/lib/v6_result_/app/unit_app.dart deleted file mode 100644 index 1a21114..0000000 --- a/lib/v6_result_/app/unit_app.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'navigation/router/app_router_delegate.dart'; -import 'navigation/views/app_navigation.dart'; -import 'navigation/views/app_navigation_rail.dart'; - -class UnitApp extends StatelessWidget { - const UnitApp({super.key}); - - @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() - ); - } -} - - diff --git a/lib/v6_result_/pages/counter/counter_page.dart b/lib/v6_result_/pages/counter/counter_page.dart deleted file mode 100644 index b5b2e17..0000000 --- a/lib/v6_result_/pages/counter/counter_page.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -class CounterPage extends StatefulWidget { - const CounterPage({super.key}); - - @override - State createState() => _CounterPageState(); -} - -class _CounterPageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } -} \ No newline at end of file diff --git a/lib/v6_result_/pages/empty/empty_page.dart b/lib/v6_result_/pages/empty/empty_page.dart deleted file mode 100644 index b05f56f..0000000 --- a/lib/v6_result_/pages/empty/empty_page.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -class EmptyPage extends StatelessWidget { - const EmptyPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - // appBar: AppBar( - // title: Text('界面走丢了'), - // ), - body: Scaffold( - body: Center( - child: Wrap( - spacing: 16, - crossAxisAlignment: WrapCrossAlignment.center, - direction: Axis.vertical, - children: [ - Icon(Icons.nearby_error,size: 64, color: Colors.grey), - Text( - '404 界面丢失', - style: TextStyle(fontSize: 24, color: Colors.grey), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/v6_result_/pages/settings/settings_page.dart b/lib/v6_result_/pages/settings/settings_page.dart deleted file mode 100644 index 0b53503..0000000 --- a/lib/v6_result_/pages/settings/settings_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class SettingPage extends StatelessWidget { - const SettingPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('SettingPage'))); - } -} diff --git a/lib/v6_result_/pages/sort/sort_page.dart b/lib/v6_result_/pages/sort/sort_page.dart deleted file mode 100644 index d440071..0000000 --- a/lib/v6_result_/pages/sort/sort_page.dart +++ /dev/null @@ -1,859 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - String currentSort = 'bubble'; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - 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)); - } - 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); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - 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()); // 延迟操作,用于可视化排序过程 - streamController.add(numbers); - } - - ///插入排序 - 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); - } - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - 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; - } - - stopwatch.stop(); - - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - setSort(String type) { - setState(() { - currentSort = type; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { - //随机往数组中填值 - numbers.add(Random().nextInt(500)); - } - setState(() {}); - } - - @override - void dispose() { - streamController.close(); - super.dispose(); - } - - @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); - }, - ) - ], - ), - 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++; - return CustomPaint( - painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, - ), - ); - }).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), - ), - ), - ], - ), - ), - ); - } -} - -class BarPainter extends CustomPainter { - //宽度 - final double width; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { - paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { - paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { - paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { - paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { - paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { - paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { - paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width, 0), - Offset( - index * width, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/v6_result_/pages/user/user_page.dart b/lib/v6_result_/pages/user/user_page.dart deleted file mode 100644 index aba9710..0000000 --- a/lib/v6_result_/pages/user/user_page.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/material.dart'; - -class UserPage extends StatelessWidget { - const UserPage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body:Center(child: Text('UserPage'))); - } -} diff --git a/lib/v7/app/navigation/router/app_router_delegate.dart b/lib/v7/app/navigation/router/app_router_delegate.dart index 20a4f30..be3d580 100644 --- a/lib/v7/app/navigation/router/app_router_delegate.dart +++ b/lib/v7/app/navigation/router/app_router_delegate.dart @@ -1,15 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; - import 'iroute.dart'; -import 'route_history.dart'; - -const List kDestinationsPaths = [ - '/color', - '/counter', - '/user', - '/settings', -]; +import 'iroute_config.dart'; +import 'route_history_manager.dart'; +import 'routes.dart'; +import 'views/not_find_view.dart'; AppRouterDelegate router = AppRouterDelegate(); @@ -18,120 +13,82 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { String get path => _path; - AppRouterDelegate() { - // keepAlivePath.add('/color'); - _histories.add(RouteHistory(path)); + final IRoutePageBuilder? notFindPageBuilder; + + AppRouterDelegate({this.notFindPageBuilder}) { + _historyManager.recode(IRouteConfig(uri: Uri.parse(path))); } - final List _histories = []; - final List _backHistories = []; + Page _defaultNotFindPageBuilder(_, __) => const MaterialPage( + child: Material(child: NotFindPage()), + ); - List get histories => _histories.reversed.toList(); + final RouteHistoryManager _historyManager = RouteHistoryManager(); - bool get hasHistory => _histories.length > 1; - - bool get hasBackHistory => _backHistories.isNotEmpty; + RouteHistoryManager get historyManager => _historyManager; /// 历史回退操作 - /// 将当前顶层移除,并加入 _backHistories 撤销列表 - /// 并转到前一路径 - void back() { - if (!hasHistory) return; - RouteHistory top = _histories.removeLast(); - _backHistories.add(top); - if (_histories.isNotEmpty) { - _path = _histories.last.path; - if (_histories.last.extra != null) { - _pathExtraMap[_path] = _histories.last.extra; - } - notifyListeners(); - } - } + /// 详见: [RouteHistoryManager.back] + void back() => _historyManager.back(changeRoute); - void toHistory(RouteHistory history) { - _path = history.path; - if (history.extra != null) { - _pathExtraMap[_path] = history.extra; - } - notifyListeners(); - } + /// 撤销回退操作 + /// 详见: [RouteHistoryManager.revocation] + void revocation() => _historyManager.revocation(changeRoute); void closeHistory(int index) { - _histories.removeAt(index); + _historyManager.close(index); notifyListeners(); } void clearHistory() { - _histories.clear(); + _historyManager.clear(); notifyListeners(); } - /// 撤销回退操作 - /// 取出回退列表的最后元素,跳转到该路径 - void revocation() { - RouteHistory target = _backHistories.removeLast(); - _path = target.path; - if (target.extra != null) { - _pathExtraMap[_path] = target.extra; - } - _histories.add(target); - notifyListeners(); - } - - 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; - return null; - } - final Map> _completerMap = {}; - - Completer? completer; - final Map _pathExtraMap = {}; - final List keepAlivePath = []; - FutureOr changePath( - String value, { - bool forResult = false, - Object? extra, - bool keepAlive = false, - bool recordHistory = true, - }) { + FutureOr changeRoute(IRouteConfig config) { + String value = config.uri.path; if (_path == value) null; - if (forResult) { + if (config.forResult) { _completerMap[value] = Completer(); } - if (keepAlive) { + if (config.keepAlive) { if (keepAlivePath.contains(value)) { keepAlivePath.remove(value); } keepAlivePath.add(value); } - if (extra != null) { - _pathExtraMap[value] = extra; + if (config.extra != null) { + _pathExtraMap[value] = config.extra; } - if (recordHistory) { - _addPathToHistory(value,extra); + if (config.recordHistory) { + _historyManager.recode(config); } _path = value; notifyListeners(); - if (forResult) { + if (config.forResult) { return _completerMap[value]!.future; } } - - void _addPathToHistory(String value, Object? extra) { - if (_histories.isNotEmpty && value == _histories.last.path) return; - _histories.add(RouteHistory( - value, - extra: _pathExtraMap[path], + FutureOr changePath( + String value, { + bool forResult = false, + Object? extra, + bool keepAlive = false, + bool recordHistory = true, + }) { + return changeRoute(IRouteConfig( + uri: Uri.parse(value), + forResult: forResult, + extra: extra, + keepAlive: keepAlive, + recordHistory: recordHistory, )); } @@ -156,7 +113,7 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { /// 去除和 topPages 中重复的界面 pages.removeWhere( - (element) => topPages.map((e) => e.key).contains(element.key)); + (element) => topPages.map((e) => e.key).contains(element.key)); } pages.addAll(topPages); @@ -165,23 +122,26 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { List _buildPageByPathFromTree(BuildContext context, String path) { List result = []; - List iRoutes = root.find(path); + List iRoutes = rootRoute.find(path); if (iRoutes.isNotEmpty) { for (int i = 0; i < iRoutes.length; i++) { - IRoute iroute = iRoutes[i]; + IRouteNode iroute = iRoutes[i]; String path = iroute.path; Object? extra = _pathExtraMap[path]; bool keepAlive = keepAlivePath.contains(path); bool forResult = _completerMap.containsKey(path); - Page? page = iroute.builder?.call( - context, - IRouteData( - uri: Uri.parse(path), - extra: extra, - keepAlive: keepAlive, - forResult: forResult, - ), + IRouteConfig config = IRouteConfig( + uri: Uri.parse(path), + extra: extra, + keepAlive: keepAlive, + forResult: forResult, ); + Page? page; + if (iroute is NotFindNode) { + page = (notFindPageBuilder ?? _defaultNotFindPageBuilder)(context, config); + } else { + page = iroute.createPage(context, config); + } if (page != null) { result.add(page); } @@ -201,8 +161,7 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { _completerMap[path]?.complete(result); _completerMap.remove(path); } - - changePath(backPath(path),recordHistory: false); + changePath(backPath(path), recordHistory: false); return route.didPop(result); } diff --git a/lib/v7/app/navigation/router/iroute.dart b/lib/v7/app/navigation/router/iroute.dart index 5c417ae..ce6e132 100644 --- a/lib/v7/app/navigation/router/iroute.dart +++ b/lib/v7/app/navigation/router/iroute.dart @@ -1,185 +1,105 @@ import 'package:flutter/material.dart'; -import '../../../pages/color/color_add_page.dart'; -import '../../../pages/color/color_detail_page.dart'; -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 '../transition/fade_transition_page.dart'; +import 'iroute_config.dart'; -class IRoute { +typedef IRoutePageBuilder = Page? Function( + BuildContext context, + IRouteConfig data, +); + +typedef IRouteWidgetBuilder = Widget? Function( + BuildContext context, + IRouteConfig data, +); + +abstract class IRouteNode { final String path; - final IRoutePageBuilder? builder; - final List children; + final List children; - const IRoute({ + const IRouteNode({ required this.path, - this.children = const [], - this.builder, + required this.children, }); - @override - String toString() { - return 'IRoute{path: $path, children: $children}'; + Page? createPage(BuildContext context, IRouteConfig config); + + List find(String input,) { + return findNodes(this, Uri.parse(input), 0, '/', []); } - IRoute? match(String path) { - return matchRoute(this, path); - } - - List find(String input){ - String fixInput = input.substring(1); - List nodes = findNodes(this,fixInput,0,'/',[]); - if(nodes.isNotEmpty&&nodes.last.path!=input){ - return []; - } - return nodes; - } - - List findNodes(IRoute node,String input,int deep,String prefix,List result){ - String separator = '/'; - - List parts = input.split(separator); - if(deep>parts.length-1){ + List findNodes( + IRouteNode node, + Uri uri, + int deep, + String prefix, + List result, + ) { + List parts = uri.pathSegments; + if (deep > parts.length - 1) { return result; } String target = parts[deep]; - if(node.children.isNotEmpty){ - List nodes = node.children.where((e) => e.path==prefix+target).toList(); + if (node.children.isNotEmpty) { + target = prefix + target; + List nodes = node.children.where((e) => e.path == target).toList(); bool match = nodes.isNotEmpty; - if(match){ - IRoute matched = nodes.first; + if (match) { + IRouteNode matched = nodes.first; result.add(matched); - String nextPrefix = '${matched.path}$separator'; - findNodes(matched, input, ++deep,nextPrefix,result); + String nextPrefix = '${matched.path}/'; + findNodes(matched, uri, ++deep, nextPrefix, result); + }else{ + result.add(NotFindNode(path: target)); + return result; } - }else{ - return result; } return result; } +} - // List findNodes(IRoute node,String input,int deep,String prefix,List result){ - // String separator = '/'; - // List parts = input.split(separator); - // if(deep>parts.length-1){ - // return result; - // } - // String target = parts[deep]; - // if(node.children.isNotEmpty){ - // List nodes = node.children.where((e) => e.path==prefix+target).toList(); - // bool match = nodes.isNotEmpty; - // if(match){ - // IRoute matched = nodes.first; - // result.add(matched); - // String nextPrefix = '${matched.path}$separator'; - // findNodes(matched, input, ++deep,nextPrefix,result); - // } - // }else{ - // return result; - // } - // return result; - // } +/// 优先调用 [pageBuilder] 构建 Page +/// 没有 [pageBuilder] 时, 使用 [widgetBuilder] 构建组件 +/// 没有 [pageBuilder] 和 [widgetBuilder] 时, 使用 [widget] 构建组件 +class IRoute extends IRouteNode { + final IRoutePageBuilder? pageBuilder; + final IRouteWidgetBuilder? widgetBuilder; + final Widget? widget; + final Map? mata; - IRoute? matchRoute(IRoute route, String path) { - if (route.path == path) { - return route; - } else { - if (route.children.isNotEmpty) { - for (int i = 0; i < route.children.length; i++) { - IRoute current = route.children[i]; - IRoute? target = matchRoute(current, path); - if (target != null) { - return target; - } - } - } else { - return null; - } + const IRoute({ + required super.path, + super.children = const [], + this.mata, + this.widget, + this.pageBuilder, + this.widgetBuilder, + }); + + @override + Page? createPage(BuildContext context, IRouteConfig config) { + if (pageBuilder != null) { + return pageBuilder!(context, config); + } + Widget? child; + if (widgetBuilder != null) { + child = widgetBuilder!(context, config); + } + child ??= widget; + if (child != null) { + return MaterialPage(child: child, key: config.pageKey); } return null; } } -typedef IRoutePageBuilder = Page? Function( - BuildContext context, IRouteData data); -class IRouteData { - final Object? extra; - final bool forResult; - final Uri uri; - final bool keepAlive; - IRouteData({ - this.extra, - required this.uri, - this.forResult = false, - this.keepAlive = false, - }); +class NotFindNode extends IRouteNode{ + NotFindNode({required super.path, super.children= const[]}); + + @override + Page? createPage(BuildContext context, IRouteConfig config) { + return null; + } } -IRoute root = IRoute(path: 'root', children: kDestinationsIRoutes); - -List kDestinationsIRoutes = [ - IRoute( - path: '/color', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color'), - child: ColorPage(), - ); - }, - children: [ - IRoute( - path: '/color/detail', - builder: (ctx, data) { - final Map queryParams = data.uri.queryParameters; - String? selectedColor = queryParams['color']; - Color color = Colors.black; - if (selectedColor != null) { - color = Color(int.parse(selectedColor, radix: 16)); - } else if (data.extra is Color) { - color = data.extra as Color; - } - return FadeTransitionPage( - key: const ValueKey('/color/detail'), - child: ColorDetailPage(color: color), - ); - }, - ), - IRoute( - path: '/color/add', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/color/add'), - child: ColorAddPage(), - ); - }), - ], - ), - IRoute( - path: '/counter', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/counter'), - child: CounterPage(), - ); - }), - IRoute( - path: '/user', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/user'), - child: UserPage(), - ); - }), - IRoute( - path: '/settings', - builder: (ctx, data) { - return const FadeTransitionPage( - key: ValueKey('/settings'), - child: SettingPage(), - ); - }), -]; diff --git a/lib/v7/app/navigation/router/iroute_config.dart b/lib/v7/app/navigation/router/iroute_config.dart new file mode 100644 index 0000000..36a58ed --- /dev/null +++ b/lib/v7/app/navigation/router/iroute_config.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class IRouteConfig { + final Object? extra; + final bool forResult; + final Uri uri; + final bool keepAlive; + final bool recordHistory; + + const IRouteConfig({ + this.extra, + required this.uri, + this.forResult = false, + this.keepAlive = false, + this.recordHistory = false, + }); + + String get path => uri.path; + + IRouteConfig copyWith({ + Object? extra, + bool? forResult, + bool? keepAlive, + bool? recordHistory, + }) => + IRouteConfig( + extra: extra ?? this.extra, + forResult: forResult ?? this.forResult, + keepAlive: keepAlive ?? this.keepAlive, + recordHistory: recordHistory ?? this.recordHistory, + uri: uri, + ); + + ValueKey get pageKey => ValueKey(path); +} diff --git a/lib/v7/app/navigation/router/route_history.dart b/lib/v7/app/navigation/router/route_history.dart deleted file mode 100644 index 02b5430..0000000 --- a/lib/v7/app/navigation/router/route_history.dart +++ /dev/null @@ -1,6 +0,0 @@ -class RouteHistory{ - final String path; - final Object? extra; - - RouteHistory(this.path, { this.extra}); -} \ No newline at end of file diff --git a/lib/v7/app/navigation/router/route_history_manager.dart b/lib/v7/app/navigation/router/route_history_manager.dart index 2709fea..8844898 100644 --- a/lib/v7/app/navigation/router/route_history_manager.dart +++ b/lib/v7/app/navigation/router/route_history_manager.dart @@ -1,12 +1,47 @@ -// import 'route_history.dart'; -// -// class RouteHistoryManager{ -// final List _histories = []; -// final List _backHistories = []; -// -// List get histories => _histories.reversed.toList(); -// -// bool get hasHistory => _histories.length > 1; -// -// bool get hasBackHistory => _backHistories.isNotEmpty; -// } \ No newline at end of file +import 'iroute_config.dart'; + +typedef OnRouteChange = void Function(IRouteConfig config); + +class RouteHistoryManager{ + final List _histories = []; + final List _backHistories = []; + + List get histories => _histories.reversed.toList(); + + bool get hasHistory => _histories.length > 1; + + bool get hasBackHistory => _backHistories.isNotEmpty; + + /// 将 [config] 加入历史记录 + void recode(IRouteConfig config){ + if (_histories.isNotEmpty && config.path == _histories.last.path) return; + _histories.add(config); + } + + /// 历史回退操作 + /// 将当前顶层移除,并加入 [_backHistories] 撤销列表 + /// 并转到前一路径 [_histories.last] + void back(OnRouteChange callback) { + if (!hasHistory) return; + IRouteConfig top = _histories.removeLast(); + _backHistories.add(top); + if (_histories.isNotEmpty) { + callback(_histories.last); + } + } + + /// 撤销回退操作 + /// 取出回退列表的最后元素,跳转到该路径 + void revocation(OnRouteChange callback) { + IRouteConfig target = _backHistories.removeLast(); + callback(target); + } + + void close(int index) { + _histories.removeAt(index); + } + + void clear() { + _histories.clear(); + } +} \ No newline at end of file diff --git a/lib/v7/app/navigation/router/routes.dart b/lib/v7/app/navigation/router/routes.dart new file mode 100644 index 0000000..0400bde --- /dev/null +++ b/lib/v7/app/navigation/router/routes.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'iroute_config.dart'; +import 'iroute.dart'; +import '../../../pages/color/color_add_page.dart'; +import '../../../pages/color/color_detail_page.dart'; +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'; + + +IRoute rootRoute = const IRoute( + path: 'root', + children: [ + IRoute( + path: '/color', + widget: ColorPage(), + children: [ + IRoute(path: '/color/detail', widgetBuilder: _buildColorDetail), + IRoute(path: '/color/add', widget: ColorAddPage()), + ], + ), + IRoute(path: '/counter', widget: CounterPage()), + IRoute(path: '/sort', widget: SortPage()), + IRoute(path: '/user', widget: UserPage()), + IRoute(path: '/settings', widget: SettingPage()), + ], +); + +Widget? _buildColorDetail(BuildContext context, IRouteConfig data) { + final Map queryParams = data.uri.queryParameters; + String? selectedColor = queryParams['color']; + Color color = Colors.black; + if (selectedColor != null) { + color = Color(int.parse(selectedColor, radix: 16)); + } else if (data.extra is Color) { + color = data.extra as Color; + } + return ColorDetailPage(color: color); +} diff --git a/lib/v7/app/navigation/router/views/not_find_view.dart b/lib/v7/app/navigation/router/views/not_find_view.dart new file mode 100644 index 0000000..ccefa16 --- /dev/null +++ b/lib/v7/app/navigation/router/views/not_find_view.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class NotFindPage extends StatelessWidget { + const NotFindPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Material( + child: Center( + child: Wrap( + spacing: 16, + crossAxisAlignment: WrapCrossAlignment.center, + direction: Axis.vertical, + children: [ + Icon(Icons.nearby_error,size: 64, color: Colors.redAccent), + Text( + '404 Page Not Find', + style: TextStyle(fontSize: 24, color: Colors.grey), + ), + ], + ), + ), + ); + } +} diff --git a/lib/v7/app/navigation/transition/fade_page_transitions_builder.dart b/lib/v7/app/navigation/transition/fade_page_transitions_builder.dart new file mode 100644 index 0000000..0587ecc --- /dev/null +++ b/lib/v7/app/navigation/transition/fade_page_transitions_builder.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class FadePageTransitionsBuilder extends PageTransitionsBuilder { + + const FadePageTransitionsBuilder(); + + @override + Widget buildTransitions( + PageRoute? route, + BuildContext? context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return _FadePagePageTransition( + animation: animation, + secondaryAnimation: secondaryAnimation, + child: child, + ); + } +} + +class _FadePagePageTransition extends StatelessWidget { + + const _FadePagePageTransition({ + required this.animation, + required this.secondaryAnimation, + required this.child, + }); + + final Animation animation; + final Animation secondaryAnimation; + final Widget child; + + @override + Widget build(BuildContext context) { + var curveTween = CurveTween(curve: Curves.easeIn); + return FadeTransition( + opacity: animation.drive(curveTween), + child: child, + ); + } +} \ No newline at end of file diff --git a/lib/v7/app/navigation/transition/fade_transition_page.dart b/lib/v7/app/navigation/transition/fade_transition_page.dart index 552171b..fc4e6e6 100644 --- a/lib/v7/app/navigation/transition/fade_transition_page.dart +++ b/lib/v7/app/navigation/transition/fade_transition_page.dart @@ -15,8 +15,7 @@ class FadeTransitionPage extends Page { }); @override - Route createRoute(BuildContext context) => - PageBasedFadeTransitionRoute(this); + Route createRoute(BuildContext context) => PageBasedFadeTransitionRoute(this); } class PageBasedFadeTransitionRoute extends PageRoute { diff --git a/lib/v7/app/navigation/views/app_navigation_rail.dart b/lib/v7/app/navigation/views/app_navigation_rail.dart index 759c50f..67a986c 100644 --- a/lib/v7/app/navigation/views/app_navigation_rail.dart +++ b/lib/v7/app/navigation/views/app_navigation_rail.dart @@ -12,10 +12,11 @@ class AppNavigationRail extends StatefulWidget { 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.person), - MenuMeta(label: '设置', icon: Icons.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 @@ -41,21 +42,33 @@ class _AppNavigationRailState extends State { ), tail: Padding( padding: const EdgeInsets.only(bottom: 6.0), - child: Text('V0.0.6',style: TextStyle(color: Colors.white,fontSize: 12),), + child: Text('V0.0.7',style: TextStyle(color: Colors.white,fontSize: 12),), ), backgroundColor: const Color(0xff3975c6), onDestinationSelected: _onDestinationSelected, - selectedIndex: router.activeIndex, + selectedIndex: activeIndex, ), ); - } + RegExp _segReg = RegExp(r'/\w+'); + + int? get activeIndex{ + String path = router.path; + RegExpMatch? match = _segReg.firstMatch(path); + if(match==null) return null; + String? target = match.group(0); + 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(kDestinationsPaths[index],keepAlive: true); + router.changePath(path,keepAlive: true); }else{ - router.changePath(kDestinationsPaths[index]); + router.changePath(path); } } diff --git a/lib/v7/app/navigation/views/app_top_bar/app_top_bar.dart b/lib/v7/app/navigation/views/app_top_bar/app_top_bar.dart index fabe4d4..ad7d875 100644 --- a/lib/v7/app/navigation/views/app_top_bar/app_top_bar.dart +++ b/lib/v7/app/navigation/views/app_top_bar/app_top_bar.dart @@ -57,6 +57,7 @@ Map kRouteLabelMap = { '/color/add': '添加颜色', '/color/detail': '颜色详情', '/counter': '计数器', + '/sort': '可视化排序算法', '/user': '我的', '/settings': '系统设置', }; diff --git a/lib/v7/app/navigation/views/app_top_bar/history_view_icon.dart b/lib/v7/app/navigation/views/app_top_bar/history_view_icon.dart index e7dd0a5..6ba4926 100644 --- a/lib/v7/app/navigation/views/app_top_bar/history_view_icon.dart +++ b/lib/v7/app/navigation/views/app_top_bar/history_view_icon.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; import '../../router/app_router_delegate.dart'; -import '../../router/route_history.dart'; +import '../../router/iroute_config.dart'; import 'app_top_bar.dart'; class HistoryViewIcon extends StatelessWidget{ @@ -55,7 +55,7 @@ class HistoryViewIcon extends StatelessWidget{ } class HistoryItem extends StatefulWidget { - final RouteHistory history; + final IRouteConfig history; final VoidCallback onPressed; final VoidCallback onDelete; @@ -83,7 +83,7 @@ class _HistoryItemState extends State { const SizedBox( height: 2, ), - Text(kRouteLabelMap[widget.history.path]!), + Text(kRouteLabelMap[widget.history.path]??'未知路由'), ], )), GestureDetector( @@ -124,7 +124,8 @@ class _HistoryPanelState extends State { @override Widget build(BuildContext context) { - if(router.histories.isEmpty){ + List histories = router.historyManager.histories; + if(histories.isEmpty){ return const Center( child: Text( '暂无浏览历史记录', @@ -135,18 +136,18 @@ class _HistoryPanelState extends State { return ListView.builder( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), itemExtent: 46, - itemCount: router.histories.length, + itemCount: histories.length, itemBuilder: (_, index) => HistoryItem( onDelete: (){ - int fixIndex = router.histories.length - 1 - index; + int fixIndex = histories.length - 1 - index; router.closeHistory(fixIndex); }, onPressed: (){ - router.toHistory(router.histories[index]); + router.changeRoute(histories[index].copyWith(recordHistory: false)); Navigator.of(context).pop(); }, - history: router.histories[index]), + history: histories[index]), ); } diff --git a/lib/v7/app/navigation/views/app_top_bar/route_history_button.dart b/lib/v7/app/navigation/views/app_top_bar/route_history_button.dart index e02439f..3ee6513 100644 --- a/lib/v7/app/navigation/views/app_top_bar/route_history_button.dart +++ b/lib/v7/app/navigation/views/app_top_bar/route_history_button.dart @@ -25,8 +25,8 @@ class _RouteHistoryButtonState extends State { @override Widget build(BuildContext context) { - bool hasHistory = router.hasHistory; - bool hasBackHistory = router.hasBackHistory; + bool hasHistory = router.historyManager.hasHistory; + bool hasBackHistory = router.historyManager.hasBackHistory; Color activeColor = const Color(0xff9195AC); Color inActiveColor = const Color(0xffC7CAD5); Color historyColor = hasHistory?activeColor:inActiveColor; diff --git a/lib/v7/app/unit_app.dart b/lib/v7/app/unit_app.dart index 1a21114..33f59bf 100644 --- a/lib/v7/app/unit_app.dart +++ b/lib/v7/app/unit_app.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; -import 'navigation/router/app_router_delegate.dart'; +import '../pages/sort/provider/state.dart'; +import 'navigation/transition/fade_page_transitions_builder.dart'; import 'navigation/views/app_navigation.dart'; -import 'navigation/views/app_navigation_rail.dart'; + class UnitApp extends StatelessWidget { const UnitApp({super.key}); @@ -9,20 +10,32 @@ 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, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.macOS: FadePageTransitionsBuilder(), + TargetPlatform.windows: FadePageTransitionsBuilder(), + TargetPlatform.linux: FadePageTransitionsBuilder(), + } + ), + 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/v7/pages/color/color_page.dart b/lib/v7/pages/color/color_page.dart index e5e6981..39bcc1b 100644 --- a/lib/v7/pages/color/color_page.dart +++ b/lib/v7/pages/color/color_page.dart @@ -38,12 +38,12 @@ class _ColorPageState extends State { void _selectColor(Color color){ // String value = color.value.toRadixString(16); // router.path = '/color/detail?color=$value'; - router.changePath('/color/detail',extra: color); + router.changePath('/color/detail_error',extra: color); } void _toAddPage() async { - Color? color = await router.changePath('/color/add',forResult: true); + Color? color = await router.changePath('/color/add',forResult: true,recordHistory: false); if (color != null) { setState(() { _colors.add(color); diff --git a/lib/v6_/pages/sort/functions.dart b/lib/v7/pages/sort/functions.dart similarity index 100% rename from lib/v6_/pages/sort/functions.dart rename to lib/v7/pages/sort/functions.dart diff --git a/lib/v6_/pages/sort/functions/bubble.dart b/lib/v7/pages/sort/functions/bubble.dart similarity index 100% rename from lib/v6_/pages/sort/functions/bubble.dart rename to lib/v7/pages/sort/functions/bubble.dart diff --git a/lib/v6_/pages/sort/functions/cocktail.dart b/lib/v7/pages/sort/functions/cocktail.dart similarity index 100% rename from lib/v6_/pages/sort/functions/cocktail.dart rename to lib/v7/pages/sort/functions/cocktail.dart diff --git a/lib/v6_/pages/sort/functions/comb.dart b/lib/v7/pages/sort/functions/comb.dart similarity index 100% rename from lib/v6_/pages/sort/functions/comb.dart rename to lib/v7/pages/sort/functions/comb.dart diff --git a/lib/v6_/pages/sort/functions/cycle.dart b/lib/v7/pages/sort/functions/cycle.dart similarity index 100% rename from lib/v6_/pages/sort/functions/cycle.dart rename to lib/v7/pages/sort/functions/cycle.dart diff --git a/lib/v6_/pages/sort/functions/gnome.dart b/lib/v7/pages/sort/functions/gnome.dart similarity index 100% rename from lib/v6_/pages/sort/functions/gnome.dart rename to lib/v7/pages/sort/functions/gnome.dart diff --git a/lib/v6_/pages/sort/functions/heap.dart b/lib/v7/pages/sort/functions/heap.dart similarity index 100% rename from lib/v6_/pages/sort/functions/heap.dart rename to lib/v7/pages/sort/functions/heap.dart diff --git a/lib/v6_/pages/sort/functions/insertion.dart b/lib/v7/pages/sort/functions/insertion.dart similarity index 100% rename from lib/v6_/pages/sort/functions/insertion.dart rename to lib/v7/pages/sort/functions/insertion.dart diff --git a/lib/v6_/pages/sort/functions/merage.dart b/lib/v7/pages/sort/functions/merage.dart similarity index 100% rename from lib/v6_/pages/sort/functions/merage.dart rename to lib/v7/pages/sort/functions/merage.dart diff --git a/lib/v6_/pages/sort/functions/oddEven.dart b/lib/v7/pages/sort/functions/oddEven.dart similarity index 100% rename from lib/v6_/pages/sort/functions/oddEven.dart rename to lib/v7/pages/sort/functions/oddEven.dart diff --git a/lib/v6_/pages/sort/functions/pigeonHole.dart b/lib/v7/pages/sort/functions/pigeonHole.dart similarity index 100% rename from lib/v6_/pages/sort/functions/pigeonHole.dart rename to lib/v7/pages/sort/functions/pigeonHole.dart diff --git a/lib/v6_/pages/sort/functions/quick.dart b/lib/v7/pages/sort/functions/quick.dart similarity index 100% rename from lib/v6_/pages/sort/functions/quick.dart rename to lib/v7/pages/sort/functions/quick.dart diff --git a/lib/v6_/pages/sort/functions/selection.dart b/lib/v7/pages/sort/functions/selection.dart similarity index 100% rename from lib/v6_/pages/sort/functions/selection.dart rename to lib/v7/pages/sort/functions/selection.dart diff --git a/lib/v6_/pages/sort/functions/shell.dart b/lib/v7/pages/sort/functions/shell.dart similarity index 100% rename from lib/v6_/pages/sort/functions/shell.dart rename to lib/v7/pages/sort/functions/shell.dart diff --git a/lib/v6_/pages/sort/provider/sort_config.dart b/lib/v7/pages/sort/provider/sort_config.dart similarity index 100% rename from lib/v6_/pages/sort/provider/sort_config.dart rename to lib/v7/pages/sort/provider/sort_config.dart diff --git a/lib/v6_/pages/sort/provider/state.dart b/lib/v7/pages/sort/provider/state.dart similarity index 93% rename from lib/v6_/pages/sort/provider/state.dart rename to lib/v7/pages/sort/provider/state.dart index e60e208..90803cf 100644 --- a/lib/v6_/pages/sort/provider/state.dart +++ b/lib/v7/pages/sort/provider/state.dart @@ -32,6 +32,10 @@ class SortState with ChangeNotifier{ notifyListeners(); } + void selectName(String name){ + if(name==config.name) return; + config = config.copyWith(name: name); + } void reset(){ data.clear(); diff --git a/lib/v7/pages/sort/sort_page.dart b/lib/v7/pages/sort/sort_page.dart deleted file mode 100644 index d440071..0000000 --- a/lib/v7/pages/sort/sort_page.dart +++ /dev/null @@ -1,859 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; - -class SortPage extends StatefulWidget { - const SortPage({Key? key}) : super(key: key); - - @override - State createState() => _SortPageState(); -} - -class _SortPageState extends State { - //存放随机数组 - List numbers = []; - - //订阅流 - StreamController> streamController = StreamController(); - String currentSort = 'bubble'; - - //柱子的数量 -> 生成排序数组的长度 - double sampleSize = 0; - - //是否排序 - bool isSorted = false; - - //是否在排序中 - 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)); - } - 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); - } - - // 依次取出最大堆的根节点(最大值),并进行堆化 - 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()); // 延迟操作,用于可视化排序过程 - streamController.add(numbers); - } - - ///插入排序 - 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); - } - } - - checkAndResetIfSorted() async { - if (isSorted) { - reset(); - await Future.delayed(const Duration(milliseconds: 200)); - } - } - - sort() async { - setState(() { - isSorting = true; - }); - - await checkAndResetIfSorted(); - - 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; - } - - stopwatch.stop(); - - print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms."); - setState(() { - isSorting = false; - isSorted = true; - }); - } - - setSort(String type) { - setState(() { - currentSort = type; - }); - } - - @override - void initState() { - super.initState(); - // reset(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - sampleSize = MediaQuery.of(context).size.width / 2; - for (int i = 0; i < sampleSize; ++i) { - //随机往数组中填值 - numbers.add(Random().nextInt(500)); - } - setState(() {}); - } - - @override - void dispose() { - streamController.close(); - super.dispose(); - } - - @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); - }, - ) - ], - ), - 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++; - return CustomPaint( - painter: BarPainter( - width: MediaQuery.of(context).size.width / sampleSize, - value: num, - index: counter, - ), - ); - }).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), - ), - ), - ], - ), - ), - ); - } -} - -class BarPainter extends CustomPainter { - //宽度 - final double width; - - //高度(数组中对应的值) - final int value; - - //位置索引 - final int index; - - BarPainter({required this.width, required this.value, required this.index}); - - @override - void paint(Canvas canvas, Size size) { - Paint paint = Paint(); - if (value < 500 * .10) { - paint.color = Colors.blue.shade100; - } else if (value < 500 * .20) { - paint.color = Colors.blue.shade200; - } else if (value < 500 * .30) { - paint.color = Colors.blue.shade300; - } else if (value < 500 * .40) { - paint.color = Colors.blue.shade400; - } else if (value < 500 * .50) { - paint.color = Colors.blue.shade500; - } else if (value < 500 * .60) { - paint.color = Colors.blue.shade600; - } else if (value < 500 * .70) { - paint.color = Colors.blue.shade700; - } else if (value < 500 * .80) { - paint.color = Colors.blue.shade800; - } else if (value < 500 * .90) { - paint.color = Colors.blue.shade900; - } else { - paint.color = const Color(0xFF011E51); - } - - paint.strokeWidth = width; - paint.strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset(index * width, 0), - Offset( - index * width, - value.ceilToDouble(), - ), - paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/v7/pages/sort/views/code_page/code_page.dart b/lib/v7/pages/sort/views/code_page/code_page.dart new file mode 100644 index 0000000..7d8d4c9 --- /dev/null +++ b/lib/v7/pages/sort/views/code_page/code_page.dart @@ -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), + )); + } +} diff --git a/lib/v6_/pages/sort/views/data_painter.dart b/lib/v7/pages/sort/views/data_painter.dart similarity index 100% rename from lib/v6_/pages/sort/views/data_painter.dart rename to lib/v7/pages/sort/views/data_painter.dart diff --git a/lib/v6_/pages/sort/views/sort_bar.dart b/lib/v7/pages/sort/views/sort_bar.dart similarity index 100% rename from lib/v6_/pages/sort/views/sort_bar.dart rename to lib/v7/pages/sort/views/sort_bar.dart diff --git a/lib/v6_/pages/sort/views/sort_button.dart b/lib/v7/pages/sort/views/sort_button.dart similarity index 51% rename from lib/v6_/pages/sort/views/sort_button.dart rename to lib/v7/pages/sort/views/sort_button.dart index ec2b30a..cab02aa 100644 --- a/lib/v6_/pages/sort/views/sort_button.dart +++ b/lib/v7/pages/sort/views/sort_button.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../provider/state.dart'; @@ -10,30 +11,47 @@ class SortButton extends StatelessWidget { 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 = Icons.refresh; + icon = CupertinoIcons.repeat; color = Colors.black; action = state.reset; + text = '点击重置'; break; } - return GestureDetector( - onTap: action, - child: Icon( - icon, - color: color, + 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),), + + ], + ), ), ); } diff --git a/lib/v7/pages/sort/views/sort_page.dart b/lib/v7/pages/sort/views/sort_page.dart new file mode 100644 index 0000000..fe1cba7 --- /dev/null +++ b/lib/v7/pages/sort/views/sort_page.dart @@ -0,0 +1,164 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.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 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(), + ], + ), + ), + 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 createState() => _NavigatorScopeState(); +} + +class _NavigatorScopeState extends State { + + @override + Widget build(BuildContext context) { + SortState state = SortStateScope.of(context); + List 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 route, result) { + return route.didPop(result); + } +} + + + +class SortSelectorPanel extends StatelessWidget { + final String active; + final ValueChanged onSelected; + final List 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 createState() => _SortItemTileState(); +} + +class _SortItemTileState extends State { + @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 + ), + ), + ), + ), + ), + ); + } +} + diff --git a/lib/v6_/pages/sort/sort_setting.dart b/lib/v7/pages/sort/views/sort_setting.dart similarity index 97% rename from lib/v6_/pages/sort/sort_setting.dart rename to lib/v7/pages/sort/views/sort_setting.dart index 3b61da8..9a96a88 100644 --- a/lib/v6_/pages/sort/sort_setting.dart +++ b/lib/v7/pages/sort/views/sort_setting.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'provider/sort_config.dart'; -import 'provider/state.dart'; +import '../provider/sort_config.dart'; +import '../provider/state.dart'; class SortSettings extends StatefulWidget { diff --git a/lib/v5_/app.dart b/lib/v8/app.dart similarity index 100% rename from lib/v5_/app.dart rename to lib/v8/app.dart diff --git a/lib/v6_result_/app/navigation/router/app_router_delegate.dart b/lib/v8/app/navigation/router/app_router_delegate.dart similarity index 56% rename from lib/v6_result_/app/navigation/router/app_router_delegate.dart rename to lib/v8/app/navigation/router/app_router_delegate.dart index 45641af..be3d580 100644 --- a/lib/v6_result_/app/navigation/router/app_router_delegate.dart +++ b/lib/v8/app/navigation/router/app_router_delegate.dart @@ -1,16 +1,10 @@ import 'dart:async'; - import 'package:flutter/material.dart'; - import 'iroute.dart'; -import 'route_history.dart'; - -const List kDestinationsPaths = [ - '/color', - '/counter', - '/user', - '/settings', -]; +import 'iroute_config.dart'; +import 'route_history_manager.dart'; +import 'routes.dart'; +import 'views/not_find_view.dart'; AppRouterDelegate router = AppRouterDelegate(); @@ -19,63 +13,67 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { String get path => _path; - AppRouterDelegate() { - // keepAlivePath.add('/color'); + final IRoutePageBuilder? notFindPageBuilder; + + AppRouterDelegate({this.notFindPageBuilder}) { + _historyManager.recode(IRouteConfig(uri: Uri.parse(path))); } - 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; - return null; - } + Page _defaultNotFindPageBuilder(_, __) => const MaterialPage( + child: Material(child: NotFindPage()), + ); - final List _histories = []; - final List _backHistories = []; + final RouteHistoryManager _historyManager = RouteHistoryManager(); - bool get hasHistory => _histories.length > 1; - - bool get hasBackHistory => _backHistories.isNotEmpty; - - final Map> _completerMap = {}; - - Completer? completer; - - final Map _pathExtraMap = {}; - - final List keepAlivePath = []; + RouteHistoryManager get historyManager => _historyManager; /// 历史回退操作 - /// 将当前顶层移除,并加入 _backHistories 撤销列表 - /// 并转到前一路径 - void back() { - if (!hasHistory) return; - RouteHistory top = _histories.removeLast(); - _backHistories.add(top); - if (_histories.isNotEmpty) { - _path = _histories.last.path; - if(_histories.last.extra!=null){ - _pathExtraMap[_path] = _histories.last.extra; - } - notifyListeners(); - } - } + /// 详见: [RouteHistoryManager.back] + void back() => _historyManager.back(changeRoute); /// 撤销回退操作 - /// 取出回退列表的最后元素,跳转到该路径 - void revocation() { - RouteHistory target = _backHistories.removeLast(); - _path = target.path; - if(target.extra!=null){ - _pathExtraMap[_path] = target.extra; - } - _histories.add(target); + /// 详见: [RouteHistoryManager.revocation] + void revocation() => _historyManager.revocation(changeRoute); + + void closeHistory(int index) { + _historyManager.close(index); notifyListeners(); - // changePath( - // target.path, - // extra: target.extra, - // ); + } + + void clearHistory() { + _historyManager.clear(); + notifyListeners(); + } + + final Map> _completerMap = {}; + final Map _pathExtraMap = {}; + final List keepAlivePath = []; + + FutureOr changeRoute(IRouteConfig config) { + String value = config.uri.path; + if (_path == value) null; + 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; + } } FutureOr changePath( @@ -83,38 +81,15 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { bool forResult = false, Object? extra, bool keepAlive = false, - bool recordHistory = false, + bool recordHistory = true, }) { - if (forResult) { - _completerMap[value] = Completer(); - } - if (keepAlive) { - if (keepAlivePath.contains(value)) { - keepAlivePath.remove(value); - } - keepAlivePath.add(value); - } - if (extra != null) { - _pathExtraMap[value] = extra; - } - - if (recordHistory) { - _histories.add(RouteHistory( - value, - extra: extra, - )); - } - - path = value; - if (forResult) { - return _completerMap[value]!.future; - } - } - - set path(String value) { - if (_path == value) return; - _path = value; - notifyListeners(); + return changeRoute(IRouteConfig( + uri: Uri.parse(value), + forResult: forResult, + extra: extra, + keepAlive: keepAlive, + recordHistory: recordHistory, + )); } @override @@ -147,23 +122,26 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { List _buildPageByPathFromTree(BuildContext context, String path) { List result = []; - List iRoutes = root.find(path); + List iRoutes = rootRoute.find(path); if (iRoutes.isNotEmpty) { for (int i = 0; i < iRoutes.length; i++) { - IRoute iroute = iRoutes[i]; + IRouteNode iroute = iRoutes[i]; String path = iroute.path; Object? extra = _pathExtraMap[path]; bool keepAlive = keepAlivePath.contains(path); bool forResult = _completerMap.containsKey(path); - Page? page = iroute.builder?.call( - context, - IRouteData( - uri: Uri.parse(path), - extra: extra, - keepAlive: keepAlive, - forResult: forResult, - ), + IRouteConfig config = IRouteConfig( + uri: Uri.parse(path), + extra: extra, + keepAlive: keepAlive, + forResult: forResult, ); + Page? page; + if (iroute is NotFindNode) { + page = (notFindPageBuilder ?? _defaultNotFindPageBuilder)(context, config); + } else { + page = iroute.createPage(context, config); + } if (page != null) { result.add(page); } @@ -183,8 +161,7 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { _completerMap[path]?.complete(result); _completerMap.remove(path); } - - path = backPath(path); + changePath(backPath(path), recordHistory: false); return route.didPop(result); } diff --git a/lib/v8/app/navigation/router/iroute.dart b/lib/v8/app/navigation/router/iroute.dart new file mode 100644 index 0000000..4026e1c --- /dev/null +++ b/lib/v8/app/navigation/router/iroute.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; + + +import 'iroute_config.dart'; + +typedef IRoutePageBuilder = Page? Function( + BuildContext context, + IRouteConfig data, +); + +typedef IRouteWidgetBuilder = Widget? Function( + BuildContext context, + IRouteConfig data, +); + +abstract class IRouteNode { + final String path; + final List children; + + const IRouteNode({ + required this.path, + required this.children, + }); + + Page? createPage(BuildContext context, IRouteConfig config); + + List find(String input,) { + return findNodes(this, Uri.parse(input), 0, '/', []); + } + + List findNodes( + IRouteNode node, + Uri uri, + int deep, + String prefix, + List result, + ) { + List parts = uri.pathSegments; + if (deep > parts.length - 1) { + return result; + } + String target = parts[deep]; + if (node.children.isNotEmpty) { + target = prefix + target; + List 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{ + result.add(NotFindNode(path: target)); + return result; + } + } + return result; + } +} + +/// 优先调用 [pageBuilder] 构建 Page +/// 没有 [pageBuilder] 时, 使用 [widgetBuilder] 构建组件 +/// 没有 [pageBuilder] 和 [widgetBuilder] 时, 使用 [widget] 构建组件 +class IRoute extends IRouteNode { + final IRoutePageBuilder? pageBuilder; + final IRouteWidgetBuilder? widgetBuilder; + final Widget? widget; + + const IRoute({ + required super.path, + super.children = const [], + this.widget, + this.pageBuilder, + this.widgetBuilder, + }); + + @override + Page? createPage(BuildContext context, IRouteConfig config) { + if (pageBuilder != null) { + return pageBuilder!(context, config); + } + Widget? child; + if (widgetBuilder != null) { + child = widgetBuilder!(context, config); + } + child ??= widget; + if (child != null) { + return MaterialPage(child: child, key: config.pageKey); + } + return null; + } +} + +/// 未知路由 +class NotFindNode extends IRouteNode{ + NotFindNode({required super.path, super.children= const[]}); + + @override + Page? createPage(BuildContext context, IRouteConfig config) { + return null; + } +} + +class CellIRouter extends IRoute { + final CellBuilder cellBuilder; + + CellIRouter( + {required super.path, + super.pageBuilder, + super.children, + required this.cellBuilder}); +} + +typedef CellBuilder = Widget Function(BuildContext context, Widget child); + diff --git a/lib/v8/app/navigation/router/iroute_config.dart b/lib/v8/app/navigation/router/iroute_config.dart new file mode 100644 index 0000000..36a58ed --- /dev/null +++ b/lib/v8/app/navigation/router/iroute_config.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class IRouteConfig { + final Object? extra; + final bool forResult; + final Uri uri; + final bool keepAlive; + final bool recordHistory; + + const IRouteConfig({ + this.extra, + required this.uri, + this.forResult = false, + this.keepAlive = false, + this.recordHistory = false, + }); + + String get path => uri.path; + + IRouteConfig copyWith({ + Object? extra, + bool? forResult, + bool? keepAlive, + bool? recordHistory, + }) => + IRouteConfig( + extra: extra ?? this.extra, + forResult: forResult ?? this.forResult, + keepAlive: keepAlive ?? this.keepAlive, + recordHistory: recordHistory ?? this.recordHistory, + uri: uri, + ); + + ValueKey get pageKey => ValueKey(path); +} diff --git a/lib/v8/app/navigation/router/route_history_manager.dart b/lib/v8/app/navigation/router/route_history_manager.dart new file mode 100644 index 0000000..8844898 --- /dev/null +++ b/lib/v8/app/navigation/router/route_history_manager.dart @@ -0,0 +1,47 @@ +import 'iroute_config.dart'; + +typedef OnRouteChange = void Function(IRouteConfig config); + +class RouteHistoryManager{ + final List _histories = []; + final List _backHistories = []; + + List get histories => _histories.reversed.toList(); + + bool get hasHistory => _histories.length > 1; + + bool get hasBackHistory => _backHistories.isNotEmpty; + + /// 将 [config] 加入历史记录 + void recode(IRouteConfig config){ + if (_histories.isNotEmpty && config.path == _histories.last.path) return; + _histories.add(config); + } + + /// 历史回退操作 + /// 将当前顶层移除,并加入 [_backHistories] 撤销列表 + /// 并转到前一路径 [_histories.last] + void back(OnRouteChange callback) { + if (!hasHistory) return; + IRouteConfig top = _histories.removeLast(); + _backHistories.add(top); + if (_histories.isNotEmpty) { + callback(_histories.last); + } + } + + /// 撤销回退操作 + /// 取出回退列表的最后元素,跳转到该路径 + void revocation(OnRouteChange callback) { + IRouteConfig target = _backHistories.removeLast(); + callback(target); + } + + void close(int index) { + _histories.removeAt(index); + } + + void clear() { + _histories.clear(); + } +} \ No newline at end of file diff --git a/lib/v8/app/navigation/router/routes.dart b/lib/v8/app/navigation/router/routes.dart new file mode 100644 index 0000000..0400bde --- /dev/null +++ b/lib/v8/app/navigation/router/routes.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'iroute_config.dart'; +import 'iroute.dart'; +import '../../../pages/color/color_add_page.dart'; +import '../../../pages/color/color_detail_page.dart'; +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'; + + +IRoute rootRoute = const IRoute( + path: 'root', + children: [ + IRoute( + path: '/color', + widget: ColorPage(), + children: [ + IRoute(path: '/color/detail', widgetBuilder: _buildColorDetail), + IRoute(path: '/color/add', widget: ColorAddPage()), + ], + ), + IRoute(path: '/counter', widget: CounterPage()), + IRoute(path: '/sort', widget: SortPage()), + IRoute(path: '/user', widget: UserPage()), + IRoute(path: '/settings', widget: SettingPage()), + ], +); + +Widget? _buildColorDetail(BuildContext context, IRouteConfig data) { + final Map queryParams = data.uri.queryParameters; + String? selectedColor = queryParams['color']; + Color color = Colors.black; + if (selectedColor != null) { + color = Color(int.parse(selectedColor, radix: 16)); + } else if (data.extra is Color) { + color = data.extra as Color; + } + return ColorDetailPage(color: color); +} diff --git a/lib/v8/app/navigation/router/views/not_find_view.dart b/lib/v8/app/navigation/router/views/not_find_view.dart new file mode 100644 index 0000000..ccefa16 --- /dev/null +++ b/lib/v8/app/navigation/router/views/not_find_view.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class NotFindPage extends StatelessWidget { + const NotFindPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Material( + child: Center( + child: Wrap( + spacing: 16, + crossAxisAlignment: WrapCrossAlignment.center, + direction: Axis.vertical, + children: [ + Icon(Icons.nearby_error,size: 64, color: Colors.redAccent), + Text( + '404 Page Not Find', + style: TextStyle(fontSize: 24, color: Colors.grey), + ), + ], + ), + ), + ); + } +} diff --git a/lib/v8/app/navigation/transition/fade_page_transitions_builder.dart b/lib/v8/app/navigation/transition/fade_page_transitions_builder.dart new file mode 100644 index 0000000..0587ecc --- /dev/null +++ b/lib/v8/app/navigation/transition/fade_page_transitions_builder.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class FadePageTransitionsBuilder extends PageTransitionsBuilder { + + const FadePageTransitionsBuilder(); + + @override + Widget buildTransitions( + PageRoute? route, + BuildContext? context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return _FadePagePageTransition( + animation: animation, + secondaryAnimation: secondaryAnimation, + child: child, + ); + } +} + +class _FadePagePageTransition extends StatelessWidget { + + const _FadePagePageTransition({ + required this.animation, + required this.secondaryAnimation, + required this.child, + }); + + final Animation animation; + final Animation secondaryAnimation; + final Widget child; + + @override + Widget build(BuildContext context) { + var curveTween = CurveTween(curve: Curves.easeIn); + return FadeTransition( + opacity: animation.drive(curveTween), + child: child, + ); + } +} \ No newline at end of file diff --git a/lib/v6_result_/app/navigation/transition/fade_transition_page.dart b/lib/v8/app/navigation/transition/fade_transition_page.dart similarity index 93% rename from lib/v6_result_/app/navigation/transition/fade_transition_page.dart rename to lib/v8/app/navigation/transition/fade_transition_page.dart index 552171b..fc4e6e6 100644 --- a/lib/v6_result_/app/navigation/transition/fade_transition_page.dart +++ b/lib/v8/app/navigation/transition/fade_transition_page.dart @@ -15,8 +15,7 @@ class FadeTransitionPage extends Page { }); @override - Route createRoute(BuildContext context) => - PageBasedFadeTransitionRoute(this); + Route createRoute(BuildContext context) => PageBasedFadeTransitionRoute(this); } class PageBasedFadeTransitionRoute extends PageRoute { diff --git a/lib/v5_/app/navigation/transition/no_transition_page.dart b/lib/v8/app/navigation/transition/no_transition_page.dart similarity index 100% rename from lib/v5_/app/navigation/transition/no_transition_page.dart rename to lib/v8/app/navigation/transition/no_transition_page.dart diff --git a/lib/v6_result_/app/navigation/views/app_navigation.dart b/lib/v8/app/navigation/views/app_navigation.dart similarity index 95% rename from lib/v6_result_/app/navigation/views/app_navigation.dart rename to lib/v8/app/navigation/views/app_navigation.dart index adcab6c..5abca82 100644 --- a/lib/v6_result_/app/navigation/views/app_navigation.dart +++ b/lib/v8/app/navigation/views/app_navigation.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../router/app_router_delegate.dart'; import 'app_navigation_rail.dart'; -import 'app_top_bar.dart'; +import 'app_top_bar/app_top_bar.dart'; class AppNavigation extends StatelessWidget { const AppNavigation({super.key}); diff --git a/lib/v5_/app/navigation/views/app_navigation_rail.dart b/lib/v8/app/navigation/views/app_navigation_rail.dart similarity index 61% rename from lib/v5_/app/navigation/views/app_navigation_rail.dart rename to lib/v8/app/navigation/views/app_navigation_rail.dart index cdf945f..67a986c 100644 --- a/lib/v5_/app/navigation/views/app_navigation_rail.dart +++ b/lib/v8/app/navigation/views/app_navigation_rail.dart @@ -12,10 +12,11 @@ class AppNavigationRail extends StatefulWidget { 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.person), - MenuMeta(label: '设置', icon: Icons.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 @@ -41,21 +42,33 @@ class _AppNavigationRailState extends State { ), tail: Padding( padding: const EdgeInsets.only(bottom: 6.0), - child: Text('V0.0.4',style: TextStyle(color: Colors.white,fontSize: 12),), + child: Text('V0.0.7',style: TextStyle(color: Colors.white,fontSize: 12),), ), backgroundColor: const Color(0xff3975c6), onDestinationSelected: _onDestinationSelected, - selectedIndex: router.activeIndex, + selectedIndex: activeIndex, ), ); - } + RegExp _segReg = RegExp(r'/\w+'); + + int? get activeIndex{ + String path = router.path; + RegExpMatch? match = _segReg.firstMatch(path); + if(match==null) return null; + String? target = match.group(0); + 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(kDestinationsPaths[index],keepAlive: true); + router.changePath(path,keepAlive: true); }else{ - router.path = kDestinationsPaths[index]; + router.changePath(path); } } diff --git a/lib/v6_result_/app/navigation/views/app_router_editor.dart b/lib/v8/app/navigation/views/app_top_bar/app_router_editor.dart similarity index 97% rename from lib/v6_result_/app/navigation/views/app_router_editor.dart rename to lib/v8/app/navigation/views/app_top_bar/app_router_editor.dart index 10d5701..40516a7 100644 --- a/lib/v6_result_/app/navigation/views/app_router_editor.dart +++ b/lib/v8/app/navigation/views/app_top_bar/app_router_editor.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; -import '../router/app_router_delegate.dart'; +import '../../router/app_router_delegate.dart'; class AppRouterEditor extends StatefulWidget { final ValueChanged? onSubmit; diff --git a/lib/v6_result_/app/navigation/views/app_top_bar.dart b/lib/v8/app/navigation/views/app_top_bar/app_top_bar.dart similarity index 85% rename from lib/v6_result_/app/navigation/views/app_top_bar.dart rename to lib/v8/app/navigation/views/app_top_bar/app_top_bar.dart index 1b95110..ad7d875 100644 --- a/lib/v6_result_/app/navigation/views/app_top_bar.dart +++ b/lib/v8/app/navigation/views/app_top_bar/app_top_bar.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:iroute/components/components.dart'; -import '../router/app_router_delegate.dart'; +import '../../router/app_router_delegate.dart'; import 'app_router_editor.dart'; +import 'history_view_icon.dart'; +import 'route_history_button.dart'; class AppTopBar extends StatelessWidget { const AppTopBar({super.key}); @@ -19,11 +21,15 @@ class AppTopBar extends StatelessWidget { Expanded( child: Row(children: [ const Spacer(), + RouteHistoryButton(), + const SizedBox(width: 12,), SizedBox( width: 250, child: AppRouterEditor( - onSubmit: (path) => router.path = path, + onSubmit: (path) => router.changePath(path), )), + const SizedBox(width: 12,), + HistoryViewIcon(), const Padding( padding: EdgeInsets.symmetric(vertical: 12.0), child: VerticalDivider( @@ -51,6 +57,7 @@ Map kRouteLabelMap = { '/color/add': '添加颜色', '/color/detail': '颜色详情', '/counter': '计数器', + '/sort': '可视化排序算法', '/user': '我的', '/settings': '系统设置', }; @@ -74,7 +81,7 @@ class _RouterIndicatorState extends State { items: pathToBreadcrumbItems(router.path), onTapItem: (item) { if (item.to != null) { - router.path = item.to!; + router.changePath(item.to!); } }, ); diff --git a/lib/v8/app/navigation/views/app_top_bar/history_view_icon.dart b/lib/v8/app/navigation/views/app_top_bar/history_view_icon.dart new file mode 100644 index 0000000..3390a49 --- /dev/null +++ b/lib/v8/app/navigation/views/app_top_bar/history_view_icon.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:iroute/components/components.dart'; +import '../../router/app_router_delegate.dart'; +import '../../router/iroute.dart'; +import '../../router/iroute_config.dart'; +import 'app_top_bar.dart'; + +class HistoryViewIcon extends StatelessWidget{ + const HistoryViewIcon({super.key}); + + @override + Widget build(BuildContext context) { + + return MouseRegion( + cursor: SystemMouseCursors.click, + child: PopPanel( + offset: const Offset(0, 10), + panel: SizedBox( + height: 350, + child: Column( + children: [ + _buildTopBar(), + const Expanded( + child:HistoryPanel(), + ), + ], + ), + ), + child: const Icon( + Icons.history, + size: 20, + ), + ), + ); + } + + Widget _buildTopBar() { + return Container( + decoration: BoxDecoration( + color: const Color(0xffFAFAFC), + borderRadius: BorderRadius.circular(6), + ), + padding: + const EdgeInsets.only(top: 10, left: 12, right: 12, bottom: 8), + child: Row( + children: [ + const Text( + '浏览历史', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const Spacer(), + TextButton(onPressed: router.clearHistory, child: const Text('清空历史')) + ], + )); + } +} + +class HistoryItem extends StatefulWidget { + final IRouteConfig history; + final VoidCallback onPressed; + final VoidCallback onDelete; + + const HistoryItem({super.key, required this.history, required this.onPressed, required this.onDelete}); + + @override + State createState() => _HistoryItemState(); +} + +class _HistoryItemState extends State { + @override + Widget build(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: widget.onPressed, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.history.path), + const SizedBox( + height: 2, + ), + Text(kRouteLabelMap[widget.history.path]??'未知路由'), + ], + )), + GestureDetector( + onTap: widget.onDelete, + child: const Icon( + Icons.close, + size: 18, + color: Color(0xff8E92A9), + ), + ), + ], + ), + ), + ); + } +} + +class HistoryPanel extends StatefulWidget { + const HistoryPanel({super.key}); + + @override + State createState() => _HistoryPanelState(); +} + +class _HistoryPanelState extends State { + + @override + void initState() { + super.initState(); + router.addListener(_onChange); + } + + @override + void dispose() { + router.removeListener(_onChange); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List histories = router.historyManager.histories; + if(histories.isEmpty){ + return const Center( + child: Text( + '暂无浏览历史记录', + style: TextStyle(fontSize: 14, color: Colors.grey), + ), + ); + } + return ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + itemExtent: 46, + itemCount: histories.length, + itemBuilder: (_, index) => + HistoryItem( + onDelete: (){ + int fixIndex = histories.length - 1 - index; + router.closeHistory(fixIndex); + }, + onPressed: (){ + router.changeRoute(histories[index].copyWith(recordHistory: false)); + Navigator.of(context).pop(); + }, + history: histories[index]), + ); + } + + void _onChange() { + setState(() {}); + } +} diff --git a/lib/v8/app/navigation/views/app_top_bar/route_history_button.dart b/lib/v8/app/navigation/views/app_top_bar/route_history_button.dart new file mode 100644 index 0000000..3ee6513 --- /dev/null +++ b/lib/v8/app/navigation/views/app_top_bar/route_history_button.dart @@ -0,0 +1,58 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:iroute/components/toly_ui/button/hover_icon_button.dart'; +import '../../router/app_router_delegate.dart'; + +class RouteHistoryButton extends StatefulWidget { + const RouteHistoryButton({super.key}); + + @override + State createState() => _RouteHistoryButtonState(); +} + +class _RouteHistoryButtonState extends State { + @override + void initState() { + super.initState(); + router.addListener(_onChange); + } + + @override + void dispose() { + router.removeListener(_onChange); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + bool hasHistory = router.historyManager.hasHistory; + bool hasBackHistory = router.historyManager.hasBackHistory; + Color activeColor = const Color(0xff9195AC); + Color inActiveColor = const Color(0xffC7CAD5); + Color historyColor = hasHistory?activeColor:inActiveColor; + Color backHistoryColor = hasBackHistory?activeColor:inActiveColor; + return Wrap( + children: [ + HoverIconButton( + size: 20, + hoverColor: historyColor, + defaultColor: historyColor, + icon: CupertinoIcons.arrow_left_circle, + onPressed: hasHistory?router.back:null, + ), + const SizedBox(width: 8,), + HoverIconButton( + size: 20, + hoverColor: backHistoryColor, + defaultColor: backHistoryColor, + icon: CupertinoIcons.arrow_right_circle, + onPressed: hasBackHistory?router.revocation:null, + ), + ], + ); + } + + void _onChange() { + setState(() {}); + } +} diff --git a/lib/v6_/app/unit_app.dart b/lib/v8/app/unit_app.dart similarity index 61% rename from lib/v6_/app/unit_app.dart rename to lib/v8/app/unit_app.dart index 36e67ab..33f59bf 100644 --- a/lib/v6_/app/unit_app.dart +++ b/lib/v8/app/unit_app.dart @@ -1,20 +1,30 @@ import 'package:flutter/material.dart'; import '../pages/sort/provider/state.dart'; -import 'navigation/router/app_router_delegate.dart'; +import 'navigation/transition/fade_page_transitions_builder.dart'; import 'navigation/views/app_navigation.dart'; -import 'navigation/views/app_navigation_rail.dart'; + class UnitApp extends StatelessWidget { const UnitApp({super.key}); @override Widget build(BuildContext context) { + return SortStateScope( notifier: SortState(), child: MaterialApp( theme: ThemeData( fontFamily: "宋体", scaffoldBackgroundColor: Colors.white, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.macOS: FadePageTransitionsBuilder(), + TargetPlatform.windows: FadePageTransitionsBuilder(), + TargetPlatform.linux: FadePageTransitionsBuilder(), + } + ), appBarTheme: const AppBarTheme( elevation: 0, iconTheme: IconThemeData(color: Colors.black), diff --git a/lib/v6_result_/pages/color/color_add_page.dart b/lib/v8/pages/color/color_add_page.dart similarity index 100% rename from lib/v6_result_/pages/color/color_add_page.dart rename to lib/v8/pages/color/color_add_page.dart diff --git a/lib/v6_result_/pages/color/color_detail_page.dart b/lib/v8/pages/color/color_detail_page.dart similarity index 100% rename from lib/v6_result_/pages/color/color_detail_page.dart rename to lib/v8/pages/color/color_detail_page.dart diff --git a/lib/v6_result_/pages/color/color_page.dart b/lib/v8/pages/color/color_page.dart similarity index 94% rename from lib/v6_result_/pages/color/color_page.dart rename to lib/v8/pages/color/color_page.dart index 85239d3..39bcc1b 100644 --- a/lib/v6_result_/pages/color/color_page.dart +++ b/lib/v8/pages/color/color_page.dart @@ -38,11 +38,12 @@ class _ColorPageState extends State { void _selectColor(Color color){ // String value = color.value.toRadixString(16); // router.path = '/color/detail?color=$value'; - router.changePath('/color/detail',extra: color); + router.changePath('/color/detail_error',extra: color); + } void _toAddPage() async { - Color? color = await router.changePath('/color/add',forResult: true); + Color? color = await router.changePath('/color/add',forResult: true,recordHistory: false); if (color != null) { setState(() { _colors.add(color); diff --git a/lib/v5_/pages/counter/counter_page.dart b/lib/v8/pages/counter/counter_page.dart similarity index 100% rename from lib/v5_/pages/counter/counter_page.dart rename to lib/v8/pages/counter/counter_page.dart diff --git a/lib/v5_/pages/empty/empty_page.dart b/lib/v8/pages/empty/empty_page.dart similarity index 100% rename from lib/v5_/pages/empty/empty_page.dart rename to lib/v8/pages/empty/empty_page.dart diff --git a/lib/v5_/pages/settings/settings_page.dart b/lib/v8/pages/settings/settings_page.dart similarity index 100% rename from lib/v5_/pages/settings/settings_page.dart rename to lib/v8/pages/settings/settings_page.dart diff --git a/lib/v8/pages/sort/functions.dart b/lib/v8/pages/sort/functions.dart new file mode 100644 index 0000000..6ae1176 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/bubble.dart b/lib/v8/pages/sort/functions/bubble.dart new file mode 100644 index 0000000..3cf3c99 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/cocktail.dart b/lib/v8/pages/sort/functions/cocktail.dart new file mode 100644 index 0000000..8c2d18c --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/comb.dart b/lib/v8/pages/sort/functions/comb.dart new file mode 100644 index 0000000..821f4a9 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/cycle.dart b/lib/v8/pages/sort/functions/cycle.dart new file mode 100644 index 0000000..4bef6eb --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/gnome.dart b/lib/v8/pages/sort/functions/gnome.dart new file mode 100644 index 0000000..5e08fc3 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/heap.dart b/lib/v8/pages/sort/functions/heap.dart new file mode 100644 index 0000000..9f5410b --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/insertion.dart b/lib/v8/pages/sort/functions/insertion.dart new file mode 100644 index 0000000..b1c7814 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/merage.dart b/lib/v8/pages/sort/functions/merage.dart new file mode 100644 index 0000000..12135b4 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/oddEven.dart b/lib/v8/pages/sort/functions/oddEven.dart new file mode 100644 index 0000000..bd6f548 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/pigeonHole.dart b/lib/v8/pages/sort/functions/pigeonHole.dart new file mode 100644 index 0000000..83bbce1 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/quick.dart b/lib/v8/pages/sort/functions/quick.dart new file mode 100644 index 0000000..25b9685 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/selection.dart b/lib/v8/pages/sort/functions/selection.dart new file mode 100644 index 0000000..185dae2 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/functions/shell.dart b/lib/v8/pages/sort/functions/shell.dart new file mode 100644 index 0000000..232f872 --- /dev/null +++ b/lib/v8/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/v8/pages/sort/provider/sort_config.dart b/lib/v8/pages/sort/provider/sort_config.dart new file mode 100644 index 0000000..2ad50ed --- /dev/null +++ b/lib/v8/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/v8/pages/sort/provider/state.dart b/lib/v8/pages/sort/provider/state.dart new file mode 100644 index 0000000..90803cf --- /dev/null +++ b/lib/v8/pages/sort/provider/state.dart @@ -0,0 +1,82 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import '../functions.dart'; +import 'sort_config.dart'; + +enum SortStatus{ + none, // 未操作 + sorting, // 排序中 + sorted, // 排序完成 +} + +class SortState with ChangeNotifier{ + + SortState(){ + reset(); + } + + SortStatus status = SortStatus.none; + + List data = []; + List 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 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 { + 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/v8/pages/sort/views/code_page/code_page.dart b/lib/v8/pages/sort/views/code_page/code_page.dart new file mode 100644 index 0000000..7d8d4c9 --- /dev/null +++ b/lib/v8/pages/sort/views/code_page/code_page.dart @@ -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), + )); + } +} diff --git a/lib/v8/pages/sort/views/data_painter.dart b/lib/v8/pages/sort/views/data_painter.dart new file mode 100644 index 0000000..d23e2f5 --- /dev/null +++ b/lib/v8/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;iAlertDialog()); + }, + child: const Icon(Icons.settings)) + ], + ); + } +} diff --git a/lib/v8/pages/sort/views/sort_button.dart b/lib/v8/pages/sort/views/sort_button.dart new file mode 100644 index 0000000..cab02aa --- /dev/null +++ b/lib/v8/pages/sort/views/sort_button.dart @@ -0,0 +1,58 @@ +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),), + + ], + ), + ), + ); + } +} diff --git a/lib/v8/pages/sort/views/sort_page.dart b/lib/v8/pages/sort/views/sort_page.dart new file mode 100644 index 0000000..980c385 --- /dev/null +++ b/lib/v8/pages/sort/views/sort_page.dart @@ -0,0 +1,185 @@ +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 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 createState() => _NavigatorScopeState(); +} + +class _NavigatorScopeState extends State { + + @override + Widget build(BuildContext context) { + SortState state = SortStateScope.of(context); + List 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 route, result) { + return route.didPop(result); + } +} + + + +class SortSelectorPanel extends StatelessWidget { + final String active; + final ValueChanged onSelected; + final List 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 createState() => _SortItemTileState(); +} + +class _SortItemTileState extends State { + @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 + ), + ), + ), + ), + ), + ); + } +} + diff --git a/lib/v8/pages/sort/views/sort_setting.dart b/lib/v8/pages/sort/views/sort_setting.dart new file mode 100644 index 0000000..9a96a88 --- /dev/null +++ b/lib/v8/pages/sort/views/sort_setting.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; + +import '../provider/sort_config.dart'; +import '../provider/state.dart'; + +class SortSettings extends StatefulWidget { + + const SortSettings({super.key,}); + + @override + State createState() => _SortSettingsState(); +} + +class _SortSettingsState extends State { + 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('确定设置')) + ], + ), + ), + ); + } +} diff --git a/lib/v5_/pages/user/user_page.dart b/lib/v8/pages/user/user_page.dart similarity index 100% rename from lib/v5_/pages/user/user_page.dart rename to lib/v8/pages/user/user_page.dart diff --git a/test/algorithm/add.dart b/test/algorithm/add.dart new file mode 100644 index 0000000..265f296 --- /dev/null +++ b/test/algorithm/add.dart @@ -0,0 +1,114 @@ +///两数相加 +// 给你两个非空的链表表示两个非负的整数。 +// 它们每位数字都是按照逆序 的方式存储的,并且每个节点只能存储一位数字 + +// 请你将两个数相加,并以相同形式返回一个表示和的链表。 +// 你可以假设除了数字 之外,这两个数都不会以 开头。 +//输入: 1=[2,4,3], 2=[5,6,4] 输出:[7,0,8] 解释: 342 + 465 = 807. +void main() { + // l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] + ListNode l1 = ListNode(2, next: ListNode(4, next: ListNode(3)),); + ListNode l2 = ListNode(5, next: ListNode(6, next: ListNode(4)),); + + //9,9,9,9,9,9,9 + // ListNode l1 = ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9, next: ListNode(9))))))))); + // ListNode l2 = ListNode(9, next: ListNode(9, next: ListNode(9,next: ListNode(9)),)); + ListNode result = addTwoNumbers(l1, l2); + print(result); +} + +class ListNode { + int val; + ListNode? next; + + ListNode(this.val, {this.next}); + + @override + String toString(){ + String result=''; + ListNode? cur = this; + while(cur!=null){ + result += cur.val.toString(); + cur = cur.next; + } + return result; + } + +} + +// ListNode? addTwoNumbers(ListNode? l1, ListNode? l2) { +// if (l1 == null) { +// return l2; +// } +// if (l2 == null) { +// return l1; +// } +// ListNode? l3 = ListNode(0); +// ListNode? cur = l3; +// int temp = 0; +// while (l1 != null || l2 != null) { +// int val1 = l1 == null ? 0 : l1.val; +// int val2 = l2 == null ? 0 : l2.val; +// int sum = val1 + val2 + temp; +// cur?.next = ListNode(sum % 10); +// temp = sum ~/ 10; +// cur = cur?.next; +// l1 = l1?.next; +// l2 = l2?.next; +// } +// if (temp != 0) { +// cur?.next = ListNode(temp); +// } +// return l3.next; +// } + +// ListNode? addTwoNumbers(ListNode? l1, ListNode? l2) { +// ListNode? head, tail; +// int carry = 0; +// while (l1 != null || l2 != null) { +// int n1 = l1?.val ?? 0; +// int n2 = l2?.val ?? 0; +// int sum = n1 + n2 + carry; +// if (head == null) { +// head = ListNode(sum % 10); +// tail = head; +// } else { +// tail!.next = ListNode(sum % 10); +// tail = tail.next; +// } +// carry = sum ~/ 10; +// l1 = l1?.next; +// l2 = l2?.next; +// } +// if (carry > 0) { +// tail!.next = ListNode(carry); +// } +// return head; +// } + +ListNode addTwoNumbers(ListNode? l1, ListNode? l2) { + ListNode pre = ListNode(0); + ListNode cur = pre; + int carry = 0; + while (l1 != null || l2 != null) { + int x = l1?.val ?? 0; + int y = l2?.val ?? 0; + int sum = x + y + carry; + + carry = sum ~/ 10; + sum = sum % 10; + cur.next = ListNode(sum); + + cur = cur.next!; + if (l1 != null) { + l1 = l1.next; + } + if (l2 != null) { + l2 = l2.next; + } + } + if (carry == 1) { + cur.next = ListNode(carry); + } + return pre.next!; +} diff --git a/test/parser/path.txt b/test/parser/path.txt new file mode 100644 index 0000000..9f69554 --- /dev/null +++ b/test/parser/path.txt @@ -0,0 +1 @@ +/color/details \ No newline at end of file diff --git a/test/structure/link_list/print.dart b/test/structure/link_list/print.dart new file mode 100644 index 0000000..a4ca869 --- /dev/null +++ b/test/structure/link_list/print.dart @@ -0,0 +1,29 @@ + +void main(){ + // 创建一个 2->4->3 的链表对象 + ListNode listNode = ListNode(2,next: ListNode(4,next: ListNode(3))); + + // 并完成 ListNode#toString 方法,打印 243 + print(listNode); +} + +class ListNode { + int val; + ListNode? next; + + ListNode(this.val, {this.next}); + + @override + String toString(){ + ListNode? cur = this; + String result = ""; + while( cur!=null){ + result = result +cur.val.toString(); + cur = cur.next; + } + return result; + } + + + +} \ No newline at end of file diff --git a/test/tree/main.dart b/test/tree/main.dart index 8fc2c02..5f2b517 100644 --- a/test/tree/main.dart +++ b/test/tree/main.dart @@ -6,22 +6,23 @@ main(){ /// 输出节点 2, 2-3,2-3-1 // findNodes(root, '2-2', 0,''); // List nodes = findNodes(root, '2-3-1', 0,'',[]); - List nodes = find( '/2/3/1'); + // List nodes = find( '/2/3/1'); + List nodes = find( '/4/3/4'); print(nodes); } List find(String input){ - String fixInput = input.substring(1); - List nodes = findNodes(root2,fixInput,0,'/',[]); - if(nodes.isNotEmpty&&nodes.last.value!=input){ - return []; - } + + List nodes = findNodes(root2,Uri.parse(input),0,'/',[]); + // if(nodes.isNotEmpty&&nodes.last.value!=input){ + // return []; + // } return nodes; } -List findNodes(Node node,String input,int deep,String prefix,List result){ - List parts = input.split('/'); +List findNodes(Node node,Uri uri,int deep,String prefix,List result){ + List parts = uri.pathSegments; if(deep>parts.length-1){ return result; } @@ -33,10 +34,11 @@ List findNodes(Node node,String input,int deep,String prefix,List re Node matched = nodes.first; result.add(matched); String nextPrefix = '${matched.value}/'; - findNodes(matched, input, ++deep,nextPrefix,result); + findNodes(matched, uri, ++deep,nextPrefix,result); + }else{ + result.add(Node(value: 'error')); + return result; } - }else{ - return result; } return result; }