v10
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ class DragToMoveWrap extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if(kIsWeb) return child;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
onPanStart: (details) {
|
onPanStart: (details) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import 'v8/app.dart';
|
import 'v10/app.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@@ -14,8 +14,7 @@ void main() {
|
|||||||
|
|
||||||
|
|
||||||
void setSize() async{
|
void setSize() async{
|
||||||
|
if(kIsWeb||Platform.isAndroid||Platform.isIOS) return;
|
||||||
if(Platform.isWindows||Platform.isWindows||Platform.isLinux){
|
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
WindowOptions windowOptions = const WindowOptions(
|
WindowOptions windowOptions = const WindowOptions(
|
||||||
size: Size(800, 540),
|
size: Size(800, 540),
|
||||||
@@ -29,6 +28,4 @@ void setSize() async{
|
|||||||
await windowManager.show();
|
await windowManager.show();
|
||||||
await windowManager.focus();
|
await windowManager.focus();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'views/navigator_scope.dart';
|
import 'views/navigator_scope.dart';
|
||||||
import 'iroute.dart';
|
import 'iroute.dart';
|
||||||
@@ -7,13 +8,10 @@ import 'route_history_manager.dart';
|
|||||||
import 'routes.dart';
|
import 'routes.dart';
|
||||||
import 'views/not_find_view.dart';
|
import 'views/not_find_view.dart';
|
||||||
|
|
||||||
AppRouterDelegate router = AppRouterDelegate(
|
AppRouterDelegate router = AppRouterDelegate(node: rootRoute);
|
||||||
initial: IRouteConfig(
|
|
||||||
uri: Uri.parse('/app/color'),
|
|
||||||
),
|
|
||||||
node: appRoute);
|
|
||||||
|
|
||||||
class AppRouterDelegate extends RouterDelegate<IRouteConfig> with ChangeNotifier {
|
class AppRouterDelegate extends RouterDelegate<IRouteConfig>
|
||||||
|
with ChangeNotifier {
|
||||||
/// 核心数据,路由配置数据列表
|
/// 核心数据,路由配置数据列表
|
||||||
final List<IRouteConfig> _configs = [];
|
final List<IRouteConfig> _configs = [];
|
||||||
|
|
||||||
@@ -25,18 +23,16 @@ class AppRouterDelegate extends RouterDelegate<IRouteConfig> with ChangeNotifier
|
|||||||
|
|
||||||
final IRouteNode node;
|
final IRouteNode node;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
IRouteConfig get currentConfiguration => current;
|
IRouteConfig? get currentConfiguration {
|
||||||
|
if(_configs.isEmpty) return null;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
AppRouterDelegate({
|
AppRouterDelegate({
|
||||||
this.notFindPageBuilder,
|
this.notFindPageBuilder,
|
||||||
required this.node,
|
required this.node,
|
||||||
required IRouteConfig initial,
|
});
|
||||||
}) {
|
|
||||||
_configs.add(initial);
|
|
||||||
_historyManager.recode(initial);
|
|
||||||
}
|
|
||||||
|
|
||||||
Page _defaultNotFindPageBuilder(_, __) => const MaterialPage(
|
Page _defaultNotFindPageBuilder(_, __) => const MaterialPage(
|
||||||
child: Material(child: NotFindPage()),
|
child: Material(child: NotFindPage()),
|
||||||
@@ -84,6 +80,7 @@ class AppRouterDelegate extends RouterDelegate<IRouteConfig> with ChangeNotifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
if (config.forResult) {
|
if (config.forResult) {
|
||||||
return _completerMap[value]!.future;
|
return _completerMap[value]!.future;
|
||||||
}
|
}
|
||||||
@@ -174,5 +171,14 @@ class AppRouterDelegate extends RouterDelegate<IRouteConfig> with ChangeNotifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setNewRoutePath(configuration) async {}
|
Future<void> setNewRoutePath(IRouteConfig configuration) async{
|
||||||
|
changeRoute(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setInitialRoutePath(IRouteConfig configuration) {
|
||||||
|
_configs.add(configuration);
|
||||||
|
_historyManager.recode(configuration);
|
||||||
|
return super.setInitialRoutePath(configuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'iroute_config.dart';
|
import 'iroute_config.dart';
|
||||||
|
|
||||||
class AppRouterParser extends RouteInformationParser<IRouteConfig>{
|
class AppRouterParser extends RouteInformationParser<IRouteConfig> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RouteInformation restoreRouteInformation(IRouteConfig configuration) {
|
RouteInformation restoreRouteInformation(IRouteConfig configuration) {
|
||||||
return RouteInformation(
|
return RouteInformation(uri: configuration.uri);
|
||||||
uri: configuration.uri
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<IRouteConfig> parseRouteInformationWithDependencies(
|
Future<IRouteConfig> parseRouteInformationWithDependencies(
|
||||||
RouteInformation routeInformation, BuildContext context) async{
|
RouteInformation routeInformation,
|
||||||
return IRouteConfig(uri: routeInformation.uri);
|
BuildContext context,
|
||||||
|
) {
|
||||||
|
return SynchronousFuture(IRouteConfig(uri: routeInformation.uri));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ abstract class IRouteNode {
|
|||||||
if (node.children.isNotEmpty) {
|
if (node.children.isNotEmpty) {
|
||||||
target = prefix + target;
|
target = prefix + target;
|
||||||
|
|
||||||
List<IRouteNode> nodes =
|
List<IRouteNode> nodes = node.children.where((e) => e.path == target).toList();
|
||||||
node.children.where((e) => e.path == target).toList();
|
|
||||||
bool match = nodes.isNotEmpty;
|
bool match = nodes.isNotEmpty;
|
||||||
if (match) {
|
if (match) {
|
||||||
IRouteNode matched = nodes.first;
|
IRouteNode matched = nodes.first;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
|
|||||||
tail: Padding(
|
tail: Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 6.0),
|
padding: const EdgeInsets.only(bottom: 6.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'V0.0.9',
|
'V0.0.10',
|
||||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -63,14 +63,14 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
|
|||||||
void _onDestinationSelected(int index) {
|
void _onDestinationSelected(int index) {
|
||||||
String path = deskNavBarMenus[index].path!;
|
String path = deskNavBarMenus[index].path!;
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
router.changePath(path, keepAlive: true);
|
router.changePath(path, keepAlive: true,recordHistory: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (index == 4) {
|
if (index == 4) {
|
||||||
router.changePath(path, style: RouteStyle.push);
|
router.changePath(path, style: RouteStyle.push,recordHistory: true);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
router.changePath(path);
|
router.changePath(path,recordHistory: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,43 +4,40 @@ import 'navigation/router/app_router_delegate.dart';
|
|||||||
import '../pages/sort/provider/state.dart';
|
import '../pages/sort/provider/state.dart';
|
||||||
import 'navigation/transition/fade_page_transitions_builder.dart';
|
import 'navigation/transition/fade_page_transitions_builder.dart';
|
||||||
|
|
||||||
|
|
||||||
class UnitApp extends StatelessWidget {
|
class UnitApp extends StatelessWidget {
|
||||||
const UnitApp({super.key});
|
const UnitApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return SortStateScope(
|
return SortStateScope(
|
||||||
notifier: SortState(),
|
notifier: SortState(),
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
routerDelegate: router,
|
routerDelegate: router,
|
||||||
routeInformationParser: AppRouterParser(),
|
routeInformationParser: AppRouterParser(),
|
||||||
theme: ThemeData(
|
routeInformationProvider: PlatformRouteInformationProvider(
|
||||||
|
initialRouteInformation: RouteInformation(uri: Uri.parse('/app/color')),
|
||||||
|
),
|
||||||
|
theme: ThemeData(
|
||||||
fontFamily: "宋体",
|
fontFamily: "宋体",
|
||||||
scaffoldBackgroundColor: Colors.white,
|
scaffoldBackgroundColor: Colors.white,
|
||||||
pageTransitionsTheme: const PageTransitionsTheme(
|
pageTransitionsTheme: const PageTransitionsTheme(builders: {
|
||||||
builders: {
|
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
||||||
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
TargetPlatform.macOS: FadePageTransitionsBuilder(),
|
||||||
TargetPlatform.macOS: FadePageTransitionsBuilder(),
|
TargetPlatform.windows: FadePageTransitionsBuilder(),
|
||||||
TargetPlatform.windows: FadePageTransitionsBuilder(),
|
TargetPlatform.linux: FadePageTransitionsBuilder(),
|
||||||
TargetPlatform.linux: FadePageTransitionsBuilder(),
|
}),
|
||||||
}
|
appBarTheme: const AppBarTheme(
|
||||||
),
|
elevation: 0,
|
||||||
appBarTheme: const AppBarTheme(
|
iconTheme: IconThemeData(color: Colors.black),
|
||||||
elevation: 0,
|
titleTextStyle: TextStyle(
|
||||||
iconTheme: IconThemeData(color: Colors.black),
|
color: Colors.black,
|
||||||
titleTextStyle: TextStyle(
|
fontSize: 18,
|
||||||
color: Colors.black,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 18,
|
))),
|
||||||
fontWeight: FontWeight.bold,
|
debugShowCheckedModeBanner: false,
|
||||||
))),
|
// home: AppNavigation()
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
// home: AppNavigation()
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
lib/v11/app.dart
Normal file
1
lib/v11/app.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'app/unit_app.dart';
|
||||||
184
lib/v11/app/navigation/router/app_router_delegate.dart
Normal file
184
lib/v11/app/navigation/router/app_router_delegate.dart
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'views/navigator_scope.dart';
|
||||||
|
import 'iroute.dart';
|
||||||
|
import 'iroute_config.dart';
|
||||||
|
import 'route_history_manager.dart';
|
||||||
|
import 'routes.dart';
|
||||||
|
import 'views/not_find_view.dart';
|
||||||
|
|
||||||
|
AppRouterDelegate router = AppRouterDelegate(node: rootRoute);
|
||||||
|
|
||||||
|
class AppRouterDelegate extends RouterDelegate<IRouteConfig>
|
||||||
|
with ChangeNotifier {
|
||||||
|
/// 核心数据,路由配置数据列表
|
||||||
|
final List<IRouteConfig> _configs = [];
|
||||||
|
|
||||||
|
String get path => current.uri.path;
|
||||||
|
|
||||||
|
IRouteConfig get current => _configs.last;
|
||||||
|
|
||||||
|
final IRoutePageBuilder? notFindPageBuilder;
|
||||||
|
|
||||||
|
final IRouteNode node;
|
||||||
|
|
||||||
|
@override
|
||||||
|
IRouteConfig? get currentConfiguration {
|
||||||
|
if(_configs.isEmpty) return null;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppRouterDelegate({
|
||||||
|
this.notFindPageBuilder,
|
||||||
|
required this.node,
|
||||||
|
});
|
||||||
|
|
||||||
|
Page _defaultNotFindPageBuilder(_, __) => const MaterialPage(
|
||||||
|
child: Material(child: NotFindPage()),
|
||||||
|
);
|
||||||
|
|
||||||
|
final RouteHistoryManager _historyManager = RouteHistoryManager();
|
||||||
|
|
||||||
|
RouteHistoryManager get historyManager => _historyManager;
|
||||||
|
|
||||||
|
/// 历史回退操作
|
||||||
|
/// 详见: [RouteHistoryManager.back]
|
||||||
|
void back() => _historyManager.back(changeRoute);
|
||||||
|
|
||||||
|
/// 撤销回退操作
|
||||||
|
/// 详见: [RouteHistoryManager.revocation]
|
||||||
|
void revocation() => _historyManager.revocation(changeRoute);
|
||||||
|
|
||||||
|
void closeHistory(int index) {
|
||||||
|
_historyManager.close(index);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearHistory() {
|
||||||
|
_historyManager.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// final List<IRouteConfig> _pathStack = [];
|
||||||
|
|
||||||
|
bool get canPop => _configs.where((e) => e.routeStyle == RouteStyle.push).isNotEmpty;
|
||||||
|
|
||||||
|
final Map<String, Completer<dynamic>> _completerMap = {};
|
||||||
|
|
||||||
|
FutureOr<dynamic> changeRoute(IRouteConfig config) {
|
||||||
|
String value = config.uri.path;
|
||||||
|
if (current == config) null;
|
||||||
|
_handleChangeStyle(config);
|
||||||
|
|
||||||
|
if (config.forResult) {
|
||||||
|
_completerMap[value] = Completer();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.recordHistory) {
|
||||||
|
_historyManager.recode(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
if (config.forResult) {
|
||||||
|
return _completerMap[value]!.future;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleChangeStyle(IRouteConfig config) {
|
||||||
|
switch (config.routeStyle) {
|
||||||
|
case RouteStyle.push:
|
||||||
|
if (_configs.contains(config)) {
|
||||||
|
_configs.remove(config);
|
||||||
|
}
|
||||||
|
_configs.add(config);
|
||||||
|
break;
|
||||||
|
case RouteStyle.replace:
|
||||||
|
List<IRouteConfig> liveRoutes =
|
||||||
|
_configs.where((e) => e.keepAlive && e != config).toList();
|
||||||
|
_configs.clear();
|
||||||
|
_configs.addAll([...liveRoutes, config]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<dynamic> changePath(
|
||||||
|
String value, {
|
||||||
|
bool forResult = false,
|
||||||
|
Object? extra,
|
||||||
|
bool keepAlive = false,
|
||||||
|
bool recordHistory = false,
|
||||||
|
RouteStyle style = RouteStyle.replace,
|
||||||
|
}) {
|
||||||
|
return changeRoute(IRouteConfig(
|
||||||
|
uri: Uri.parse(value),
|
||||||
|
forResult: forResult,
|
||||||
|
extra: extra,
|
||||||
|
routeStyle: style,
|
||||||
|
keepAlive: keepAlive,
|
||||||
|
recordHistory: recordHistory,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return NavigatorScope(
|
||||||
|
node: node,
|
||||||
|
onPopPage: _onPopPage,
|
||||||
|
configs: _configs,
|
||||||
|
notFindPageBuilder: (notFindPageBuilder ?? _defaultNotFindPageBuilder),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> popRoute() async {
|
||||||
|
print('=======popRoute=========');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backStack() {
|
||||||
|
if (_configs.isNotEmpty) {
|
||||||
|
_configs.removeLast();
|
||||||
|
if (_configs.isNotEmpty) {
|
||||||
|
changeRoute(_configs.last);
|
||||||
|
} else {
|
||||||
|
changeRoute(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _onPopPage(Route route, result) {
|
||||||
|
if (_completerMap.containsKey(path)) {
|
||||||
|
_completerMap[path]?.complete(result);
|
||||||
|
_completerMap.remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canPop) {
|
||||||
|
_configs.removeLast();
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
changePath(backPath(path), recordHistory: false);
|
||||||
|
}
|
||||||
|
return route.didPop(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
String backPath(String path) {
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
if (uri.pathSegments.length == 1) return path;
|
||||||
|
List<String> parts = List.of(uri.pathSegments)..removeLast();
|
||||||
|
return '/${parts.join('/')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setNewRoutePath(IRouteConfig configuration) async{
|
||||||
|
changeRoute(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setInitialRoutePath(IRouteConfig configuration) {
|
||||||
|
_configs.add(configuration);
|
||||||
|
_historyManager.recode(configuration);
|
||||||
|
return super.setInitialRoutePath(configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/v11/app/navigation/router/app_router_parser.dart
Normal file
22
lib/v11/app/navigation/router/app_router_parser.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'iroute_config.dart';
|
||||||
|
|
||||||
|
class AppRouterParser extends RouteInformationParser<IRouteConfig> {
|
||||||
|
@override
|
||||||
|
RouteInformation restoreRouteInformation(IRouteConfig configuration) {
|
||||||
|
return RouteInformation(uri: configuration.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<IRouteConfig> parseRouteInformationWithDependencies(
|
||||||
|
RouteInformation routeInformation,
|
||||||
|
BuildContext context,
|
||||||
|
) {
|
||||||
|
if(routeInformation.state is IRouteConfig){
|
||||||
|
return SynchronousFuture(routeInformation.state as IRouteConfig);
|
||||||
|
}
|
||||||
|
return SynchronousFuture(IRouteConfig(uri: routeInformation.uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
156
lib/v11/app/navigation/router/iroute.dart
Normal file
156
lib/v11/app/navigation/router/iroute.dart
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/v9/app/navigation/router/views/navigator_scope.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<IRouteNode> children;
|
||||||
|
|
||||||
|
const IRouteNode({
|
||||||
|
required this.path,
|
||||||
|
required this.children,
|
||||||
|
});
|
||||||
|
|
||||||
|
Page? createPage(BuildContext context, IRouteConfig config);
|
||||||
|
|
||||||
|
List<IRouteNode> find(
|
||||||
|
String input,
|
||||||
|
) {
|
||||||
|
String prefix = '/';
|
||||||
|
if (this is CellIRoute) {
|
||||||
|
input = input.replaceFirst(path, '');
|
||||||
|
if (path != '/') {
|
||||||
|
prefix = path + "/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return findNodes(this, Uri.parse(input), 0, prefix, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IRouteNode> findNodes(
|
||||||
|
IRouteNode node,
|
||||||
|
Uri uri,
|
||||||
|
int deep,
|
||||||
|
String prefix,
|
||||||
|
List<IRouteNode> result,
|
||||||
|
) {
|
||||||
|
List<String> parts = uri.pathSegments;
|
||||||
|
if (deep > parts.length - 1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
String target = parts[deep];
|
||||||
|
if (node.children.isNotEmpty) {
|
||||||
|
target = prefix + target;
|
||||||
|
|
||||||
|
List<IRouteNode> nodes = node.children.where((e) => e.path == target).toList();
|
||||||
|
bool match = nodes.isNotEmpty;
|
||||||
|
if (match) {
|
||||||
|
IRouteNode matched = nodes.first;
|
||||||
|
result.add(matched);
|
||||||
|
String nextPrefix = '${matched.path}/';
|
||||||
|
findNodes(matched, uri, ++deep, nextPrefix, result);
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CellBuilder = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
IRouteConfig config,
|
||||||
|
Widget navigator,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef CellIRoutePageBuilder = Page? Function(
|
||||||
|
BuildContext context,
|
||||||
|
IRouteConfig data,
|
||||||
|
Widget child,
|
||||||
|
);
|
||||||
|
|
||||||
|
class CellIRoute extends IRouteNode {
|
||||||
|
final CellBuilder cellBuilder;
|
||||||
|
final CellIRoutePageBuilder? pageBuilder;
|
||||||
|
|
||||||
|
const CellIRoute({
|
||||||
|
required this.cellBuilder,
|
||||||
|
this.pageBuilder,
|
||||||
|
required super.path,
|
||||||
|
required super.children,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Page? createPage(BuildContext context, IRouteConfig config) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Page? createCellPage(BuildContext context, IRouteConfig config,
|
||||||
|
Widget child) {
|
||||||
|
if (pageBuilder != null) {
|
||||||
|
return pageBuilder!(context, config, child);
|
||||||
|
}
|
||||||
|
print("======CellIRoute#createCellPage${config.pageKey}=================");
|
||||||
|
return MaterialPage(
|
||||||
|
child: child,
|
||||||
|
key: config.pageKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
lib/v11/app/navigation/router/iroute_config.dart
Normal file
71
lib/v11/app/navigation/router/iroute_config.dart
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum RouteStyle{
|
||||||
|
push,
|
||||||
|
replace,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class IRouteConfig {
|
||||||
|
final Object? extra;
|
||||||
|
final bool forResult;
|
||||||
|
final Uri uri;
|
||||||
|
final bool keepAlive;
|
||||||
|
final RouteStyle routeStyle;
|
||||||
|
final bool recordHistory;
|
||||||
|
|
||||||
|
const IRouteConfig({
|
||||||
|
this.extra,
|
||||||
|
required this.uri,
|
||||||
|
this.forResult = false,
|
||||||
|
this.keepAlive = false,
|
||||||
|
this.routeStyle = RouteStyle.replace,
|
||||||
|
this.recordHistory = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
String get path => uri.path;
|
||||||
|
|
||||||
|
IRouteConfig copyWith({
|
||||||
|
Object? extra,
|
||||||
|
bool? forResult,
|
||||||
|
bool? keepAlive,
|
||||||
|
bool? recordHistory,
|
||||||
|
String? path,
|
||||||
|
}) =>
|
||||||
|
IRouteConfig(
|
||||||
|
extra: extra ?? this.extra,
|
||||||
|
forResult: forResult ?? this.forResult,
|
||||||
|
keepAlive: keepAlive ?? this.keepAlive,
|
||||||
|
recordHistory: recordHistory ?? this.recordHistory,
|
||||||
|
uri: path!=null?Uri.parse(path):uri,
|
||||||
|
);
|
||||||
|
|
||||||
|
ValueKey get pageKey => ValueKey(hashCode);
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'IRouteConfig{extra: $extra, forResult: $forResult, uri: $uri, keepAlive: $keepAlive, routeStyle: $routeStyle, recordHistory: $recordHistory}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is IRouteConfig &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
extra == other.extra &&
|
||||||
|
forResult == other.forResult &&
|
||||||
|
uri == other.uri &&
|
||||||
|
keepAlive == other.keepAlive &&
|
||||||
|
routeStyle == other.routeStyle &&
|
||||||
|
recordHistory == other.recordHistory;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
extra.hashCode ^
|
||||||
|
forResult.hashCode ^
|
||||||
|
uri.hashCode ^
|
||||||
|
keepAlive.hashCode ^
|
||||||
|
routeStyle.hashCode ^
|
||||||
|
recordHistory.hashCode;
|
||||||
|
}
|
||||||
47
lib/v11/app/navigation/router/route_history_manager.dart
Normal file
47
lib/v11/app/navigation/router/route_history_manager.dart
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import 'iroute_config.dart';
|
||||||
|
|
||||||
|
typedef OnRouteChange = void Function(IRouteConfig config);
|
||||||
|
|
||||||
|
class RouteHistoryManager{
|
||||||
|
final List<IRouteConfig> _histories = [];
|
||||||
|
final List<IRouteConfig> _backHistories = [];
|
||||||
|
|
||||||
|
List<IRouteConfig> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
95
lib/v11/app/navigation/router/routes.dart
Normal file
95
lib/v11/app/navigation/router/routes.dart
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../../../pages/login/login.dart';
|
||||||
|
import '../transition/no_transition_page.dart';
|
||||||
|
import '../../../pages/sort/views/player/sort_player.dart';
|
||||||
|
import 'iroute_config.dart';
|
||||||
|
import '../views/app_navigation.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/sort_page.dart';
|
||||||
|
import '../../../pages/sort/views/settings/sort_setting.dart';
|
||||||
|
|
||||||
|
CellIRoute appRoute = CellIRoute(
|
||||||
|
cellBuilder: (_, __, navigator) => AppNavigation(
|
||||||
|
navigator: navigator,
|
||||||
|
),
|
||||||
|
path: '/app',
|
||||||
|
children: [
|
||||||
|
IRoute(
|
||||||
|
path: '/app/color',
|
||||||
|
widget: ColorPage(),
|
||||||
|
children: [
|
||||||
|
IRoute(path: '/app/color/detail', widgetBuilder: _buildColorDetail),
|
||||||
|
IRoute(path: '/app/color/add', widget: ColorAddPage()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const IRoute(path: '/app/counter', widget: CounterPage()),
|
||||||
|
CellIRoute(
|
||||||
|
cellBuilder: (_, __, navigator) => SortNavigation(navigator: navigator),
|
||||||
|
// pageBuilder: (_,config,child)=> NoTransitionPage(
|
||||||
|
// child: child,
|
||||||
|
// key: config.pageKey
|
||||||
|
// ),
|
||||||
|
path: '/app/sort',
|
||||||
|
children: [
|
||||||
|
const IRoute(
|
||||||
|
path: '/app/sort/settings',
|
||||||
|
widget: SortSettings(),
|
||||||
|
),
|
||||||
|
const IRoute(
|
||||||
|
path: '/app/sort/player',
|
||||||
|
widget: SortPlayer(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const IRoute(path: '/app/user', widget: UserPage()),
|
||||||
|
const IRoute(path: '/app/settings', widget: SettingPage()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
IRoute rootRoute = IRoute(path: '/', children: [
|
||||||
|
appRoute,
|
||||||
|
const IRoute(
|
||||||
|
path: '/login',
|
||||||
|
widget: LoginPage()
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
Widget? _buildColorDetail(BuildContext context, IRouteConfig data) {
|
||||||
|
final Map<String, String> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> kRouteLabelMap = {
|
||||||
|
'/app': '',
|
||||||
|
'/app/color': '颜色板',
|
||||||
|
'/app/color/add': '添加颜色',
|
||||||
|
'/app/color/detail': '颜色详情',
|
||||||
|
'/app/counter': '计数器',
|
||||||
|
'/app/sort': '排序算法',
|
||||||
|
'/app/sort/player': '演示',
|
||||||
|
'/app/sort/settings': '排序配置',
|
||||||
|
'/app/user': '我的',
|
||||||
|
'/app/settings': '系统设置',
|
||||||
|
};
|
||||||
|
|
||||||
|
const List<MenuMeta> deskNavBarMenus = [
|
||||||
|
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined, path: '/app/color'),
|
||||||
|
MenuMeta(label: '计数器', icon: Icons.add_chart, path: '/app/counter'),
|
||||||
|
MenuMeta(label: '排序', icon: Icons.sort, path: '/app/sort/player'),
|
||||||
|
MenuMeta(label: '我的', icon: Icons.person, path: '/app/user'),
|
||||||
|
MenuMeta(label: '设置', icon: Icons.settings, path: '/app/settings'),
|
||||||
|
];
|
||||||
109
lib/v11/app/navigation/router/views/navigator_scope.dart
Normal file
109
lib/v11/app/navigation/router/views/navigator_scope.dart
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../iroute.dart';
|
||||||
|
|
||||||
|
import '../iroute_config.dart';
|
||||||
|
|
||||||
|
class NavigatorScope extends StatefulWidget {
|
||||||
|
final IRouteNode node;
|
||||||
|
final PopPageCallback onPopPage;
|
||||||
|
final List<IRouteConfig> configs;
|
||||||
|
final IRoutePageBuilder notFindPageBuilder;
|
||||||
|
|
||||||
|
const NavigatorScope({
|
||||||
|
super.key,
|
||||||
|
required this.node,
|
||||||
|
required this.onPopPage,
|
||||||
|
required this.configs,
|
||||||
|
required this.notFindPageBuilder,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NavigatorScope> createState() => _NavigatorScopeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavigatorScopeState extends State<NavigatorScope> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget content = Navigator(
|
||||||
|
onPopPage: widget.onPopPage,
|
||||||
|
pages: _buildPages(context, widget.configs),
|
||||||
|
);
|
||||||
|
|
||||||
|
if(widget.node is CellIRoute){
|
||||||
|
content = (widget.node as CellIRoute).cellBuilder(context,widget.configs.last,content);
|
||||||
|
}
|
||||||
|
return HeroControllerScope(
|
||||||
|
controller: MaterialApp.createMaterialHeroController(),
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Page> _buildPages(BuildContext context, List<IRouteConfig> configs) {
|
||||||
|
IRouteConfig top = configs.last;
|
||||||
|
List<IRouteConfig> bottoms =
|
||||||
|
configs.sublist(0, configs.length - 1).toList();
|
||||||
|
List<Page> pages = [];
|
||||||
|
List<Page> topPages = _buildPageByPathFromTree(context, top);
|
||||||
|
pages = _buildLivePageByPathList(context, bottoms, top, topPages);
|
||||||
|
pages.addAll(topPages);
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Page> _buildLivePageByPathList(
|
||||||
|
BuildContext context,
|
||||||
|
List<IRouteConfig> paths,
|
||||||
|
IRouteConfig curConfig,
|
||||||
|
List<Page> curPages,
|
||||||
|
) {
|
||||||
|
List<Page> pages = [];
|
||||||
|
if (paths.isNotEmpty) {
|
||||||
|
for (IRouteConfig path in paths) {
|
||||||
|
if (path != curConfig) {
|
||||||
|
pages.addAll(_buildPageByPathFromTree(context, path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 去除和 curPages 中重复的界面
|
||||||
|
pages.removeWhere((page) => curPages.map((e) => e.key).contains(page.key));
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Page> _buildPageByPathFromTree(
|
||||||
|
BuildContext context, IRouteConfig config) {
|
||||||
|
List<Page> result = [];
|
||||||
|
List<IRouteNode> iRoutes = widget.node.find(config.path);
|
||||||
|
|
||||||
|
if (iRoutes.isNotEmpty) {
|
||||||
|
for (int i = 0; i < iRoutes.length; i++) {
|
||||||
|
IRouteNode iroute = iRoutes[i];
|
||||||
|
IRouteConfig fixConfig = config;
|
||||||
|
|
||||||
|
if(iroute.path!=config.uri.path){
|
||||||
|
fixConfig = IRouteConfig(uri: Uri.parse(iroute.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Page? page;
|
||||||
|
if (iroute is NotFindNode) {
|
||||||
|
page = widget.notFindPageBuilder(context, config);
|
||||||
|
} else if (iroute is CellIRoute) {
|
||||||
|
Widget scope = NavigatorScope(
|
||||||
|
node: iroute,
|
||||||
|
onPopPage: widget.onPopPage,
|
||||||
|
configs: widget.configs,
|
||||||
|
notFindPageBuilder: widget.notFindPageBuilder,
|
||||||
|
);
|
||||||
|
page = iroute.createCellPage(context, fixConfig, scope);
|
||||||
|
} else {
|
||||||
|
page = iroute.createPage(context, fixConfig);
|
||||||
|
}
|
||||||
|
if (page != null) {
|
||||||
|
result.add(page);
|
||||||
|
}
|
||||||
|
if (iroute is CellIRoute) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
lib/v11/app/navigation/router/views/not_find_view.dart
Normal file
25
lib/v11/app/navigation/router/views/not_find_view.dart
Normal file
@@ -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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../app_router_delegate.dart';
|
||||||
|
class RouteBackIndicator extends StatefulWidget {
|
||||||
|
const RouteBackIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouteBackIndicator> createState() => _RouteBackIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouteBackIndicatorState extends State<RouteBackIndicator> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
router.addListener(_onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
router.removeListener(_onChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if(router.canPop){
|
||||||
|
return MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: router.backStack,
|
||||||
|
child: Container(
|
||||||
|
width: 26,
|
||||||
|
height: 26,
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Color(0xffE3E5E7),
|
||||||
|
borderRadius: BorderRadius.circular(6)
|
||||||
|
),
|
||||||
|
child: Icon(Icons.arrow_back_ios_new,size: 14,)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onChange() {
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FadePageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
|
|
||||||
|
const FadePageTransitionsBuilder();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildTransitions<T>(
|
||||||
|
PageRoute<T>? route,
|
||||||
|
BuildContext? context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Animation<double> 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<double> animation;
|
||||||
|
final Animation<double> secondaryAnimation;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var curveTween = CurveTween(curve: Curves.easeIn);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation.drive(curveTween),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
lib/v11/app/navigation/transition/fade_transition_page.dart
Normal file
52
lib/v11/app/navigation/transition/fade_transition_page.dart
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// 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<T> extends Page<T> {
|
||||||
|
final Widget child;
|
||||||
|
final Duration duration;
|
||||||
|
|
||||||
|
const FadeTransitionPage({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.duration = const Duration(milliseconds: 300),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Route<T> createRoute(BuildContext context) => PageBasedFadeTransitionRoute<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageBasedFadeTransitionRoute<T> extends PageRoute<T> {
|
||||||
|
final FadeTransitionPage<T> _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<double> animation,
|
||||||
|
Animation<double> secondaryAnimation) {
|
||||||
|
var curveTween = CurveTween(curve: Curves.easeIn);
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation.drive(curveTween),
|
||||||
|
child: (settings as FadeTransitionPage).child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
||||||
|
Animation<double> secondaryAnimation, Widget child) =>
|
||||||
|
child;
|
||||||
|
}
|
||||||
47
lib/v11/app/navigation/transition/no_transition_page.dart
Normal file
47
lib/v11/app/navigation/transition/no_transition_page.dart
Normal file
@@ -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<T> extends Page<T> {
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const NoTransitionPage({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Route<T> createRoute(BuildContext context) => NoTransitionRoute<T>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoTransitionRoute<T> extends PageRoute<T> {
|
||||||
|
|
||||||
|
final NoTransitionPage<T> _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<double> animation,
|
||||||
|
Animation<double> secondaryAnimation) {
|
||||||
|
return (settings as NoTransitionPage).child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildTransitions(BuildContext context, Animation<double> animation,
|
||||||
|
Animation<double> secondaryAnimation, Widget child) =>
|
||||||
|
child;
|
||||||
|
}
|
||||||
51
lib/v11/app/navigation/views/app_navigation.dart
Normal file
51
lib/v11/app/navigation/views/app_navigation.dart
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../router/app_router_delegate.dart';
|
||||||
|
import 'app_navigation_rail.dart';
|
||||||
|
import 'app_top_bar/app_top_bar.dart';
|
||||||
|
|
||||||
|
class AppNavigation extends StatefulWidget {
|
||||||
|
final Widget navigator;
|
||||||
|
|
||||||
|
const AppNavigation({super.key,required this.navigator});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AppNavigation> createState() => _AppNavigationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppNavigationState extends State<AppNavigation> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
print('======_AppNavigationState#initState==============');
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
print('======_AppNavigationState#dispose==============');
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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: widget.navigator,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
lib/v11/app/navigation/views/app_navigation_rail.dart
Normal file
80
lib/v11/app/navigation/views/app_navigation_rail.dart
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../router/app_router_delegate.dart';
|
||||||
|
import '../router/iroute_config.dart';
|
||||||
|
import '../router/routes.dart';
|
||||||
|
|
||||||
|
class AppNavigationRail extends StatefulWidget {
|
||||||
|
const AppNavigationRail({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AppNavigationRail> createState() => _AppNavigationRailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppNavigationRailState extends State<AppNavigationRail> {
|
||||||
|
|
||||||
|
@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.10',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: const Color(0xff3975c6),
|
||||||
|
onDestinationSelected: _onDestinationSelected,
|
||||||
|
selectedIndex: activeIndex,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegExp _segReg = RegExp(r'/app/\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!.contains(target??''));
|
||||||
|
if (index == -1) return null;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDestinationSelected(int index) {
|
||||||
|
String path = deskNavBarMenus[index].path!;
|
||||||
|
if (index == 1) {
|
||||||
|
router.changePath(path, keepAlive: true,recordHistory: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index == 4) {
|
||||||
|
router.changePath(path, style: RouteStyle.push,recordHistory: true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
router.changePath(path,recordHistory: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRouterChange() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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<String>? onSubmit;
|
||||||
|
const AppRouterEditor({super.key, this.onSubmit});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AppRouterEditor> createState() => _AppRouterEditorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppRouterEditorState extends State<AppRouterEditor> {
|
||||||
|
|
||||||
|
final TextEditingController _controller = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_onRouteChange();
|
||||||
|
router.addListener(_onRouteChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRouteChange() {
|
||||||
|
_controller.text=router.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
router.removeListener(_onRouteChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
child: CupertinoTextField(
|
||||||
|
controller: _controller,
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32),
|
||||||
|
placeholder: '输入路由地址导航',
|
||||||
|
onSubmitted: widget.onSubmit,
|
||||||
|
decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart
Normal file
107
lib/v11/app/navigation/views/app_top_bar/app_top_bar.dart
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../../router/app_router_delegate.dart';
|
||||||
|
import '../../router/routes.dart';
|
||||||
|
import '../../router/views/route_back_indicator.dart';
|
||||||
|
import 'app_router_editor.dart';
|
||||||
|
import 'history_view_icon.dart';
|
||||||
|
import 'route_history_button.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 RouteBackIndicator(),
|
||||||
|
const RouterIndicator(),
|
||||||
|
Expanded(
|
||||||
|
child: Row(children: [
|
||||||
|
const Spacer(),
|
||||||
|
RouteHistoryButton(),
|
||||||
|
const SizedBox(width: 12,),
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: AppRouterEditor(
|
||||||
|
onSubmit: (path) => router.changePath(path),
|
||||||
|
)),
|
||||||
|
const SizedBox(width: 12,),
|
||||||
|
HistoryViewIcon(),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: VerticalDivider(
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
const WindowButtons()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterIndicator extends StatefulWidget {
|
||||||
|
const RouterIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouterIndicator> createState() => _RouterIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _RouterIndicatorState extends State<RouterIndicator> {
|
||||||
|
@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.changePath(item.to!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRouterChange() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BreadcrumbItem> pathToBreadcrumbItems(String path) {
|
||||||
|
Uri uri = Uri.parse(path);
|
||||||
|
List<BreadcrumbItem> 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] ?? '未知路由';
|
||||||
|
if(label.isNotEmpty){
|
||||||
|
result.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
158
lib/v11/app/navigation/views/app_top_bar/history_view_icon.dart
Normal file
158
lib/v11/app/navigation/views/app_top_bar/history_view_icon.dart
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../../router/app_router_delegate.dart';
|
||||||
|
import '../../router/routes.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<HistoryItem> createState() => _HistoryItemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HistoryItemState extends State<HistoryItem> {
|
||||||
|
@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<HistoryPanel> createState() => _HistoryPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HistoryPanelState extends State<HistoryPanel> {
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
router.addListener(_onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
router.removeListener(_onChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<IRouteConfig> 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(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<RouteHistoryButton> createState() => _RouteHistoryButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
||||||
|
@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(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/v11/app/unit_app.dart
Normal file
43
lib/v11/app/unit_app.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'navigation/router/app_router_parser.dart';
|
||||||
|
import 'navigation/router/app_router_delegate.dart';
|
||||||
|
import '../pages/sort/provider/state.dart';
|
||||||
|
import 'navigation/transition/fade_page_transitions_builder.dart';
|
||||||
|
|
||||||
|
class UnitApp extends StatelessWidget {
|
||||||
|
const UnitApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SortStateScope(
|
||||||
|
notifier: SortState(),
|
||||||
|
child: MaterialApp.router(
|
||||||
|
routerDelegate: router,
|
||||||
|
routeInformationParser: AppRouterParser(),
|
||||||
|
routeInformationProvider: PlatformRouteInformationProvider(
|
||||||
|
initialRouteInformation: RouteInformation(uri: Uri.parse('/app/color')),
|
||||||
|
),
|
||||||
|
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()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
lib/v11/pages/color/color_add_page.dart
Normal file
99
lib/v11/pages/color/color_add_page.dart
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
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<ColorAddPage> createState() => _ColorAddPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ColorAddPageState extends State<ColorAddPage> {
|
||||||
|
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 changeColor(Color value) {
|
||||||
|
_color = value;
|
||||||
|
setState(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
25
lib/v11/pages/color/color_detail_page.dart
Normal file
25
lib/v11/pages/color/color_detail_page.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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(
|
||||||
|
body: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: color,
|
||||||
|
child: Text(text ,style: style,),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/v11/pages/color/color_page.dart
Normal file
65
lib/v11/pages/color/color_page.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
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<ColorPage> createState() => _ColorPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ColorPageState extends State<ColorPage> {
|
||||||
|
final List<Color> _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
|
||||||
|
void initState() {
|
||||||
|
print('======_ColorPageState#initState==============');
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
print('======_ColorPageState#dispose==============');
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.changePath('/app/color/detail',extra: color);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toAddPage() async {
|
||||||
|
Color? color = await router.changePath('/app/color/add',forResult: true,recordHistory: false);
|
||||||
|
if (color != null) {
|
||||||
|
setState(() {
|
||||||
|
_colors.add(color);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
lib/v11/pages/counter/counter_page.dart
Normal file
55
lib/v11/pages/counter/counter_page.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CounterPage extends StatefulWidget {
|
||||||
|
const CounterPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CounterPage> createState() => _CounterPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CounterPageState extends State<CounterPage> {
|
||||||
|
int _counter = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
print('======_CounterPageState#initState==============');
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
print('======_CounterPageState#dispose==============');
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _incrementCounter() {
|
||||||
|
setState(() {
|
||||||
|
_counter++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
lib/v11/pages/empty/empty_page.dart
Normal file
30
lib/v11/pages/empty/empty_page.dart
Normal file
@@ -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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
lib/v11/pages/login/login.dart
Normal file
24
lib/v11/pages/login/login.dart
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../app/navigation/router/app_router_delegate.dart';
|
||||||
|
|
||||||
|
class LoginPage extends StatelessWidget {
|
||||||
|
const LoginPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('Login Page',style: TextStyle(fontSize: 24),),
|
||||||
|
const SizedBox(height: 20,),
|
||||||
|
ElevatedButton(onPressed: (){
|
||||||
|
router.changePath('/app/color');
|
||||||
|
}, child: Text('点击进入'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
lib/v11/pages/settings/settings_page.dart
Normal file
11
lib/v11/pages/settings/settings_page.dart
Normal file
@@ -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')));
|
||||||
|
}
|
||||||
|
}
|
||||||
46
lib/v11/pages/sort/functions.dart
Normal file
46
lib/v11/pages/sort/functions.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import 'functions/bubble.dart';
|
||||||
|
import 'functions/cocktail.dart';
|
||||||
|
import 'functions/comb.dart';
|
||||||
|
import 'functions/cycle.dart';
|
||||||
|
import 'functions/gnome.dart';
|
||||||
|
import 'functions/heap.dart';
|
||||||
|
import 'functions/insertion.dart';
|
||||||
|
import 'functions/merage.dart';
|
||||||
|
import 'functions/pigeonHole.dart';
|
||||||
|
import 'functions/quick.dart';
|
||||||
|
import 'functions/selection.dart';
|
||||||
|
import 'functions/shell.dart';
|
||||||
|
|
||||||
|
typedef SortFunction = Future<void> Function(List<int> src, SortCallback callback);
|
||||||
|
typedef SortCallback = Future<void> Function(List<int> dist);
|
||||||
|
|
||||||
|
Map<String, SortFunction> sortFunctionMap = {
|
||||||
|
'insertion': insertionSort,
|
||||||
|
'bubble': bubbleSort,
|
||||||
|
'cocktail': cocktailSort,
|
||||||
|
'comb': combSort,
|
||||||
|
'pigeonHole': pigeonHoleSort,
|
||||||
|
'shell': shellSort,
|
||||||
|
'selection': selectionSort,
|
||||||
|
'gnome': gnomeSort,
|
||||||
|
'cycle': cycleSort,
|
||||||
|
'heap': heapSort,
|
||||||
|
'quick': quickSort,
|
||||||
|
'mergeSort': mergeSort,
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, String> sortNameMap = {
|
||||||
|
'insertion': '插入排序',
|
||||||
|
'bubble': '冒泡排序',
|
||||||
|
'cocktail': '鸡尾酒排序(双向冒泡排序)',
|
||||||
|
'comb': '梳排序',
|
||||||
|
'pigeonHole': '鸽巢排序',
|
||||||
|
'shell': '希尔排序',
|
||||||
|
'selection': '选择排序',
|
||||||
|
'gnome': '侏儒排序',
|
||||||
|
'cycle': '循环排序',
|
||||||
|
'heap': '堆排序',
|
||||||
|
'quick': '快速排序',
|
||||||
|
'mergeSort': '归并排序',
|
||||||
|
};
|
||||||
|
|
||||||
19
lib/v11/pages/sort/functions/bubble.dart
Normal file
19
lib/v11/pages/sort/functions/bubble.dart
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///冒泡排序
|
||||||
|
Future<void> bubbleSort(List<int> src, SortCallback callback ) async{
|
||||||
|
//控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。
|
||||||
|
for (int i = 0; i < src.length; ++i) {
|
||||||
|
//遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。
|
||||||
|
for (int j = 0; j < src.length - i - 1; ++j) {
|
||||||
|
//如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。
|
||||||
|
if (src[j] > src[j + 1]) {
|
||||||
|
int temp = src[j];
|
||||||
|
src[j] = src[j + 1];
|
||||||
|
src[j + 1] = temp;
|
||||||
|
}
|
||||||
|
//实现一个延迟,以便在ui上展示排序的动画效果
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
lib/v11/pages/sort/functions/cocktail.dart
Normal file
52
lib/v11/pages/sort/functions/cocktail.dart
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///鸡尾酒排序(双向冒泡排序)
|
||||||
|
Future<void> cocktailSort(List<int> src, SortCallback callback ) async {
|
||||||
|
bool swapped = true; // 表示是否进行了交换
|
||||||
|
int start = 0; // 当前未排序部分的起始位置
|
||||||
|
int end = src.length; // 当前未排序部分的结束位置
|
||||||
|
|
||||||
|
// 开始排序循环,只有当没有进行交换时才会退出循环
|
||||||
|
while (swapped == true) {
|
||||||
|
swapped = false;
|
||||||
|
|
||||||
|
// 从左往右遍历需要排序的部分
|
||||||
|
for (int i = start; i < end - 1; ++i) {
|
||||||
|
// 对每两个相邻元素进行比较
|
||||||
|
if (src[i] > src[i + 1]) {
|
||||||
|
// 如果前面的元素大于后面的元素,则交换它们的位置
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[i + 1];
|
||||||
|
src[i + 1] = temp;
|
||||||
|
swapped = true; // 进行了交换
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现动画效果,延迟一段时间后更新数组状态
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有进行交换,则说明已经排好序,退出循环
|
||||||
|
if (swapped == false) break;
|
||||||
|
// 重设为false,准备进行下一轮排序
|
||||||
|
swapped = false;
|
||||||
|
// 将end设置为上一轮排序的最后一个元素的位置
|
||||||
|
end = end - 1;
|
||||||
|
|
||||||
|
// 从右往左遍历需要排序的部分
|
||||||
|
for (int i = end - 1; i >= start; i--) {
|
||||||
|
// 对每两个相邻元素进行比较
|
||||||
|
if (src[i] > src[i + 1]) {
|
||||||
|
// 如果前面的元素大于后面的元素,则交换它们的位置
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[i + 1];
|
||||||
|
src[i + 1] = temp;
|
||||||
|
swapped = true; // 进行了交换
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现动画效果,延迟一段时间后更新数组状态
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
// 将start向右移一位,准备下一轮排序
|
||||||
|
start = start + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
lib/v11/pages/sort/functions/comb.dart
Normal file
34
lib/v11/pages/sort/functions/comb.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///梳排序(Comb Sort)
|
||||||
|
Future<void> combSort(List<int> src, SortCallback callback) async{
|
||||||
|
int gap = src.length;
|
||||||
|
|
||||||
|
bool swapped = true;
|
||||||
|
|
||||||
|
// 当间隔不为1或存在交换时执行循环
|
||||||
|
while (gap != 1 || swapped == true) {
|
||||||
|
// 通过缩小间隔来逐步将元素归位
|
||||||
|
gap = getNextGap(gap);
|
||||||
|
swapped = false;
|
||||||
|
for (int i = 0; i < src.length - gap; i++) {
|
||||||
|
// 如果当前元素大于间隔位置上的元素,则交换它们的位置
|
||||||
|
if (src[i] > src[i + gap]) {
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[i + gap];
|
||||||
|
src[i + gap] = temp;
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实现一个延迟,以便在 UI 上展示排序的动画效果。
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNextGap(int gap) {
|
||||||
|
// 根据当前间隔值计算下一个间隔值
|
||||||
|
gap = (gap * 10) ~/ 13;
|
||||||
|
if (gap < 1) return 1;
|
||||||
|
return gap;
|
||||||
|
}
|
||||||
54
lib/v11/pages/sort/functions/cycle.dart
Normal file
54
lib/v11/pages/sort/functions/cycle.dart
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///循环排序
|
||||||
|
Future<void> cycleSort(List<int> src, SortCallback callback) async {
|
||||||
|
int writes = 0;
|
||||||
|
for (int cycleStart = 0; cycleStart <= src.length - 2; cycleStart++) {
|
||||||
|
int item = src[cycleStart];
|
||||||
|
int pos = cycleStart;
|
||||||
|
|
||||||
|
// 在未排序部分中寻找比当前元素小的元素个数
|
||||||
|
for (int i = cycleStart + 1; i < src.length; i++) {
|
||||||
|
if (src[i] < item) pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前元素已经在正确位置上,则跳过此次迭代
|
||||||
|
if (pos == cycleStart) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将当前元素放置到正确的位置上,并记录写操作次数
|
||||||
|
while (item == src[pos]) {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
if (pos != cycleStart) {
|
||||||
|
int temp = item;
|
||||||
|
item = src[pos];
|
||||||
|
src[pos] = temp;
|
||||||
|
writes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环将位于当前位置的元素放置到正确的位置上
|
||||||
|
while (pos != cycleStart) {
|
||||||
|
pos = cycleStart;
|
||||||
|
// 继续在未排序部分中寻找比当前元素小的元素个数
|
||||||
|
for (int i = cycleStart + 1; i < src.length; i++) {
|
||||||
|
if (src[i] < item) pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将当前元素放置到正确的位置上,并记录写操作次数
|
||||||
|
while (item == src[pos]) {
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
if (item != src[pos]) {
|
||||||
|
int temp = item;
|
||||||
|
item = src[pos];
|
||||||
|
src[pos] = temp;
|
||||||
|
writes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加延迟操作以展示排序过程
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/v11/pages/sort/functions/gnome.dart
Normal file
22
lib/v11/pages/sort/functions/gnome.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///地精排序 (侏儒排序)
|
||||||
|
Future<void> gnomeSort(List<int> src, SortCallback callback) async {
|
||||||
|
int index = 0;
|
||||||
|
while (index < src.length) {
|
||||||
|
// 当 index 小于数组长度时执行循环
|
||||||
|
if (index == 0) index++;
|
||||||
|
if (src[index] >= src[index - 1]) {
|
||||||
|
// 如果当前元素大于等于前面的元素,则将 index 加1
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
// 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置)
|
||||||
|
int temp = src[index];
|
||||||
|
src[index] = src[index - 1];
|
||||||
|
src[index - 1] = temp;
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
38
lib/v11/pages/sort/functions/heap.dart
Normal file
38
lib/v11/pages/sort/functions/heap.dart
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///堆排序
|
||||||
|
Future<void> heapSort(List<int> src, SortCallback callback) async {
|
||||||
|
// 从最后一个非叶子节点开始,构建最大堆
|
||||||
|
for (int i = src.length ~/ 2; i >= 0; i--) {
|
||||||
|
await heapify(src,callback, src.length, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 依次取出最大堆的根节点(最大值),并进行堆化
|
||||||
|
for (int i = src.length - 1; i >= 0; i--) {
|
||||||
|
int temp = src[0];
|
||||||
|
src[0] = src[i];
|
||||||
|
src[i] = temp;
|
||||||
|
await heapify(src, callback,i, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> heapify(List<int> src, SortCallback callback, int n, int i) async{
|
||||||
|
int largest = i;
|
||||||
|
int l = 2 * i + 1; // 左子节点索引
|
||||||
|
int r = 2 * i + 2; // 右子节点索引
|
||||||
|
|
||||||
|
// 如果左子节点存在并且大于父节点,则更新最大值索引
|
||||||
|
if (l < n && src[l] > src[largest]) largest = l;
|
||||||
|
|
||||||
|
// 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引
|
||||||
|
if (r < n && src[r] > src[largest]) largest = r;
|
||||||
|
|
||||||
|
// 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化
|
||||||
|
if (largest != i) {
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[largest];
|
||||||
|
src[largest] = temp;
|
||||||
|
heapify(src,callback, n, largest);
|
||||||
|
}
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
19
lib/v11/pages/sort/functions/insertion.dart
Normal file
19
lib/v11/pages/sort/functions/insertion.dart
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///插入排序
|
||||||
|
Future<void> insertionSort(List<int> src, SortCallback callback) async {
|
||||||
|
for (int i = 1; i < src.length; i++) {
|
||||||
|
int temp = src[i]; // 将当前元素存储到临时变量 temp 中
|
||||||
|
int j = i - 1; // j 表示已排序部分的最后一个元素的索引
|
||||||
|
|
||||||
|
// 在已排序部分从后往前查找,找到合适位置插入当前元素
|
||||||
|
while (j >= 0 && temp < src[j]) {
|
||||||
|
src[j + 1] = src[j]; // 当前元素比已排序部分的元素小,将元素后移一位
|
||||||
|
--j; // 向前遍历
|
||||||
|
// 更新排序结果回调
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
src[j + 1] = temp; // 插入当前元素到已排序部分的正确位置
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
lib/v11/pages/sort/functions/merage.dart
Normal file
79
lib/v11/pages/sort/functions/merage.dart
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
//快速排序
|
||||||
|
Future<void> mergeSort(List<int> src, SortCallback callback) async {
|
||||||
|
await _mergeSort(src,callback,0,src.length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
///归并排序
|
||||||
|
Future<void> _mergeSort(List<int> src, SortCallback callback,int leftIndex, int rightIndex) async {
|
||||||
|
// 定义一个名为 merge 的异步函数,用于合并两个有序子数组
|
||||||
|
Future<void> merge(int leftIndex, int middleIndex, int rightIndex) async {
|
||||||
|
// 计算左侧子数组和右侧子数组的大小
|
||||||
|
int leftSize = middleIndex - leftIndex + 1;
|
||||||
|
int rightSize = rightIndex - middleIndex;
|
||||||
|
|
||||||
|
// 创建左侧子数组和右侧子数组
|
||||||
|
List leftList = List.generate(leftSize, (index) => 0);
|
||||||
|
List rightList = List.generate(rightSize, (index) => 0);
|
||||||
|
|
||||||
|
// 将原始数组中的元素分别复制到左侧子数组和右侧子数组中
|
||||||
|
for (int i = 0; i < leftSize; i++) {
|
||||||
|
leftList[i] = src[leftIndex + i];
|
||||||
|
}
|
||||||
|
for (int j = 0; j < rightSize; j++) {
|
||||||
|
rightList[j] = src[middleIndex + j + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化游标和索引
|
||||||
|
int i = 0, j = 0;
|
||||||
|
int k = leftIndex;
|
||||||
|
|
||||||
|
// 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中
|
||||||
|
while (i < leftSize && j < rightSize) {
|
||||||
|
if (leftList[i] <= rightList[j]) {
|
||||||
|
src[k] = leftList[i];
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
src[k] = rightList[j];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await callback(src);
|
||||||
|
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将左侧子数组或右侧子数组中剩余的元素放入原始数组中
|
||||||
|
while (i < leftSize) {
|
||||||
|
src[k] = leftList[i];
|
||||||
|
i++;
|
||||||
|
k++;
|
||||||
|
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (j < rightSize) {
|
||||||
|
src[k] = rightList[j];
|
||||||
|
j++;
|
||||||
|
k++;
|
||||||
|
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果左索引小于右索引,则递归地对数组进行归并排序
|
||||||
|
if (leftIndex < rightIndex) {
|
||||||
|
// 计算中间索引位置
|
||||||
|
int middleIndex = (rightIndex + leftIndex) ~/ 2;
|
||||||
|
|
||||||
|
// 分别对左侧子数组和右侧子数组进行归并排序
|
||||||
|
await _mergeSort(src,callback,leftIndex, middleIndex);
|
||||||
|
await _mergeSort(src,callback,middleIndex + 1, rightIndex);
|
||||||
|
|
||||||
|
await callback(src);
|
||||||
|
|
||||||
|
// 合并两个有序子数组
|
||||||
|
await merge(leftIndex, middleIndex, rightIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
lib/v11/pages/sort/functions/oddEven.dart
Normal file
37
lib/v11/pages/sort/functions/oddEven.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///奇偶排序(Odd-Even Sort)
|
||||||
|
Future<void> oddEvenSort(List<int> src, SortCallback callback) async {
|
||||||
|
bool isSorted = false;
|
||||||
|
|
||||||
|
while (!isSorted) {
|
||||||
|
// 当 isSorted 为 false 时执行循环
|
||||||
|
isSorted = true; // 先假设数组已经排好序
|
||||||
|
|
||||||
|
for (int i = 1; i <= src.length - 2; i = i + 2) {
|
||||||
|
// 对奇数索引位置进行比较
|
||||||
|
if (src[i] > src[i + 1]) {
|
||||||
|
// 如果当前元素大于后面的元素,则交换它们的值
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[i + 1];
|
||||||
|
src[i + 1] = temp;
|
||||||
|
isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= src.length - 2; i = i + 2) {
|
||||||
|
// 对偶数索引位置进行比较
|
||||||
|
if (src[i] > src[i + 1]) {
|
||||||
|
// 如果当前元素大于后面的元素,则交换它们的值
|
||||||
|
int temp = src[i];
|
||||||
|
src[i] = src[i + 1];
|
||||||
|
src[i + 1] = temp;
|
||||||
|
isSorted = false;
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
33
lib/v11/pages/sort/functions/pigeonHole.dart
Normal file
33
lib/v11/pages/sort/functions/pigeonHole.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///鸽巢排序
|
||||||
|
Future<void> pigeonHoleSort(List<int> src, SortCallback callback ) async{
|
||||||
|
int min = src[0];
|
||||||
|
int max = src[0];
|
||||||
|
int range, i, j, index;
|
||||||
|
|
||||||
|
// 找到数组中的最大值和最小值
|
||||||
|
for (int a = 0; a < src.length; a++) {
|
||||||
|
if (src[a] > max) max = src[a];
|
||||||
|
if (src[a] < min) min = src[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算鸽巢的个数
|
||||||
|
range = max - min + 1;
|
||||||
|
List<int> p = List.generate(range, (i) => 0);
|
||||||
|
|
||||||
|
// 将数字分配到各个鸽巢中
|
||||||
|
for (i = 0; i < src.length; i++) {
|
||||||
|
p[src[i] - min]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
// 将鸽巢中的数字取出,重新放回到数组中
|
||||||
|
for (j = 0; j < range; j++) {
|
||||||
|
while (p[j]-- > 0) {
|
||||||
|
src[index++] = j + min;
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/v11/pages/sort/functions/quick.dart
Normal file
65
lib/v11/pages/sort/functions/quick.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
//快速排序
|
||||||
|
Future<void> quickSort(List<int> src, SortCallback callback) async {
|
||||||
|
await _quickSort(src,callback,0,src.length-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
///快速排序
|
||||||
|
Future<void> _quickSort(List<int> src, SortCallback callback,int leftIndex,int rightIndex) async {
|
||||||
|
// 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置
|
||||||
|
Future<int> _partition(int left, int right) async {
|
||||||
|
// 选择中间位置的元素作为基准元素
|
||||||
|
int p = (left + (right - left) / 2).toInt();
|
||||||
|
|
||||||
|
// 交换基准元素和最右边的元素
|
||||||
|
var temp = src[p];
|
||||||
|
src[p] = src[right];
|
||||||
|
src[right] = temp;
|
||||||
|
await callback(src);
|
||||||
|
|
||||||
|
// 初始化游标 cursor
|
||||||
|
int cursor = left;
|
||||||
|
|
||||||
|
// 遍历数组并根据基准元素将元素交换到左侧或右侧
|
||||||
|
for (int i = left; i < right; i++) {
|
||||||
|
if (cf(src[i], src[right]) <= 0) {
|
||||||
|
// 如果当前元素小于等于基准元素,则交换它和游标位置的元素
|
||||||
|
var temp = src[i];
|
||||||
|
src[i] = src[cursor];
|
||||||
|
src[cursor] = temp;
|
||||||
|
cursor++;
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将基准元素放置在游标位置
|
||||||
|
temp = src[right];
|
||||||
|
src[right] = src[cursor];
|
||||||
|
src[cursor] = temp;
|
||||||
|
|
||||||
|
await callback(src);
|
||||||
|
|
||||||
|
return cursor; // 返回基准元素的索引位置
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果左索引小于右索引,则递归地对数组进行快速排序
|
||||||
|
if (leftIndex < rightIndex) {
|
||||||
|
int p = await _partition(leftIndex, rightIndex);
|
||||||
|
|
||||||
|
await _quickSort(src,callback,leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序
|
||||||
|
|
||||||
|
await _quickSort(src,callback, p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较函数,用于判断两个元素的大小关系
|
||||||
|
cf(int a, int b) {
|
||||||
|
if (a < b) {
|
||||||
|
return -1; // 若 a 小于 b,则返回 -1
|
||||||
|
} else if (a > b) {
|
||||||
|
return 1; // 若 a 大于 b,则返回 1
|
||||||
|
} else {
|
||||||
|
return 0; // 若 a 等于 b,则返回 0
|
||||||
|
}
|
||||||
|
}
|
||||||
18
lib/v11/pages/sort/functions/selection.dart
Normal file
18
lib/v11/pages/sort/functions/selection.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///选择排序
|
||||||
|
Future<void> selectionSort(List<int> src, SortCallback callback ) async {
|
||||||
|
for (int i = 0; i < src.length; i++) {
|
||||||
|
for (int j = i + 1; j < src.length; j++) {
|
||||||
|
// 遍历未排序部分,内层循环控制变量 j
|
||||||
|
if (src[i] > src[j]) {
|
||||||
|
// 判断当前元素是否比后续元素小
|
||||||
|
int temp = src[j];
|
||||||
|
// 交换当前元素和后续较小的元素
|
||||||
|
src[j] = src[i];
|
||||||
|
src[i] = temp;
|
||||||
|
}
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/v11/pages/sort/functions/shell.dart
Normal file
21
lib/v11/pages/sort/functions/shell.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import '../functions.dart';
|
||||||
|
|
||||||
|
///希尔排序
|
||||||
|
Future<void> shellSort(List<int> src, SortCallback callback) async{
|
||||||
|
//定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。
|
||||||
|
for (int gap = src.length ~/ 2; gap > 0; gap ~/= 2) {
|
||||||
|
//遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。
|
||||||
|
for (int i = gap; i < src.length; i += 1) {
|
||||||
|
//将当前遍历到的元素赋值给它
|
||||||
|
int temp = src[i];
|
||||||
|
//内部使用一个 for 循环来实现插入排序。
|
||||||
|
//循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。
|
||||||
|
int j;
|
||||||
|
for (j = i; j >= gap && src[j - gap] > temp; j -= gap) {
|
||||||
|
src[j] = src[j - gap];
|
||||||
|
}
|
||||||
|
src[j] = temp;
|
||||||
|
await callback(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
lib/v11/pages/sort/provider/sort_config.dart
Normal file
35
lib/v11/pages/sort/provider/sort_config.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SortConfig {
|
||||||
|
final int count;
|
||||||
|
final int seed;
|
||||||
|
final Duration duration;
|
||||||
|
final String name;
|
||||||
|
final int colorIndex;
|
||||||
|
|
||||||
|
SortConfig({
|
||||||
|
this.count = 100,
|
||||||
|
this.duration = const Duration(microseconds: 1500),
|
||||||
|
this.seed = -1,
|
||||||
|
this.colorIndex = 0,
|
||||||
|
this.name = 'insertion',
|
||||||
|
});
|
||||||
|
|
||||||
|
SortConfig copyWith({
|
||||||
|
int? count,
|
||||||
|
int? seed,
|
||||||
|
int? colorIndex,
|
||||||
|
Duration? duration,
|
||||||
|
String? name,
|
||||||
|
}) =>
|
||||||
|
SortConfig(
|
||||||
|
count:count??this.count,
|
||||||
|
seed:seed??this.seed,
|
||||||
|
duration:duration??this.duration,
|
||||||
|
name:name??this.name,
|
||||||
|
colorIndex:colorIndex??this.colorIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
101
lib/v11/pages/sort/provider/state.dart
Normal file
101
lib/v11/pages/sort/provider/state.dart
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../functions.dart';
|
||||||
|
import 'sort_config.dart';
|
||||||
|
|
||||||
|
enum SortStatus{
|
||||||
|
none, // 未操作
|
||||||
|
sorting, // 排序中
|
||||||
|
sorted, // 排序完成
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MaterialColor> kColorSupport = [
|
||||||
|
Colors.blue,
|
||||||
|
Colors.lightBlue,
|
||||||
|
Colors.cyan,
|
||||||
|
Colors.red,
|
||||||
|
Colors.pink,
|
||||||
|
Colors.orange,
|
||||||
|
Colors.yellow,
|
||||||
|
Colors.green,
|
||||||
|
Colors.indigo,
|
||||||
|
Colors.purple,
|
||||||
|
Colors.deepPurple,
|
||||||
|
];
|
||||||
|
|
||||||
|
class SortState with ChangeNotifier{
|
||||||
|
|
||||||
|
SortState(){
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
SortStatus status = SortStatus.none;
|
||||||
|
|
||||||
|
List<int> data = [];
|
||||||
|
List<int> stepData = [];
|
||||||
|
|
||||||
|
SortConfig _config = SortConfig();
|
||||||
|
SortConfig get config => _config;
|
||||||
|
Random random = Random();
|
||||||
|
|
||||||
|
set config(SortConfig config){
|
||||||
|
_config = config;
|
||||||
|
reset();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectName(String name){
|
||||||
|
if(name==config.name) return;
|
||||||
|
config = config.copyWith(name: name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectColor(int colorIndex){
|
||||||
|
if(colorIndex==config.colorIndex) return;
|
||||||
|
config = config.copyWith(colorIndex: colorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset(){
|
||||||
|
data.clear();
|
||||||
|
status = SortStatus.none;
|
||||||
|
notifyListeners();
|
||||||
|
int count = config.count;
|
||||||
|
if(config.seed!=-1){
|
||||||
|
random = Random(config.seed);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
//随机往数组中填值
|
||||||
|
data.add(random.nextInt(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort() async{
|
||||||
|
status = SortStatus.sorting;
|
||||||
|
notifyListeners();
|
||||||
|
Stopwatch stopwatch = Stopwatch()..start();
|
||||||
|
SortFunction? sortFunction = sortFunctionMap[config.name];
|
||||||
|
if(sortFunction!=null){
|
||||||
|
await sortFunction(data,(arr) async {
|
||||||
|
await Future.delayed(config.duration);
|
||||||
|
notifyListeners();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
status = SortStatus.sorted;
|
||||||
|
notifyListeners();
|
||||||
|
stopwatch.stop();
|
||||||
|
print("Sorting completed in ${stopwatch.elapsed.inMilliseconds} ms.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides the current [SortState] to descendant widgets in the tree.
|
||||||
|
class SortStateScope extends InheritedNotifier<SortState> {
|
||||||
|
const SortStateScope({
|
||||||
|
required super.notifier,
|
||||||
|
required super.child,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
static SortState of(BuildContext context) =>
|
||||||
|
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user