diff --git a/lib/13/02/main.dart b/lib/13/02/main.dart index dc14a42..d273e50 100644 --- a/lib/13/02/main.dart +++ b/lib/13/02/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:window_manager/window_manager.dart'; import 'pages/app/app.dart'; import 'pages/app/app_router_delegate.dart'; @@ -11,4 +12,3 @@ void main() { - diff --git a/lib/13/04/app/navigation/app_router_delegate.dart b/lib/13/04/app/navigation/app_router_delegate.dart index 57f8479..587f207 100644 --- a/lib/13/04/app/navigation/app_router_delegate.dart +++ b/lib/13/04/app/navigation/app_router_delegate.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:iroute/13/04/app/navigation/router/iroute.dart'; +import 'package:iroute/common/pages/stl_color_page.dart'; import '../../pages/color/color_page.dart'; import '../../pages/empty/empty_page.dart'; @@ -34,9 +35,13 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { return null; } - late Completer completer; + final Map> _completerMap = {}; + + Completer? completer; + Future changePathForResult(String value) async{ - completer = Completer(); + Completer completer = Completer(); + _completerMap[value] = completer; path = value; return completer.future; } @@ -78,30 +83,11 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { } List _buildPageByPath(String path) { - - + if(path.startsWith('/color')){ + return buildColorPages(path); + } Widget? child; - List result = []; - if(path.startsWith('/color')){ - result.add( FadeTransitionPage( - key: ValueKey('/color'), - child:const ColorPage(), - )); - - - if(path == '/color/add'){ - result.add( FadeTransitionPage( - key: ValueKey('/color/add'), - child:const ColorAddPage(), - )); - } - return result; - } - - if (path == kDestinationsPaths[0]) { - child = const ColorPage(); - } if (path == kDestinationsPaths[1]) { child = const CounterPage(); } @@ -119,6 +105,38 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { ]; } + 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:StlColorPage(color: color), + )); + } + } + if(segment == 'add'){ + result.add( const FadeTransitionPage( + key: ValueKey('/color/add'), + child:ColorAddPage(), + )); + } + + } + return result; + } + @override Future popRoute() async { print('=======popRoute========='); @@ -126,17 +144,28 @@ class AppRouterDelegate extends RouterDelegate with ChangeNotifier { } bool _onPopPage(Route route, result) { - if(path == '/color/add'){ - completer.complete(result); - path = '/color'; + 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 = ['/']; diff --git a/lib/13/04/pages/color/color_add_page.dart b/lib/13/04/pages/color/color_add_page.dart index bcc647b..b3cf660 100644 --- a/lib/13/04/pages/color/color_add_page.dart +++ b/lib/13/04/pages/color/color_add_page.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter_colorpicker/flutter_colorpicker.dart'; class ColorAddPage extends StatefulWidget { const ColorAddPage({super.key}); @@ -22,31 +23,58 @@ class _ColorAddPageState extends State { Widget build(BuildContext context) { String text = '# ${_color.value.toRadixString(16)}'; return Scaffold( - // appBar: AppBar( - // title: Text('添加颜色'), - // actions: [IconButton(onPressed: _selectColor, icon: Icon(Icons.check ))], - // ), - body: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0,vertical: 20), + bottomNavigationBar: Container( + margin: EdgeInsets.only(right:20,bottom: 20), + // color: Colors.redAccent, child: Row( + textDirection:TextDirection.rtl, 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), - ), - ), + 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), + 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, + + ), + ], + ), ); } @@ -64,4 +92,11 @@ class _ColorAddPageState extends State { void _selectColor() { Navigator.of(context).pop(_color); } + + void changeColor(Color value) { + _color = value; + setState(() { + + }); + } } diff --git a/lib/13/04/pages/color/color_page.dart b/lib/13/04/pages/color/color_page.dart index 7d41fad..9c8ca6d 100644 --- a/lib/13/04/pages/color/color_page.dart +++ b/lib/13/04/pages/color/color_page.dart @@ -1,19 +1,7 @@ import 'package:flutter/material.dart'; import 'package:iroute/common/components/colors_panel.dart'; -import 'package:iroute/common/pages/stl_color_page.dart'; import '../../app/navigation/app_router_delegate.dart'; -// class HomePage extends StatelessWidget { -// const HomePage({super.key}); -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// body: const Center(child: Text('HomePage'))); -// } -// } - - class ColorPage extends StatefulWidget { const ColorPage({super.key}); @@ -45,11 +33,12 @@ class _ColorPageState extends State { } void _selectColor(Color color){ - // String value = color.value.toRadixString(16); + String value = color.value.toRadixString(16); + router.path = '/color/detail?color=$value'; } void _toAddPage() async { - dynamic color = await router.changePathForResult('/color/add'); + Color? color = await router.changePathForResult('/color/add'); if (color != null) { setState(() { _colors.add(color); diff --git a/lib/14/01/app/navigation/app_router_delegate.dart b/lib/14/01/app/navigation/app_router_delegate.dart new file mode 100644 index 0000000..587f207 --- /dev/null +++ b/lib/14/01/app/navigation/app_router_delegate.dart @@ -0,0 +1,208 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:iroute/13/04/app/navigation/router/iroute.dart'; +import 'package:iroute/common/pages/stl_color_page.dart'; + +import '../../pages/color/color_page.dart'; +import '../../pages/empty/empty_page.dart'; +import '../../pages/settings/settings_page.dart'; +import '../../pages/counter/counter_page.dart'; +import '../../pages/user/user_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 { + 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; + + Future changePathForResult(String value) async{ + Completer completer = Completer(); + _completerMap[value] = completer; + path = value; + return completer.future; + } + + set path(String value) { + if (_path == value) return; + _path = value; + notifyListeners(); + } + + // IRoute? parserPath(String path){ + // + // List parts = path.split('/'); + // String lever1 = '/${parts[1]}'; + // List iRoutes = kDestinationsIRoutes.where((e) => e.path == lever1).toList(); + // + // int counter = 2; + // + // IRoute? result; + // String check = lever1; + // for(int i = 0;i _buildPageByPath(String path) { + if(path.startsWith('/color')){ + return buildColorPages(path); + } + + Widget? child; + 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:StlColorPage(color: color), + )); + } + } + 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/14/01/app/navigation/router/iroute.dart b/lib/14/01/app/navigation/router/iroute.dart new file mode 100644 index 0000000..b703af9 --- /dev/null +++ b/lib/14/01/app/navigation/router/iroute.dart @@ -0,0 +1,37 @@ +class IRoute { + final String path; + final List children; + + const IRoute({required this.path, this.children = const []}); + + @override + String toString() { + return 'IRoute{path: $path, children: $children}'; + } + + List list(){ + + return []; + } + +} + + +const List kDestinationsIRoutes = [ + IRoute( + path: '/color', + children: [ + IRoute(path: '/color/add'), + IRoute(path: '/color/detail'), + ], + ), + IRoute( + path: '/counter', + ), + IRoute( + path: '/user', + ), + IRoute( + path: '/settings', + ), +]; diff --git a/lib/14/01/app/navigation/views/app_navigation_rail.dart b/lib/14/01/app/navigation/views/app_navigation_rail.dart new file mode 100644 index 0000000..2899ea2 --- /dev/null +++ b/lib/14/01/app/navigation/views/app_navigation_rail.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import '../app_router_delegate.dart'; + +class AppNavigationRail extends StatefulWidget { + const AppNavigationRail({super.key}); + + @override + State createState() => _AppNavigationRailState(); +} + +class _AppNavigationRailState extends State { + final List destinations = const [ + NavigationRailDestination(icon: Icon(Icons.color_lens_outlined), label: Text("颜色板")), + NavigationRailDestination(icon: Icon(Icons.add_chart), label: Text("计数器")), + NavigationRailDestination(icon: Icon(Icons.person), label: Text("我的")), + NavigationRailDestination(icon: Icon(Icons.settings), label: Text("设置")), + ]; + + @override + void initState() { + super.initState(); + router.addListener(_onRouterChange); + } + + @override + void dispose() { + router.removeListener(_onRouterChange); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return NavigationRail( + labelType: NavigationRailLabelType.all, + onDestinationSelected: _onDestinationSelected, + destinations: destinations, + selectedIndex: router.activeIndex, + ); + } + + void _onDestinationSelected(int index) { + router.path = kDestinationsPaths[index]; + } + + void _onRouterChange() { + setState(() { + + }); + } +} \ No newline at end of file diff --git a/lib/14/01/app/unit_app.dart b/lib/14/01/app/unit_app.dart new file mode 100644 index 0000000..5332b5d --- /dev/null +++ b/lib/14/01/app/unit_app.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'navigation/app_router_delegate.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( + appBarTheme: const AppBarTheme( + elevation: 0, + iconTheme: IconThemeData(color: Colors.black), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 18, + fontWeight: FontWeight.bold, + ))), + debugShowCheckedModeBanner: false, + home: Scaffold( + body: Row( + children: [ + const AppNavigationRail(), + Expanded( + child: Router( + routerDelegate: router, + backButtonDispatcher: RootBackButtonDispatcher(), + ), + ), + ], + ), + )); + } +} + + diff --git a/lib/14/01/main.dart b/lib/14/01/main.dart new file mode 100644 index 0000000..38c9d78 --- /dev/null +++ b/lib/14/01/main.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +import 'app/unit_app.dart'; +import 'app/navigation/app_router_delegate.dart'; + + + + + +void main() { + runApp(const UnitApp()); +} + + + + diff --git a/lib/14/01/pages/color/color_add_page.dart b/lib/14/01/pages/color/color_add_page.dart new file mode 100644 index 0000000..b3cf660 --- /dev/null +++ b/lib/14/01/pages/color/color_add_page.dart @@ -0,0 +1,102 @@ +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), + 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/14/01/pages/color/color_page.dart b/lib/14/01/pages/color/color_page.dart new file mode 100644 index 0000000..9c8ca6d --- /dev/null +++ b/lib/14/01/pages/color/color_page.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:iroute/common/components/colors_panel.dart'; +import '../../app/navigation/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: ColorsPanel( + colors: _colors, + onSelect: _selectColor, + ), + ); + } + + void _selectColor(Color color){ + String value = color.value.toRadixString(16); + router.path = '/color/detail?color=$value'; + } + + void _toAddPage() async { + Color? color = await router.changePathForResult('/color/add'); + if (color != null) { + setState(() { + _colors.add(color); + }); + } + } +} \ No newline at end of file diff --git a/lib/14/01/pages/counter/counter_page.dart b/lib/14/01/pages/counter/counter_page.dart new file mode 100644 index 0000000..b5b2e17 --- /dev/null +++ b/lib/14/01/pages/counter/counter_page.dart @@ -0,0 +1,43 @@ +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/14/01/pages/empty/empty_page.dart b/lib/14/01/pages/empty/empty_page.dart new file mode 100644 index 0000000..e9064a8 --- /dev/null +++ b/lib/14/01/pages/empty/empty_page.dart @@ -0,0 +1,30 @@ +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/14/01/pages/settings/settings_page.dart b/lib/14/01/pages/settings/settings_page.dart new file mode 100644 index 0000000..0b53503 --- /dev/null +++ b/lib/14/01/pages/settings/settings_page.dart @@ -0,0 +1,11 @@ +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/14/01/pages/user/user_page.dart b/lib/14/01/pages/user/user_page.dart new file mode 100644 index 0000000..aba9710 --- /dev/null +++ b/lib/14/01/pages/user/user_page.dart @@ -0,0 +1,11 @@ +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/14/01/transition/fade_transition_page.dart b/lib/14/01/transition/fade_transition_page.dart new file mode 100644 index 0000000..552171b --- /dev/null +++ b/lib/14/01/transition/fade_transition_page.dart @@ -0,0 +1,53 @@ +// 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/14/01/transition/no_transition_page.dart b/lib/14/01/transition/no_transition_page.dart new file mode 100644 index 0000000..291910b --- /dev/null +++ b/lib/14/01/transition/no_transition_page.dart @@ -0,0 +1,47 @@ +// 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/14/02/app/navigation/router/app_router_delegate.dart b/lib/14/02/app/navigation/router/app_router_delegate.dart new file mode 100644 index 0000000..1026168 --- /dev/null +++ b/lib/14/02/app/navigation/router/app_router_delegate.dart @@ -0,0 +1,210 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:iroute/13/04/app/navigation/router/iroute.dart'; +import 'package:iroute/common/pages/stl_color_page.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/counter/counter_page.dart'; +import '../../../pages/sort/sort_page.dart'; +import '../../../pages/user/user_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 { + 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; + + Future changePathForResult(String value) async{ + Completer completer = Completer(); + _completerMap[value] = completer; + path = value; + return completer.future; + } + + set path(String value) { + if (_path == value) return; + _path = value; + notifyListeners(); + } + + // IRoute? parserPath(String path){ + // + // List parts = path.split('/'); + // String lever1 = '/${parts[1]}'; + // List iRoutes = kDestinationsIRoutes.where((e) => e.path == lever1).toList(); + // + // int counter = 2; + // + // IRoute? result; + // String check = lever1; + // for(int i = 0;i _buildPageByPath(String path) { + if(path.startsWith('/color')){ + return buildColorPages(path); + } + + Widget? child; + if (path == kDestinationsPaths[1]) { + child = const CounterPage(); + } + if (path == kDestinationsPaths[2]) { + child = const SortPage(); + } + 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), + )); + } + } + 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/14/02/app/navigation/router/iroute.dart b/lib/14/02/app/navigation/router/iroute.dart new file mode 100644 index 0000000..b703af9 --- /dev/null +++ b/lib/14/02/app/navigation/router/iroute.dart @@ -0,0 +1,37 @@ +class IRoute { + final String path; + final List children; + + const IRoute({required this.path, this.children = const []}); + + @override + String toString() { + return 'IRoute{path: $path, children: $children}'; + } + + List list(){ + + return []; + } + +} + + +const List kDestinationsIRoutes = [ + IRoute( + path: '/color', + children: [ + IRoute(path: '/color/add'), + IRoute(path: '/color/detail'), + ], + ), + IRoute( + path: '/counter', + ), + IRoute( + path: '/user', + ), + IRoute( + path: '/settings', + ), +]; diff --git a/lib/14/02/app/navigation/views/app_navigation.dart b/lib/14/02/app/navigation/views/app_navigation.dart new file mode 100644 index 0000000..792353e --- /dev/null +++ b/lib/14/02/app/navigation/views/app_navigation.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import '../router/app_router_delegate.dart'; +import 'app_navigation_rail.dart'; +import 'background.dart'; +import 'top_bar.dart'; + +class AppNavigation extends StatelessWidget { + const AppNavigation({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + // const Background(), + Row( + children: [ + const AppNavigationRail(), + Expanded( + child: Column( + children: [ + TopBar(), + Divider(height: 1,), + Expanded( + child: Router( + routerDelegate: router, + backButtonDispatcher: RootBackButtonDispatcher(), + ), + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/14/02/app/navigation/views/app_navigation_rail.dart b/lib/14/02/app/navigation/views/app_navigation_rail.dart new file mode 100644 index 0000000..27d3d8d --- /dev/null +++ b/lib/14/02/app/navigation/views/app_navigation_rail.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import '../../../components/tolyui/navigation/toly_navigation_rail.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 destinations = const [ + NavigationRailDestination(icon: Icon(Icons.color_lens_outlined), label: Text("颜色板")), + NavigationRailDestination(icon: Icon(Icons.add_chart), label: Text("计数器")), + NavigationRailDestination(icon: Icon(Icons.person), label: Text("我的")), + NavigationRailDestination(icon: Icon(Icons.settings), label: Text("设置")), + ]; + + @override + void initState() { + super.initState(); + router.addListener(_onRouterChange); + } + + @override + void dispose() { + router.removeListener(_onRouterChange); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return TolyNavigationRail( + leading: Padding( + padding: const EdgeInsets.symmetric(vertical: 18.0), + child: FlutterLogo(), + ), + backgroundColor: const Color(0xff3975c6), + onDestinationSelected: _onDestinationSelected, + selectedIndex: router.activeIndex, + ); + + // return NavigationRail( + // backgroundColor: Colors.transparent, + // labelType: NavigationRailLabelType.all, + // onDestinationSelected: _onDestinationSelected, + // destinations: destinations, + // selectedIndex: router.activeIndex, + // ); + } + + void _onDestinationSelected(int index) { + router.path = kDestinationsPaths[index]; + } + + void _onRouterChange() { + setState(() {}); + } +} diff --git a/lib/14/02/app/navigation/views/background.dart b/lib/14/02/app/navigation/views/background.dart new file mode 100644 index 0000000..9d52058 --- /dev/null +++ b/lib/14/02/app/navigation/views/background.dart @@ -0,0 +1,19 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class Background extends StatelessWidget { + const Background({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + gradient: + LinearGradient(transform: GradientRotation(pi / 2), colors: [ + Color(0xffF1EFF1), + Color(0xffFFFFFF), + ])), + ); + } +} diff --git a/lib/14/02/app/navigation/views/top_bar.dart b/lib/14/02/app/navigation/views/top_bar.dart new file mode 100644 index 0000000..24a8c35 --- /dev/null +++ b/lib/14/02/app/navigation/views/top_bar.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import '../router/app_router_delegate.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../../../components/tolyui/navigation/toly_breadcrumb.dart'; +import '../../../components/windows/window_buttons.dart'; + +class TopBar extends StatelessWidget { + const TopBar({super.key}); + + @override + Widget build(BuildContext context) { + return DragToMoveAreaNoDouble( + child: Container( + alignment: Alignment.center, + height: 46, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + RouterIndicator(), + Spacer(), + WindowButtons() + ], + ), + ), + ); + } +} + +class RouterIndicator extends StatefulWidget { + const RouterIndicator({super.key}); + + @override + State createState() => _RouterIndicatorState(); +} + +class _RouterIndicatorState extends State { + @override + void initState() { + super.initState(); + router.addListener(_onRouterChange); + } + + Map routeLabelMap = { + '/color': '颜色板', + '/color/add': '添加颜色', + '/color/detail': '颜色详情', + '/counter': '计数器', + '/user': '我的', + '/settings': '系统设置', + }; + + @override + Widget build(BuildContext context) { + return TolyBreadcrumb( + items: pathToBreadcrumbItems(router.path), + onTapItem: (item){ + if(item.to!=null){ + router.path = item.to!; + } + }, + ); + } + + void _onRouterChange() { + print('_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'; + result.add(BreadcrumbItem(to: to, label: routeLabelMap[to] ?? '未知路由',active: to==distPath)); + } + return result; + } +} + +class DragToMoveAreaNoDouble extends StatelessWidget { + final Widget child; + + const DragToMoveAreaNoDouble({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onPanStart: (details) { + windowManager.startDragging(); + }, + child: child, + ); + } +} diff --git a/lib/14/02/app/unit_app.dart b/lib/14/02/app/unit_app.dart new file mode 100644 index 0000000..8042aa8 --- /dev/null +++ b/lib/14/02/app/unit_app.dart @@ -0,0 +1,28 @@ +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: "宋体", + 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/14/02/components/tolyui/navigation/menu_meta.dart b/lib/14/02/components/tolyui/navigation/menu_meta.dart new file mode 100644 index 0000000..22c7dec --- /dev/null +++ b/lib/14/02/components/tolyui/navigation/menu_meta.dart @@ -0,0 +1,18 @@ +import 'package:flutter/cupertino.dart'; + +class MenuMeta { + // 标签 + final String label; + + // 图标数据 + final IconData icon; + + // 图标颜色 + final Color? color; + + const MenuMeta({ + required this.label, + required this.icon, + this.color, + }); +} diff --git a/lib/14/02/components/tolyui/navigation/toly_breadcrumb.dart b/lib/14/02/components/tolyui/navigation/toly_breadcrumb.dart new file mode 100644 index 0000000..6595556 --- /dev/null +++ b/lib/14/02/components/tolyui/navigation/toly_breadcrumb.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/gestures/events.dart'; + +class TolyBreadcrumb extends StatelessWidget { + final List items; + final ValueChanged? onTapItem; + + const TolyBreadcrumb({super.key, required this.items, this.onTapItem}); + + @override + Widget build(BuildContext context) { + List children = []; + + for (int i = 0; i < items.length; i++) { + children.add(TolyBreadcrumbItem( + item: items[i], onTapItem: onTapItem, + )); + if (i != items.length - 1) { + children.add(Padding( + padding: const EdgeInsets.symmetric(horizontal: 4.0), + child: Text( + '/', + style: TextStyle(color: Colors.grey), + ), + )); + } + } + + return Wrap( + children: children, + ); + } +} + +class TolyBreadcrumbItem extends StatefulWidget { + final BreadcrumbItem item; + final ValueChanged? onTapItem; + const TolyBreadcrumbItem({super.key, required this.item, required this.onTapItem}); + + @override + State createState() => _TolyBreadcrumbItemState(); +} + +class _TolyBreadcrumbItemState extends State { + + bool _hover = false; + + @override + Widget build(BuildContext context) { + bool hasTarget = (widget.item.to != null); + Color? color = (_hover&&hasTarget)?Colors.blue:null; + MouseCursor cursor = hasTarget?SystemMouseCursors.click:SystemMouseCursors.basic; + + if(widget.item.active) { + color = null; + cursor = SystemMouseCursors.basic; + } + + TextStyle style = TextStyle( + fontWeight: hasTarget ? FontWeight.bold : null, + color: color + ); + + return MouseRegion( + cursor: cursor, + onEnter: _onEnter, + onExit: _onExit, + child: GestureDetector( + onTap: () { + if(!widget.item.active){ + widget.onTapItem?.call(widget.item); + } + }, + child: Text(widget.item.label, style: style)), + ); + } + + void _onEnter(PointerEnterEvent event) { + setState(() { + _hover = true; + }); + } + + void _onExit(PointerExitEvent event) { + setState(() { + _hover = false; + }); + } +} + +class BreadcrumbItem { + final String? to; + final String label; + final bool active; + + BreadcrumbItem( {this.to, required this.label,this.active=false,}); +} + +// +// 首页 +// 活动管理 +// 活动列表 +// 活动详情 +// diff --git a/lib/14/02/components/tolyui/navigation/toly_navigation_rail.dart b/lib/14/02/components/tolyui/navigation/toly_navigation_rail.dart new file mode 100644 index 0000000..221e214 --- /dev/null +++ b/lib/14/02/components/tolyui/navigation/toly_navigation_rail.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; + +import 'menu_meta.dart'; + +// final List destinations = const [ +// NavigationRailDestination(icon: Icon(Icons.color_lens_outlined), label: Text("颜色板")), +// NavigationRailDestination(icon: Icon(Icons.add_chart), label: Text("计数器")), +// NavigationRailDestination(icon: Icon(Icons.person), label: Text("我的")), +// NavigationRailDestination(icon: Icon(Icons.settings), label: Text("设置")), +// ]; + +const List kDeskNavBarMenus = [ + MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined), + MenuMeta( + label: '计数器', + icon: Icons.add_chart, + ), + MenuMeta( + label: '我的', + icon: Icons.person, + ), + MenuMeta(label: '设置', icon: Icons.settings), +]; + +class TolyNavigationRail extends StatelessWidget { + final ValueChanged onDestinationSelected; + final Color backgroundColor; + final int? selectedIndex; + final Widget? leading; + + const TolyNavigationRail({ + Key? key, + required this.onDestinationSelected, + required this.selectedIndex, + this.leading, + required this.backgroundColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: 64, + color: backgroundColor, + child: Column( + children: [ + if(leading!=null) leading!, + Expanded( + child: LeftNavigationMenu( + items: kDeskNavBarMenus, + selectedIndex: selectedIndex, + onTapItem: onDestinationSelected, + )), + // if (selectedIndex == 0) HelpButton() + ], + ), + ); + } +} + +class LeftNavigationMenu extends StatelessWidget { + final List items; + final ValueChanged onTapItem; + + final int? selectedIndex; + + const LeftNavigationMenu( + {Key? key, + required this.items, + required this.selectedIndex, + required this.onTapItem}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + children: items.asMap().keys.map((int index) { + return LeftNavigationBarItemWidget( + item: items[index], + selected: index == selectedIndex, + onTap: () { + onTapItem.call(index); + }, + ); + }).toList(), + ); + } +} + +class LeftNavigationBarItemWidget extends StatefulWidget { + final MenuMeta item; + final bool selected; + final VoidCallback onTap; + + const LeftNavigationBarItemWidget( + {Key? key, + required this.item, + required this.selected, + required this.onTap}) + : super(key: key); + + @override + State createState() => + _LeftNavigationBarItemWidgetState(); +} + +class _LeftNavigationBarItemWidgetState + extends State { + bool _hover = false; + + @override + Widget build(BuildContext context) { + Color? bgColor; + Color iconColor; + if (widget.selected) { + bgColor = Colors.white.withOpacity(0.2); + iconColor = Colors.white; + } else { + bgColor = _hover ? Colors.white.withOpacity(0.1) : null; + iconColor = Colors.white.withOpacity(0.8); + } + + return InkWell( + onTap: widget.selected + ? null + : () { + widget.onTap(); + setState(() { + _hover = false; + }); + }, + onHover: widget.selected + ? null + : (v) { + setState(() { + _hover = v; + }); + }, + child: Container( + alignment: Alignment.center, + margin: EdgeInsets.only(bottom: 10), + width: 50, + height: 50, + decoration: BoxDecoration( + color: bgColor, borderRadius: BorderRadius.circular(8)), + child: Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 2, + children: [ + Icon( + widget.item.icon, + color: iconColor, + ), + Text( + widget.item.label, + style: TextStyle(color: iconColor, fontSize: 12), + ) + ], + ), + ), + ); + } +} diff --git a/lib/14/02/components/windows/window_buttons.dart b/lib/14/02/components/windows/window_buttons.dart new file mode 100644 index 0000000..714687a --- /dev/null +++ b/lib/14/02/components/windows/window_buttons.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:window_manager/window_manager.dart'; + +class WindowButtons extends StatefulWidget { + final List? actions; + const WindowButtons({Key? key, this.actions}) : super(key: key); + + @override + State createState() => _WindowButtonsState(); +} + +class _WindowButtonsState extends State { + @override + Widget build(BuildContext context) { + Brightness brightness = Theme.of(context).brightness; + return Align( + alignment:Alignment.topRight,child: Wrap( + spacing: 5, + children: [ + if(widget.actions!=null) + ...widget.actions!, + SizedBox( + width: 30, + height: 30, + child: WindowCaptionButton.minimize( + brightness:brightness, + onPressed: () async { + bool isMinimized = await windowManager.isMinimized(); + if (isMinimized) { + windowManager.restore(); + } else { + windowManager.minimize(); + } + }, + ), + ), + SizedBox( + width: 30, + height: 30, + child: FutureBuilder( + future: windowManager.isMaximized(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.data == true) { + return WindowCaptionButton.unmaximize( + brightness: brightness, + onPressed: () async{ + await windowManager.unmaximize(); + setState(() { + + }); + }, + ); + } + return WindowCaptionButton.maximize( + brightness: brightness, + onPressed: () async{ + await windowManager.maximize(); + setState(() { + + }); + }, + ); + }, + ), + ), + SizedBox( + height: 30, + width: 30, + child: WindowCaptionButton.close( + brightness: brightness, + onPressed: () { + windowManager.close(); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/14/02/main.dart b/lib/14/02/main.dart new file mode 100644 index 0000000..ec1745c --- /dev/null +++ b/lib/14/02/main.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:window_manager/window_manager.dart'; + +import 'app/unit_app.dart'; +import 'app/navigation/router/app_router_delegate.dart'; + + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + setSize(); + runApp(const UnitApp()); +} + + +void setSize() async{ + await windowManager.ensureInitialized(); + WindowOptions windowOptions = const WindowOptions( + size: Size(800, 540), + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.hidden, + ); + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); +} diff --git a/lib/14/02/pages/color/color_add_page.dart b/lib/14/02/pages/color/color_add_page.dart new file mode 100644 index 0000000..c6ca0cd --- /dev/null +++ b/lib/14/02/pages/color/color_add_page.dart @@ -0,0 +1,102 @@ +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/14/02/pages/color/color_detail_page.dart b/lib/14/02/pages/color/color_detail_page.dart new file mode 100644 index 0000000..17fcd17 --- /dev/null +++ b/lib/14/02/pages/color/color_detail_page.dart @@ -0,0 +1,34 @@ +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/14/02/pages/color/color_page.dart b/lib/14/02/pages/color/color_page.dart new file mode 100644 index 0000000..4c526d0 --- /dev/null +++ b/lib/14/02/pages/color/color_page.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:iroute/common/components/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'; + } + + void _toAddPage() async { + Color? color = await router.changePathForResult('/color/add'); + if (color != null) { + setState(() { + _colors.add(color); + }); + } + } +} \ No newline at end of file diff --git a/lib/14/02/pages/counter/counter_page.dart b/lib/14/02/pages/counter/counter_page.dart new file mode 100644 index 0000000..b5b2e17 --- /dev/null +++ b/lib/14/02/pages/counter/counter_page.dart @@ -0,0 +1,43 @@ +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/14/02/pages/empty/empty_page.dart b/lib/14/02/pages/empty/empty_page.dart new file mode 100644 index 0000000..e9064a8 --- /dev/null +++ b/lib/14/02/pages/empty/empty_page.dart @@ -0,0 +1,30 @@ +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/14/02/pages/settings/settings_page.dart b/lib/14/02/pages/settings/settings_page.dart new file mode 100644 index 0000000..0b53503 --- /dev/null +++ b/lib/14/02/pages/settings/settings_page.dart @@ -0,0 +1,11 @@ +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/14/02/pages/sort/sort_page.dart b/lib/14/02/pages/sort/sort_page.dart new file mode 100644 index 0000000..8172cc0 --- /dev/null +++ b/lib/14/02/pages/sort/sort_page.dart @@ -0,0 +1,859 @@ +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); + streamController.add(numbers); + } + + // 依次取出最大堆的根节点(最大值),并进行堆化 + for (int i = numbers.length - 1; i >= 0; i--) { + int temp = numbers[0]; + numbers[0] = numbers[i]; + numbers[i] = temp; + await heapify(numbers, i, 0); + streamController.add(numbers); + } + } + + heapify(List arr, int n, int i) async { + int largest = i; + int l = 2 * i + 1; // 左子节点索引 + int r = 2 * i + 2; // 右子节点索引 + + // 如果左子节点存在并且大于父节点,则更新最大值索引 + if (l < n && arr[l] > arr[largest]) largest = l; + + // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 + if (r < n && arr[r] > arr[largest]) largest = r; + + // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 + if (largest != i) { + int temp = numbers[i]; + numbers[i] = numbers[largest]; + numbers[largest] = temp; + heapify(arr, n, largest); + } + + await Future.delayed(getDuration()); // 延迟操作,用于可视化排序过程 + } + + ///插入排序 + insertionSort() async { + for (int i = 1; i < numbers.length; i++) { + int temp = numbers[i]; // 将当前元素存储到临时变量 temp 中 + int j = i - 1; // j 表示已排序部分的最后一个元素的索引 + + // 在已排序部分从后往前查找,找到合适位置插入当前元素 + while (j >= 0 && temp < numbers[j]) { + numbers[j + 1] = numbers[j]; // 当前元素比已排序部分的元素小,将元素后移一位 + --j; // 向前遍历 + await Future.delayed(getDuration()); + streamController.add(numbers); // 更新排序结果 + } + + numbers[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 + await Future.delayed(getDuration(), () {}); + streamController.add(numbers); // 更新排序结果 + } + } + + ///地精排序 (侏儒排序) + gnomeSort() async { + int index = 0; + + while (index < numbers.length) { + // 当 index 小于数组长度时执行循环 + if (index == 0) index++; + if (numbers[index] >= numbers[index - 1]) { + // 如果当前元素大于等于前面的元素,则将 index 加1 + index++; + } else { + // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) + int temp = numbers[index]; + numbers[index] = numbers[index - 1]; + numbers[index - 1] = temp; + index--; + } + await Future.delayed(getDuration()); + streamController.add(numbers); + } + + return; + } + + ///奇偶排序(Odd-Even Sort) + oddEvenSort() async { + bool isSorted = false; + + while (!isSorted) { + // 当 isSorted 为 false 时执行循环 + isSorted = true; // 先假设数组已经排好序 + + for (int i = 1; i <= numbers.length - 2; i = i + 2) { + // 对奇数索引位置进行比较 + if (numbers[i] > numbers[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = numbers[i]; + numbers[i] = numbers[i + 1]; + numbers[i + 1] = temp; + isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false + await Future.delayed(getDuration()); + streamController.add(numbers); + } + } + + for (int i = 0; i <= numbers.length - 2; i = i + 2) { + // 对偶数索引位置进行比较 + if (numbers[i] > numbers[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = numbers[i]; + numbers[i] = numbers[i + 1]; + numbers[i + 1] = temp; + isSorted = false; + await Future.delayed(getDuration()); + streamController.add(numbers); + } + } + } + + return; + } + + ///快速排序 + quickSort(int leftIndex, int rightIndex) async { + // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 + Future _partition(int left, int right) async { + // 选择中间位置的元素作为基准元素 + int p = (left + (right - left) / 2).toInt(); + + // 交换基准元素和最右边的元素 + var temp = numbers[p]; + numbers[p] = numbers[right]; + numbers[right] = temp; + await Future.delayed(getDuration()); + streamController.add(numbers); + + // 初始化游标 cursor + int cursor = left; + + // 遍历数组并根据基准元素将元素交换到左侧或右侧 + for (int i = left; i < right; i++) { + if (cf(numbers[i], numbers[right]) <= 0) { + // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 + var temp = numbers[i]; + numbers[i] = numbers[cursor]; + numbers[cursor] = temp; + cursor++; + + await Future.delayed(getDuration()); + streamController.add(numbers); + } + } + + // 将基准元素放置在游标位置 + temp = numbers[right]; + numbers[right] = numbers[cursor]; + numbers[cursor] = temp; + + await Future.delayed(getDuration()); + streamController.add(numbers); + + return cursor; // 返回基准元素的索引位置 + } + + // 如果左索引小于右索引,则递归地对数组进行快速排序 + if (leftIndex < rightIndex) { + int p = await _partition(leftIndex, rightIndex); + + await quickSort(leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 + + await quickSort(p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 + } + } + + // 比较函数,用于判断两个元素的大小关系 + cf(int a, int b) { + if (a < b) { + return -1; // 若 a 小于 b,则返回 -1 + } else if (a > b) { + return 1; // 若 a 大于 b,则返回 1 + } else { + return 0; // 若 a 等于 b,则返回 0 + } + } + + ///归并排序 + mergeSort(int leftIndex, int rightIndex) async { + // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 + Future merge(int leftIndex, int middleIndex, int rightIndex) async { + // 计算左侧子数组和右侧子数组的大小 + int leftSize = middleIndex - leftIndex + 1; + int rightSize = rightIndex - middleIndex; + + // 创建左侧子数组和右侧子数组 + List leftList = List.generate(leftSize, (index) => 0); + List rightList = List.generate(rightSize, (index) => 0); + + // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 + for (int i = 0; i < leftSize; i++) { + leftList[i] = numbers[leftIndex + i]; + } + for (int j = 0; j < rightSize; j++) { + rightList[j] = numbers[middleIndex + j + 1]; + } + + // 初始化游标和索引 + int i = 0, j = 0; + int k = leftIndex; + + // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 + while (i < leftSize && j < rightSize) { + if (leftList[i] <= rightList[j]) { + numbers[k] = leftList[i]; + i++; + } else { + numbers[k] = rightList[j]; + j++; + } + + await Future.delayed(getDuration()); + streamController.add(numbers); + + k++; + } + + // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 + while (i < leftSize) { + numbers[k] = leftList[i]; + i++; + k++; + + await Future.delayed(getDuration()); + streamController.add(numbers); + } + + while (j < rightSize) { + numbers[k] = rightList[j]; + j++; + k++; + + await Future.delayed(getDuration()); + streamController.add(numbers); + } + } + + // 如果左索引小于右索引,则递归地对数组进行归并排序 + if (leftIndex < rightIndex) { + // 计算中间索引位置 + int middleIndex = (rightIndex + leftIndex) ~/ 2; + + // 分别对左侧子数组和右侧子数组进行归并排序 + await mergeSort(leftIndex, middleIndex); + await mergeSort(middleIndex + 1, rightIndex); + + await Future.delayed(getDuration()); + streamController.add(numbers); + + // 合并两个有序子数组 + await merge(leftIndex, middleIndex, rightIndex); + } + } + + 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/14/02/pages/user/user_page.dart b/lib/14/02/pages/user/user_page.dart new file mode 100644 index 0000000..aba9710 --- /dev/null +++ b/lib/14/02/pages/user/user_page.dart @@ -0,0 +1,11 @@ +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/14/02/transition/fade_transition_page.dart b/lib/14/02/transition/fade_transition_page.dart new file mode 100644 index 0000000..552171b --- /dev/null +++ b/lib/14/02/transition/fade_transition_page.dart @@ -0,0 +1,53 @@ +// 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/14/02/transition/no_transition_page.dart b/lib/14/02/transition/no_transition_page.dart new file mode 100644 index 0000000..291910b --- /dev/null +++ b/lib/14/02/transition/no_transition_page.dart @@ -0,0 +1,47 @@ +// 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/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index f6f23bf..0cacc75 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,18 @@ #include "generated_plugin_registrant.h" +#include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) screen_retriever_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); + screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f16b4c3..62f151f 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,7 +3,9 @@ # list(APPEND FLUTTER_PLUGIN_LIST + screen_retriever url_launcher_linux + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8236f57..ea24344 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,12 @@ import FlutterMacOS import Foundation +import screen_retriever import url_launcher_macos +import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 8fe732b..c1a95d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -86,6 +86,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_colorpicker: + dependency: "direct main" + description: + name: flutter_colorpicker + sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" flutter_lints: dependency: "direct dev" description: @@ -200,6 +208,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.2.1" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.9" sky_engine: dependency: transitive description: flutter @@ -348,6 +364,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.1.4-beta" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: dcc865277f26a7dad263a47d0e405d77e21f12cb71f30333a52710a408690bd7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.7" sdks: dart: ">=3.1.1 <4.0.0" flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index 160ec64..d4f622c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_colorpicker: ^1.0.3 + window_manager: ^0.3.7 adaptive_navigation: ^0.0.4 go_router: ^10.1.0 provider: 6.0.5 diff --git a/test/parser/back.dart b/test/parser/back.dart new file mode 100644 index 0000000..aaf1b70 --- /dev/null +++ b/test/parser/back.dart @@ -0,0 +1,9 @@ +void main(){ + print(backPath('/a/c/d?k=v')); +} + +String backPath(String path){ + Uri uri = Uri.parse(path); + List parts = List.of(uri.pathSegments)..removeLast(); + return '/${parts.join('/')}'; +} \ No newline at end of file diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 4f78848..fe7ec1c 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,15 @@ #include "generated_plugin_registrant.h" +#include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + ScreenRetrieverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 88b22e5..fb2dea6 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,9 @@ # list(APPEND FLUTTER_PLUGIN_LIST + screen_retriever url_launcher_windows + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index 955ee30..10bc521 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -28,7 +28,8 @@ bool FlutterWindow::OnCreate() { SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); +// this->Show(); + //delete this->Show() }); // Flutter can complete the first frame before the "show window" callback is diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp index 60608d0..d1f584e 100644 --- a/windows/runner/win32_window.cpp +++ b/windows/runner/win32_window.cpp @@ -135,7 +135,9 @@ bool Win32Window::Create(const std::wstring& title, double scale_factor = dpi / 96.0; HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW, +// window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + window_class, title.c_str(), + WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), Scale(size.width, scale_factor), Scale(size.height, scale_factor), nullptr, nullptr, GetModuleHandle(nullptr), this);