24
This commit is contained in:
@@ -2,14 +2,16 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
import 'v11/app.dart';
|
import 'v12/app.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
setSize();
|
setSize();
|
||||||
runApp(const UnitApp());
|
GoRouter.optionURLReflectsImperativeAPIs = true;
|
||||||
|
runApp(UnitApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
25
lib/v11/app/navigation/helper/function.dart
Normal file
25
lib/v11/app/navigation/helper/function.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
Map<String, String> kRouteLabelMap = {
|
||||||
|
'': '',
|
||||||
|
'/color': '颜色板',
|
||||||
|
'/color/add': '添加颜色',
|
||||||
|
'/color/detail': '颜色详情',
|
||||||
|
'/counter': '计数器',
|
||||||
|
'/sort': '排序算法',
|
||||||
|
'/sort/player': '',
|
||||||
|
'/sort/settings': '排序配置',
|
||||||
|
'/user': '我的',
|
||||||
|
'/settings': '系统设置',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
String calcRouteName(BuildContext context,String path){
|
||||||
|
String? result = kRouteLabelMap[path];
|
||||||
|
if(result !=null) return result;
|
||||||
|
if(path.startsWith('/sort/player/')){
|
||||||
|
return path.split('/sort/player/')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '未知路由';
|
||||||
|
}
|
||||||
108
lib/v11/app/navigation/helper/router_history.dart
Normal file
108
lib/v11/app/navigation/helper/router_history.dart
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class RouterHistoryScope extends InheritedNotifier<RouterHistory> {
|
||||||
|
const RouterHistoryScope({super.key, required super.child, super.notifier});
|
||||||
|
|
||||||
|
static RouterHistory of(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.dependOnInheritedWidgetOfExactType<RouterHistoryScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RouterHistory read(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.getInheritedWidgetOfExactType<RouterHistoryScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterHistory with ChangeNotifier {
|
||||||
|
final List<String> exclusives;
|
||||||
|
|
||||||
|
final GoRouterDelegate delegate;
|
||||||
|
|
||||||
|
final List<RouteMatchList> _histories = [];
|
||||||
|
final List<RouteMatchList> _backHistories = [];
|
||||||
|
|
||||||
|
List<RouteMatchList> get histories => _histories;
|
||||||
|
|
||||||
|
RouterHistory(this.delegate, {this.exclusives = const []}) {
|
||||||
|
delegate.addListener(_onRouteChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 用于记录当前历史记录
|
||||||
|
/// 清空历史之后,切换时,先记录 _current
|
||||||
|
RouteMatchList? _current;
|
||||||
|
|
||||||
|
void _onRouteChange() {
|
||||||
|
/// 当没有历史,且 _current 非空
|
||||||
|
if (_histories.isEmpty && _current != null) {
|
||||||
|
_histories.add(_current!);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteMatchList matchList = delegate.currentConfiguration;
|
||||||
|
if (_histories.isNotEmpty && matchList == _histories.last
|
||||||
|
|| matchList.isEmpty
|
||||||
|
) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String uri = matchList.last.matchedLocation;
|
||||||
|
if (exclusives.contains(uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_recode(matchList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 [history] 加入历史记录
|
||||||
|
void _recode(RouteMatchList history) {
|
||||||
|
_current = history;
|
||||||
|
_histories.add(history);
|
||||||
|
if (hasHistory) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasHistory => _histories.length > 1;
|
||||||
|
|
||||||
|
bool get hasBackHistory => _backHistories.isNotEmpty;
|
||||||
|
|
||||||
|
/// 历史回退操作
|
||||||
|
/// 将当前顶层移除,并加入 [_backHistories] 撤销列表
|
||||||
|
/// 并转到前一路径 [_histories.last]
|
||||||
|
void back() {
|
||||||
|
if (!hasHistory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RouteMatchList top = _histories.removeLast();
|
||||||
|
_backHistories.add(top);
|
||||||
|
if (_histories.isNotEmpty) {
|
||||||
|
delegate.setNewRoutePath(_histories.last);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 撤销回退操作
|
||||||
|
/// 取出回退列表的最后元素,跳转到该路径
|
||||||
|
void revocation() {
|
||||||
|
RouteMatchList target = _backHistories.removeLast();
|
||||||
|
delegate.setNewRoutePath(target);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(RouteMatchList history) {
|
||||||
|
_histories.remove(history);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_histories.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(RouteMatchList history) {
|
||||||
|
_histories.remove(history);
|
||||||
|
delegate.setNewRoutePath(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lib/v11/app/navigation/router/app.dart
Normal file
44
lib/v11/app/navigation/router/app.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/counter/counter_page.dart';
|
||||||
|
import '../../../pages/user/user_page.dart';
|
||||||
|
import '../../../pages/settings/settings_page.dart';
|
||||||
|
import '../../../pages/empty/empty_panel.dart';
|
||||||
|
import '../views/app_navigation.dart';
|
||||||
|
import 'color.dart';
|
||||||
|
import 'sort.dart';
|
||||||
|
|
||||||
|
|
||||||
|
final RouteBase appRoute = ShellRoute(
|
||||||
|
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||||
|
return AppNavigation(navigator: child);
|
||||||
|
},
|
||||||
|
routes: <RouteBase>[
|
||||||
|
colorRouters,
|
||||||
|
GoRoute(
|
||||||
|
path: 'counter',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const CounterPage();
|
||||||
|
}),
|
||||||
|
sortRouters,
|
||||||
|
GoRoute(
|
||||||
|
path: 'user',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const UserPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'settings',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const SettingPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '404',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
String msg = '无法访问: ${state.extra}';
|
||||||
|
return EmptyPanel(msg: msg);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
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.toString();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
35
lib/v11/app/navigation/router/color.dart
Normal file
35
lib/v11/app/navigation/router/color.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/color/color_add_page.dart';
|
||||||
|
import '../../../pages/color/color_detail_page.dart';
|
||||||
|
import '../../../pages/color/color_page.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final RouteBase colorRouters = GoRoute(
|
||||||
|
path: 'color',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const ColorPage();
|
||||||
|
},
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: 'detail',
|
||||||
|
name: 'colorDetail',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
String? selectedColor = state.uri.queryParameters['color'];
|
||||||
|
Color color = Colors.black;
|
||||||
|
if (selectedColor != null) {
|
||||||
|
color = Color(int.parse(selectedColor, radix: 16));
|
||||||
|
}
|
||||||
|
return ColorDetailPage(color: color);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'add',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const ColorAddPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:iroute/v9/app/navigation/router/views/navigator_scope.dart';
|
|
||||||
|
|
||||||
import 'iroute_config.dart';
|
|
||||||
import 'utils/path_utils.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) => _match(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _match(String path ,String target){
|
|
||||||
if(!path.contains(':')){
|
|
||||||
return path == target;
|
|
||||||
}
|
|
||||||
return patternToRegExp(path,[]).hasMatch(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 优先调用 [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);
|
|
||||||
}
|
|
||||||
return MaterialPage(
|
|
||||||
child: child,
|
|
||||||
key: config.pageKey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
enum RouteStyle{
|
|
||||||
push,
|
|
||||||
replace,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class IRouteConfig {
|
|
||||||
final Object? extra;
|
|
||||||
final bool forResult;
|
|
||||||
final Uri uri;
|
|
||||||
final Map<String,String>? pathParams;
|
|
||||||
final bool keepAlive;
|
|
||||||
final RouteStyle routeStyle;
|
|
||||||
final bool recordHistory;
|
|
||||||
|
|
||||||
const IRouteConfig({
|
|
||||||
this.extra,
|
|
||||||
required this.uri,
|
|
||||||
this.forResult = false,
|
|
||||||
this.pathParams,
|
|
||||||
this.keepAlive = false,
|
|
||||||
this.routeStyle = RouteStyle.replace,
|
|
||||||
this.recordHistory = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
String get path => uri.toString();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
27
lib/v11/app/navigation/router/root.dart
Normal file
27
lib/v11/app/navigation/router/root.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/login/login.dart';
|
||||||
|
import 'app.dart';
|
||||||
|
|
||||||
|
final RouteBase rootRoute = GoRoute(
|
||||||
|
path: '/',
|
||||||
|
redirect: _redirect,
|
||||||
|
routes: [
|
||||||
|
appRoute,
|
||||||
|
GoRoute(
|
||||||
|
path: 'login',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const LoginPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
FutureOr<String?> _redirect(BuildContext context, GoRouterState state) {
|
||||||
|
if(state.fullPath=='/'){
|
||||||
|
return '/color';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
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/:name',
|
|
||||||
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/:name': '',
|
|
||||||
'/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'),
|
|
||||||
];
|
|
||||||
44
lib/v11/app/navigation/router/sort.dart
Normal file
44
lib/v11/app/navigation/router/sort.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/sort/provider/state.dart';
|
||||||
|
|
||||||
|
import '../../../pages/sort/views/player/sort_player.dart';
|
||||||
|
import '../../../pages/sort/views/settings/sort_setting.dart';
|
||||||
|
import '../../../pages/sort/views/sort_page/sort_page.dart';
|
||||||
|
|
||||||
|
final RouteBase sortRouters = ShellRoute(
|
||||||
|
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||||
|
return SortNavigation(navigator: child);
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'sort',
|
||||||
|
redirect: _redirectSort,
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'player/:name',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
print(state.pathParameters);// 获取路径参数
|
||||||
|
return const SortPlayer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'settings',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const SortSettings();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
FutureOr<String?> _redirectSort(BuildContext context, GoRouterState state) {
|
||||||
|
if (state.fullPath == '/sort') {
|
||||||
|
String name = SortStateScope.read(context).config.name;
|
||||||
|
return '/sort/player/$name';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
final RegExp _parameterRegExp = RegExp(r':(\w+)(\((?:\\.|[^\\()])+\))?');
|
|
||||||
|
|
||||||
RegExp patternToRegExp(String pattern, List<String> parameters) {
|
|
||||||
final StringBuffer buffer = StringBuffer('^');
|
|
||||||
int start = 0;
|
|
||||||
for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
|
|
||||||
if (match.start > start) {
|
|
||||||
buffer.write(RegExp.escape(pattern.substring(start, match.start)));
|
|
||||||
}
|
|
||||||
final String name = match[1]!;
|
|
||||||
final String? optionalPattern = match[2];
|
|
||||||
final String regex = optionalPattern != null
|
|
||||||
? _escapeGroup(optionalPattern, name)
|
|
||||||
: '(?<$name>[^/]+)';
|
|
||||||
buffer.write(regex);
|
|
||||||
parameters.add(name);
|
|
||||||
start = match.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start < pattern.length) {
|
|
||||||
buffer.write(RegExp.escape(pattern.substring(start)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pattern.endsWith('/')) {
|
|
||||||
buffer.write(r'(?=/|$)');
|
|
||||||
}
|
|
||||||
return RegExp(buffer.toString(), caseSensitive: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _escapeGroup(String group, [String? name]) {
|
|
||||||
final String escapedGroup = group.replaceFirstMapped(
|
|
||||||
RegExp(r'[:=!]'), (Match match) => '\\${match[0]}');
|
|
||||||
if (name != null) {
|
|
||||||
return '(?<$name>$escapedGroup)';
|
|
||||||
}
|
|
||||||
return escapedGroup;
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../router/app_router_delegate.dart';
|
|
||||||
import 'app_navigation_rail.dart';
|
import 'app_navigation_rail.dart';
|
||||||
import 'app_top_bar/app_top_bar.dart';
|
import 'app_top_bar/app_top_bar.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:iroute/components/components.dart';
|
import 'package:iroute/components/components.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import '../../../pages/sort/provider/state.dart';
|
|
||||||
import '../router/app_router_delegate.dart';
|
|
||||||
import '../router/iroute_config.dart';
|
|
||||||
import '../router/routes.dart';
|
|
||||||
|
|
||||||
class AppNavigationRail extends StatefulWidget {
|
class AppNavigationRail extends StatefulWidget {
|
||||||
const AppNavigationRail({super.key});
|
const AppNavigationRail({super.key});
|
||||||
@@ -14,18 +10,14 @@ class AppNavigationRail extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AppNavigationRailState extends State<AppNavigationRail> {
|
class _AppNavigationRailState extends State<AppNavigationRail> {
|
||||||
|
final List<MenuMeta> deskNavBarMenus = const [
|
||||||
|
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined, path: '/color'),
|
||||||
|
MenuMeta(label: '计数器', icon: Icons.add_chart, path: '/counter'),
|
||||||
|
MenuMeta(label: '排序', icon: Icons.sort, path: '/sort'),
|
||||||
|
MenuMeta(label: '我的', icon: Icons.person, path: '/user'),
|
||||||
|
MenuMeta(label: '设置', icon: Icons.settings, path: '/settings'),
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
router.addListener(_onRouterChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
router.removeListener(_onRouterChange);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -39,7 +31,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.11',
|
'V0.1.1',
|
||||||
style: TextStyle(color: Colors.white, fontSize: 12),
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -50,10 +42,10 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExp _segReg = RegExp(r'/app/\w+');
|
final RegExp _segReg = RegExp(r'/\w+');
|
||||||
|
|
||||||
int? get activeIndex {
|
int? get activeIndex {
|
||||||
String path = router.path;
|
final String path = GoRouterState.of(context).uri.toString();
|
||||||
RegExpMatch? match = _segReg.firstMatch(path);
|
RegExpMatch? match = _segReg.firstMatch(path);
|
||||||
if (match == null) return null;
|
if (match == null) return null;
|
||||||
String? target = match.group(0);
|
String? target = match.group(0);
|
||||||
@@ -64,25 +56,10 @@ 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==4){
|
||||||
router.changePath(path, keepAlive: true,recordHistory: true);
|
GoRouter.of(context).push(path);
|
||||||
return;
|
}else{
|
||||||
|
GoRouter.of(context).go(path);
|
||||||
}
|
}
|
||||||
if(index ==2){
|
|
||||||
SortState state = SortStateScope.read(context);
|
|
||||||
String name = state.config.name;
|
|
||||||
router.changePath('/app/sort/$name',recordHistory: true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (index == 4) {
|
|
||||||
router.changePath(path, style: RouteStyle.push,recordHistory: true);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
router.changePath(path,recordHistory: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onRouterChange() {
|
|
||||||
setState(() {});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
|
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
|
||||||
import '../../router/app_router_delegate.dart';
|
// import '../../router/app_router_delegate.dart';
|
||||||
|
|
||||||
class AppRouterEditor extends StatefulWidget {
|
class AppRouterEditor extends StatefulWidget {
|
||||||
final ValueChanged<String>? onSubmit;
|
final ValueChanged<String>? onSubmit;
|
||||||
@@ -14,23 +15,34 @@ class AppRouterEditor extends StatefulWidget {
|
|||||||
class _AppRouterEditorState extends State<AppRouterEditor> {
|
class _AppRouterEditorState extends State<AppRouterEditor> {
|
||||||
|
|
||||||
final TextEditingController _controller = TextEditingController();
|
final TextEditingController _controller = TextEditingController();
|
||||||
|
late GoRouterDelegate _delegate ;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
|
||||||
_onRouteChange();
|
_onRouteChange();
|
||||||
router.addListener(_onRouteChange);
|
_delegate.addListener(_onRouteChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onRouteChange() {
|
void _onRouteChange() {
|
||||||
_controller.text=router.path;
|
|
||||||
|
List<RouteMatch> matches = _delegate.currentConfiguration.matches;
|
||||||
|
if(matches.isEmpty) return;
|
||||||
|
RouteMatch match = matches.last;
|
||||||
|
if(match is ImperativeRouteMatch){
|
||||||
|
_controller.text = match.matches.uri.toString();
|
||||||
|
}else{
|
||||||
|
_controller.text = match.matchedLocation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
router.removeListener(_onRouteChange);
|
_delegate.removeListener(_onRouteChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:iroute/components/components.dart';
|
import 'package:iroute/components/components.dart';
|
||||||
import '../../../../pages/sort/provider/state.dart';
|
import '../../helper/function.dart';
|
||||||
import '../../router/app_router_delegate.dart';
|
import '../route_back_indicator.dart';
|
||||||
import '../../router/routes.dart';
|
|
||||||
import '../../router/utils/path_utils.dart';
|
|
||||||
import '../../router/views/route_back_indicator.dart';
|
|
||||||
import 'app_router_editor.dart';
|
import 'app_router_editor.dart';
|
||||||
import 'history_view_icon.dart';
|
|
||||||
import 'route_history_button.dart';
|
import 'route_history_button.dart';
|
||||||
|
import 'history_view_icon.dart';
|
||||||
|
|
||||||
class AppTopBar extends StatelessWidget {
|
class AppTopBar extends StatelessWidget {
|
||||||
const AppTopBar({super.key});
|
const AppTopBar({super.key});
|
||||||
@@ -16,31 +14,38 @@ class AppTopBar extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DragToMoveWrap(
|
return DragToMoveWrap(
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.center,
|
// alignment: Alignment.center,
|
||||||
height: 46,
|
height: 46,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
const RouteBackIndicator(),
|
const RouteBackIndicator(),
|
||||||
const RouterIndicator(),
|
const RouterIndicator(),
|
||||||
|
// Spacer(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(children: [
|
child: Row(textDirection: TextDirection.rtl, 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(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 12.0),
|
padding: EdgeInsets.symmetric(vertical: 12.0),
|
||||||
child: VerticalDivider(
|
child: VerticalDivider(
|
||||||
width: 32,
|
width: 32,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
HistoryViewIcon(),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: AppRouterEditor(
|
||||||
|
onSubmit: (path) {
|
||||||
|
GoRouter.of(context).go(path);
|
||||||
|
// => router.changePath(path)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
RouteHistoryButton(),
|
||||||
])),
|
])),
|
||||||
const WindowButtons()
|
const WindowButtons()
|
||||||
],
|
],
|
||||||
@@ -57,27 +62,35 @@ class RouterIndicator extends StatefulWidget {
|
|||||||
State<RouterIndicator> createState() => _RouterIndicatorState();
|
State<RouterIndicator> createState() => _RouterIndicatorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _RouterIndicatorState extends State<RouterIndicator> {
|
class _RouterIndicatorState extends State<RouterIndicator> {
|
||||||
|
late GoRouterDelegate _delegate;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
router.addListener(_onRouterChange);
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
_delegate.addListener(_onRouterChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
router.removeListener(_onRouterChange);
|
_delegate.removeListener(_onRouterChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
List<RouteMatch> matches = _delegate.currentConfiguration.matches;
|
||||||
|
if(matches.isEmpty) return const SizedBox();
|
||||||
|
RouteMatch match = _delegate.currentConfiguration.matches.last;
|
||||||
|
|
||||||
|
print(
|
||||||
|
"=========_RouterIndicatorState:build==${match.matchedLocation}========");
|
||||||
|
|
||||||
return TolyBreadcrumb(
|
return TolyBreadcrumb(
|
||||||
items: pathToBreadcrumbItems(router.path),
|
items: pathToBreadcrumbItems(context, match.matchedLocation),
|
||||||
onTapItem: (item) {
|
onTapItem: (item) {
|
||||||
if (item.to != null) {
|
if (item.to != null) {
|
||||||
router.changePath(item.to!);
|
GoRouter.of(context).go(item.to!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -85,16 +98,10 @@ class _RouterIndicatorState extends State<RouterIndicator> {
|
|||||||
|
|
||||||
void _onRouterChange() {
|
void _onRouterChange() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
if(router.path.startsWith('/app/sort/')){
|
|
||||||
SortState state = SortStateScope.of(context);
|
|
||||||
String name = router.path.replaceAll('/app/sort/', '');
|
|
||||||
if(name!='settings'){
|
|
||||||
state.selectName(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BreadcrumbItem> pathToBreadcrumbItems(String path) {
|
List<BreadcrumbItem> pathToBreadcrumbItems(
|
||||||
|
BuildContext context, String path) {
|
||||||
Uri uri = Uri.parse(path);
|
Uri uri = Uri.parse(path);
|
||||||
List<BreadcrumbItem> result = [];
|
List<BreadcrumbItem> result = [];
|
||||||
String to = '';
|
String to = '';
|
||||||
@@ -106,16 +113,25 @@ class _RouterIndicatorState extends State<RouterIndicator> {
|
|||||||
|
|
||||||
for (String segment in uri.pathSegments) {
|
for (String segment in uri.pathSegments) {
|
||||||
to += '/$segment';
|
to += '/$segment';
|
||||||
String label = '';
|
String label = calcRouteName(context, to);
|
||||||
if(to.startsWith('/app/sort/')){
|
if (label.isNotEmpty) {
|
||||||
label = to.replaceAll('/app/sort/', '');
|
result
|
||||||
}else{
|
.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
||||||
label = kRouteLabelMap[to] ?? '未知路由';
|
|
||||||
}
|
|
||||||
if(label.isNotEmpty){
|
|
||||||
result.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> kRouteLabelMap = {
|
||||||
|
'': '',
|
||||||
|
'/color': '颜色板',
|
||||||
|
'/color/add': '添加颜色',
|
||||||
|
'/color/detail': '颜色详情',
|
||||||
|
'/counter': '计数器',
|
||||||
|
'/sort': '排序算法',
|
||||||
|
'/sort/player': '演示',
|
||||||
|
'/sort/settings': '排序配置',
|
||||||
|
'/user': '我的',
|
||||||
|
'/settings': '系统设置',
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:iroute/components/components.dart';
|
import 'package:iroute/components/components.dart';
|
||||||
import '../../router/app_router_delegate.dart';
|
import 'package:iroute/v11/app/navigation/helper/function.dart';
|
||||||
import '../../router/routes.dart';
|
import '../../helper/router_history.dart';
|
||||||
import '../../router/iroute_config.dart';
|
|
||||||
import 'app_top_bar.dart';
|
import 'app_top_bar.dart';
|
||||||
|
|
||||||
class HistoryViewIcon extends StatelessWidget{
|
class HistoryViewIcon extends StatelessWidget{
|
||||||
@@ -10,6 +10,7 @@ class HistoryViewIcon extends StatelessWidget{
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
RouterHistory routerHistory = RouterHistoryScope.read(context);
|
||||||
|
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
@@ -19,7 +20,7 @@ class HistoryViewIcon extends StatelessWidget{
|
|||||||
height: 350,
|
height: 350,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
_buildTopBar(),
|
_buildTopBar(routerHistory),
|
||||||
const Expanded(
|
const Expanded(
|
||||||
child:HistoryPanel(),
|
child:HistoryPanel(),
|
||||||
),
|
),
|
||||||
@@ -34,7 +35,7 @@ class HistoryViewIcon extends StatelessWidget{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTopBar() {
|
Widget _buildTopBar( RouterHistory routerHistory) {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xffFAFAFC),
|
color: const Color(0xffFAFAFC),
|
||||||
@@ -49,14 +50,14 @@ class HistoryViewIcon extends StatelessWidget{
|
|||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
TextButton(onPressed: router.clearHistory, child: const Text('清空历史'))
|
TextButton(onPressed: routerHistory.clear, child: const Text('清空历史'))
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HistoryItem extends StatefulWidget {
|
class HistoryItem extends StatefulWidget {
|
||||||
final IRouteConfig history;
|
final RouteMatchList history;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
final VoidCallback onDelete;
|
final VoidCallback onDelete;
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ class HistoryItem extends StatefulWidget {
|
|||||||
class _HistoryItemState extends State<HistoryItem> {
|
class _HistoryItemState extends State<HistoryItem> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
String path = widget.history.matches.last.matchedLocation;
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@@ -80,11 +82,11 @@ class _HistoryItemState extends State<HistoryItem> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(widget.history.path),
|
Text(path),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
Text(kRouteLabelMap[widget.history.path]??'未知路由'),
|
Text(calcRouteName(context, path)),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@@ -110,22 +112,18 @@ class HistoryPanel extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HistoryPanelState extends State<HistoryPanel> {
|
class _HistoryPanelState extends State<HistoryPanel> {
|
||||||
|
late RouterHistory _history ;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void didChangeDependencies() {
|
||||||
super.initState();
|
super.didChangeDependencies();
|
||||||
router.addListener(_onChange);
|
_history = RouterHistoryScope.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
router.removeListener(_onChange);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
List<IRouteConfig> histories = router.historyManager.histories;
|
List<RouteMatchList> histories = _history.histories.reversed.toList();
|
||||||
if(histories.isEmpty){
|
if(histories.isEmpty){
|
||||||
return const Center(
|
return const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -141,12 +139,12 @@ class _HistoryPanelState extends State<HistoryPanel> {
|
|||||||
itemBuilder: (_, index) =>
|
itemBuilder: (_, index) =>
|
||||||
HistoryItem(
|
HistoryItem(
|
||||||
onDelete: (){
|
onDelete: (){
|
||||||
int fixIndex = histories.length - 1 - index;
|
_history.close(histories[index]);
|
||||||
router.closeHistory(fixIndex);
|
|
||||||
},
|
},
|
||||||
onPressed: (){
|
onPressed: ()async {
|
||||||
router.changeRoute(histories[index].copyWith(recordHistory: false));
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
_history.select(histories[index]);
|
||||||
|
// router.changeRoute(histories[index].copyWith(recordHistory: false));
|
||||||
},
|
},
|
||||||
history: histories[index]),
|
history: histories[index]),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
|
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
|
||||||
import '../../router/app_router_delegate.dart';
|
|
||||||
|
import '../../helper/router_history.dart';
|
||||||
|
|
||||||
class RouteHistoryButton extends StatefulWidget {
|
class RouteHistoryButton extends StatefulWidget {
|
||||||
const RouteHistoryButton({super.key});
|
const RouteHistoryButton({super.key});
|
||||||
@@ -11,22 +12,19 @@ class RouteHistoryButton extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
||||||
@override
|
|
||||||
void initState() {
|
late RouterHistory _history ;
|
||||||
super.initState();
|
|
||||||
router.addListener(_onChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void didChangeDependencies() {
|
||||||
router.removeListener(_onChange);
|
super.didChangeDependencies();
|
||||||
super.dispose();
|
_history = RouterHistoryScope.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool hasHistory = router.historyManager.hasHistory;
|
bool hasHistory = _history.hasHistory;
|
||||||
bool hasBackHistory = router.historyManager.hasBackHistory;
|
bool hasBackHistory = _history.hasBackHistory;
|
||||||
Color activeColor = const Color(0xff9195AC);
|
Color activeColor = const Color(0xff9195AC);
|
||||||
Color inActiveColor = const Color(0xffC7CAD5);
|
Color inActiveColor = const Color(0xffC7CAD5);
|
||||||
Color historyColor = hasHistory?activeColor:inActiveColor;
|
Color historyColor = hasHistory?activeColor:inActiveColor;
|
||||||
@@ -38,7 +36,7 @@ class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
|||||||
hoverColor: historyColor,
|
hoverColor: historyColor,
|
||||||
defaultColor: historyColor,
|
defaultColor: historyColor,
|
||||||
icon: CupertinoIcons.arrow_left_circle,
|
icon: CupertinoIcons.arrow_left_circle,
|
||||||
onPressed: hasHistory?router.back:null,
|
onPressed: hasHistory?_history.back:null,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8,),
|
const SizedBox(width: 8,),
|
||||||
HoverIconButton(
|
HoverIconButton(
|
||||||
@@ -46,13 +44,9 @@ class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
|||||||
hoverColor: backHistoryColor,
|
hoverColor: backHistoryColor,
|
||||||
defaultColor: backHistoryColor,
|
defaultColor: backHistoryColor,
|
||||||
icon: CupertinoIcons.arrow_right_circle,
|
icon: CupertinoIcons.arrow_right_circle,
|
||||||
onPressed: hasBackHistory?router.revocation:null,
|
onPressed: hasBackHistory?_history.revocation:null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onChange() {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../app_router_delegate.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class RouteBackIndicator extends StatefulWidget {
|
class RouteBackIndicator extends StatefulWidget {
|
||||||
const RouteBackIndicator({super.key});
|
const RouteBackIndicator({super.key});
|
||||||
|
|
||||||
@@ -9,25 +10,30 @@ class RouteBackIndicator extends StatefulWidget {
|
|||||||
|
|
||||||
class _RouteBackIndicatorState extends State<RouteBackIndicator> {
|
class _RouteBackIndicatorState extends State<RouteBackIndicator> {
|
||||||
|
|
||||||
|
late GoRouterDelegate _delegate ;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
router.addListener(_onChange);
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
_delegate.addListener(_onChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
router.removeListener(_onChange);
|
_delegate.removeListener(_onChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if(router.canPop){
|
bool hasPush = _delegate.currentConfiguration.matches
|
||||||
|
.whereType<ImperativeRouteMatch>().isNotEmpty;
|
||||||
|
if(hasPush){
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: router.backStack,
|
onTap: context.pop,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 26,
|
width: 26,
|
||||||
height: 26,
|
height: 26,
|
||||||
@@ -1,42 +1,53 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'navigation/router/app_router_parser.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'navigation/router/app_router_delegate.dart';
|
import '../pages/empty/empty_page.dart';
|
||||||
|
import 'navigation/router/root.dart';
|
||||||
import '../pages/sort/provider/state.dart';
|
import '../pages/sort/provider/state.dart';
|
||||||
|
import 'navigation/helper/router_history.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});
|
UnitApp({super.key});
|
||||||
|
|
||||||
|
final GoRouter _router = GoRouter(
|
||||||
|
routes: <RouteBase>[rootRoute],
|
||||||
|
onException: (BuildContext ctx,GoRouterState state, GoRouter router){
|
||||||
|
router.go('/404', extra: state.uri.toString());
|
||||||
|
},
|
||||||
|
// errorBuilder: (_,state) {
|
||||||
|
// return EmptyPage(msg: '无法访问: ${state.matchedLocation}',);
|
||||||
|
// }
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SortStateScope(
|
return SortStateScope(
|
||||||
notifier: SortState(),
|
notifier: SortState(),
|
||||||
child: MaterialApp.router(
|
child: RouterHistoryScope(
|
||||||
routerDelegate: router,
|
notifier: RouterHistory(_router.routerDelegate, exclusives: ['/login']),
|
||||||
routeInformationParser: AppRouterParser(),
|
child: MaterialApp.router(
|
||||||
routeInformationProvider: PlatformRouteInformationProvider(
|
routerConfig: _router,
|
||||||
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()
|
||||||
),
|
),
|
||||||
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()
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:iroute/components/project/colors_panel.dart';
|
import 'package:iroute/components/project/colors_panel.dart';
|
||||||
import '../../app/navigation/router/app_router_delegate.dart';
|
|
||||||
|
|
||||||
class ColorPage extends StatefulWidget {
|
class ColorPage extends StatefulWidget {
|
||||||
const ColorPage({super.key});
|
const ColorPage({super.key});
|
||||||
@@ -11,10 +11,26 @@ class ColorPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _ColorPageState extends State<ColorPage> {
|
class _ColorPageState extends State<ColorPage> {
|
||||||
final List<Color> _colors = [
|
final List<Color> _colors = [
|
||||||
Colors.red, Colors.black, Colors.blue, Colors.green, Colors.orange,
|
Colors.red,
|
||||||
Colors.pink, Colors.purple, Colors.indigo, Colors.amber, Colors.cyan,
|
Colors.black,
|
||||||
Colors.redAccent, Colors.grey, Colors.blueAccent, Colors.greenAccent, Colors.orangeAccent,
|
Colors.blue,
|
||||||
Colors.pinkAccent, Colors.purpleAccent, Colors.indigoAccent, Colors.amberAccent, Colors.cyanAccent,
|
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
|
@override
|
||||||
@@ -47,20 +63,19 @@ class _ColorPageState extends State<ColorPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _selectColor(Color color){
|
void _selectColor(Color color) {
|
||||||
String value = color.value.toRadixString(16);
|
String value = color.value.toRadixString(16);
|
||||||
String path = '/app/color/detail?color=$value';
|
context.push('/color/detail?color=$value');
|
||||||
// router.changePath('/app/color/detail',extra: color);
|
// GoRouter.of(context) .pushNamed('colorDetail', queryParameters: {'color': value});
|
||||||
router.changePath(path,recordHistory: true);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toAddPage() async {
|
void _toAddPage() async {
|
||||||
Color? color = await router.changePath('/app/color/add',forResult: true,recordHistory: false);
|
Color? color = await context.push('/color/add');
|
||||||
if (color != null) {
|
|
||||||
setState(() {
|
if (color != null) {
|
||||||
_colors.add(color);
|
setState(() {
|
||||||
});
|
_colors.add(color);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,48 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
|
||||||
class EmptyPage extends StatelessWidget {
|
class EmptyPage extends StatelessWidget {
|
||||||
const EmptyPage({super.key});
|
final String msg;
|
||||||
|
const EmptyPage({super.key, required this.msg});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return DragToMoveWrap(
|
||||||
// appBar: AppBar(
|
child: Scaffold(
|
||||||
// title: Text('界面走丢了'),
|
appBar: PreferredSize(
|
||||||
// ),
|
preferredSize: Size.fromHeight(46),
|
||||||
body: Scaffold(
|
child: Row(
|
||||||
body: Center(
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 16,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
direction: Axis.vertical,
|
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.nearby_error,size: 64, color: Colors.grey),
|
const SizedBox(width: 20,),
|
||||||
Text(
|
Text(
|
||||||
'404 界面丢失',
|
'404 界面丢失',
|
||||||
style: TextStyle(fontSize: 24, color: Colors.grey),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
|
Spacer(),
|
||||||
|
const WindowButtons()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 16,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
direction: Axis.vertical,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.nearby_error,size: 64, color: Colors.redAccent),
|
||||||
|
Text(
|
||||||
|
msg,
|
||||||
|
style: TextStyle(fontSize: 24, color: Colors.grey),
|
||||||
|
),
|
||||||
|
ElevatedButton(onPressed: (){
|
||||||
|
context.go('/');
|
||||||
|
}, child: Text('返回首页'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
lib/v11/pages/empty/empty_panel.dart
Normal file
30
lib/v11/pages/empty/empty_panel.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
|
||||||
|
class EmptyPanel extends StatelessWidget {
|
||||||
|
final String msg;
|
||||||
|
const EmptyPanel({super.key, required this.msg});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 16,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
direction: Axis.vertical,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.nearby_error,size: 64, color: Colors.redAccent),
|
||||||
|
Text(
|
||||||
|
msg,
|
||||||
|
style: TextStyle(fontSize: 24, color: Colors.grey),
|
||||||
|
),
|
||||||
|
ElevatedButton(onPressed: (){
|
||||||
|
context.go('/');
|
||||||
|
}, child: Text('返回首页'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../app/navigation/router/app_router_delegate.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
|
||||||
class LoginPage extends StatelessWidget {
|
class LoginPage extends StatelessWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({super.key});
|
||||||
@@ -14,7 +15,8 @@ class LoginPage extends StatelessWidget {
|
|||||||
Text('Login Page',style: TextStyle(fontSize: 24),),
|
Text('Login Page',style: TextStyle(fontSize: 24),),
|
||||||
const SizedBox(height: 20,),
|
const SizedBox(height: 20,),
|
||||||
ElevatedButton(onPressed: (){
|
ElevatedButton(onPressed: (){
|
||||||
router.changePath('/app/color');
|
GoRouter.of(context).go('/color');
|
||||||
|
// router.changePath('/app/color');
|
||||||
}, child: Text('点击进入'))
|
}, child: Text('点击进入'))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class SortStateScope extends InheritedNotifier<SortState> {
|
|||||||
static SortState of(BuildContext context) =>
|
static SortState of(BuildContext context) =>
|
||||||
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
context.dependOnInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
||||||
|
|
||||||
static SortState read(BuildContext context) =>
|
static SortState read(BuildContext context) {
|
||||||
context.getInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
return context.getInheritedWidgetOfExactType<SortStateScope>()!.notifier!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../provider/state.dart';
|
import '../../provider/state.dart';
|
||||||
import 'color_picker.dart';
|
import 'color_picker.dart';
|
||||||
class SortSettings extends StatefulWidget {
|
class SortSettings extends StatefulWidget {
|
||||||
@@ -66,8 +67,9 @@ class _SortSettingsState extends State<SortSettings> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
splashRadius: 20,
|
splashRadius: 20,
|
||||||
onPressed: (){
|
onPressed: (){
|
||||||
SortState state = SortStateScope.of(context);
|
|
||||||
state.config =state.config.copyWith(
|
SortState state = SortStateScope.read(context);
|
||||||
|
state.config = state.config.copyWith(
|
||||||
count: int.parse(_count.text),
|
count: int.parse(_count.text),
|
||||||
duration: Duration(
|
duration: Duration(
|
||||||
microseconds: int.parse(_duration.text),
|
microseconds: int.parse(_duration.text),
|
||||||
@@ -75,7 +77,8 @@ class _SortSettingsState extends State<SortSettings> {
|
|||||||
seed: int.parse(_seed.text),
|
seed: int.parse(_seed.text),
|
||||||
colorIndex: _colorIndex
|
colorIndex: _colorIndex
|
||||||
);
|
);
|
||||||
Navigator.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
|
// Navigator.of(context).pop();
|
||||||
}, icon: Icon(Icons.check)),
|
}, icon: Icon(Icons.check)),
|
||||||
)],
|
)],
|
||||||
iconTheme: IconThemeData(color: Colors.black),
|
iconTheme: IconThemeData(color: Colors.black),
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../../../../app/navigation/router/iroute_config.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../app/navigation/router/app_router_delegate.dart';
|
|
||||||
import 'sort_button.dart';
|
import 'sort_button.dart';
|
||||||
|
|
||||||
import '../../functions.dart';
|
import '../../functions.dart';
|
||||||
@@ -62,7 +61,8 @@ class SortRailPanel extends StatelessWidget {
|
|||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
router.changePath('/app/sort/settings',style: RouteStyle.push);
|
GoRouter.of(context).push('/sort/settings');
|
||||||
|
// router.changePath('/app/sort/settings',style: RouteStyle.push);
|
||||||
},
|
},
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
CupertinoIcons.settings,
|
CupertinoIcons.settings,
|
||||||
@@ -81,7 +81,7 @@ class SortRailPanel extends StatelessWidget {
|
|||||||
options: sortNameMap.values.toList(),
|
options: sortNameMap.values.toList(),
|
||||||
onSelected: (name) {
|
onSelected: (name) {
|
||||||
state.selectName(name);
|
state.selectName(name);
|
||||||
router.changePath('/app/sort/${name}',recordHistory: true);
|
GoRouter.of(context).go('/sort/player/$name');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
1
lib/v12/app.dart
Normal file
1
lib/v12/app.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export 'app/unit_app.dart';
|
||||||
75
lib/v12/app/authentication/auth_scope.dart
Normal file
75
lib/v12/app/authentication/auth_scope.dart
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
const Map<String,String> _kTestUserMap = {
|
||||||
|
'toly': '123456',
|
||||||
|
'张风捷特烈': '111111',
|
||||||
|
'test1': '111111',
|
||||||
|
};
|
||||||
|
|
||||||
|
class AuthScope extends InheritedNotifier<AuthResult> {
|
||||||
|
const AuthScope({super.key, required super.child, super.notifier});
|
||||||
|
|
||||||
|
static AuthResult of(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.dependOnInheritedWidgetOfExactType<AuthScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AuthResult read(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.getInheritedWidgetOfExactType<AuthScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthStatus{
|
||||||
|
none,
|
||||||
|
success,
|
||||||
|
failed,
|
||||||
|
waitingLogin,
|
||||||
|
waitingLoginOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthResult with ChangeNotifier {
|
||||||
|
String? name;
|
||||||
|
int coin = 0;
|
||||||
|
String? token;
|
||||||
|
AuthStatus status = AuthStatus.none;
|
||||||
|
|
||||||
|
AuthResult();
|
||||||
|
|
||||||
|
Future<(bool,String)> login(String username,String pwd) async{
|
||||||
|
/// 模拟校验 与 模拟延时
|
||||||
|
bool allow = _kTestUserMap[username] == pwd;
|
||||||
|
status = AuthStatus.waitingLogin;
|
||||||
|
notifyListeners();
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
if(allow){
|
||||||
|
name = username;
|
||||||
|
coin = 1994328;
|
||||||
|
token = 'testToken======';
|
||||||
|
status = AuthStatus.success;
|
||||||
|
notifyListeners();
|
||||||
|
return (true,'登陆成功!');
|
||||||
|
}else{
|
||||||
|
/// 可以返回一写其他错误信息
|
||||||
|
status = AuthStatus.failed;
|
||||||
|
notifyListeners();
|
||||||
|
return (false,'登陆失败!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> logout() async{
|
||||||
|
status = AuthStatus.waitingLoginOut;
|
||||||
|
notifyListeners();
|
||||||
|
/// 模拟退出登录请求耗时
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
name = null;
|
||||||
|
coin = 0;
|
||||||
|
token = null;
|
||||||
|
status = AuthStatus.none;
|
||||||
|
notifyListeners();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
25
lib/v12/app/navigation/helper/function.dart
Normal file
25
lib/v12/app/navigation/helper/function.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
Map<String, String> kRouteLabelMap = {
|
||||||
|
'': '',
|
||||||
|
'/color': '颜色板',
|
||||||
|
'/color/add': '添加颜色',
|
||||||
|
'/color/detail': '颜色详情',
|
||||||
|
'/counter': '计数器',
|
||||||
|
'/sort': '排序算法',
|
||||||
|
'/sort/player': '',
|
||||||
|
'/sort/settings': '排序配置',
|
||||||
|
'/user': '我的',
|
||||||
|
'/settings': '系统设置',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
String calcRouteName(BuildContext context,String path){
|
||||||
|
String? result = kRouteLabelMap[path];
|
||||||
|
if(result !=null) return result;
|
||||||
|
if(path.startsWith('/sort/player/')){
|
||||||
|
return path.split('/sort/player/')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return '未知路由';
|
||||||
|
}
|
||||||
108
lib/v12/app/navigation/helper/router_history.dart
Normal file
108
lib/v12/app/navigation/helper/router_history.dart
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class RouterHistoryScope extends InheritedNotifier<RouterHistory> {
|
||||||
|
const RouterHistoryScope({super.key, required super.child, super.notifier});
|
||||||
|
|
||||||
|
static RouterHistory of(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.dependOnInheritedWidgetOfExactType<RouterHistoryScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RouterHistory read(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.getInheritedWidgetOfExactType<RouterHistoryScope>()!
|
||||||
|
.notifier!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterHistory with ChangeNotifier {
|
||||||
|
final List<String> exclusives;
|
||||||
|
|
||||||
|
final GoRouterDelegate delegate;
|
||||||
|
|
||||||
|
final List<RouteMatchList> _histories = [];
|
||||||
|
final List<RouteMatchList> _backHistories = [];
|
||||||
|
|
||||||
|
List<RouteMatchList> get histories => _histories;
|
||||||
|
|
||||||
|
RouterHistory(this.delegate, {this.exclusives = const []}) {
|
||||||
|
delegate.addListener(_onRouteChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 用于记录当前历史记录
|
||||||
|
/// 清空历史之后,切换时,先记录 _current
|
||||||
|
RouteMatchList? _current;
|
||||||
|
|
||||||
|
void _onRouteChange() {
|
||||||
|
/// 当没有历史,且 _current 非空
|
||||||
|
if (_histories.isEmpty && _current != null) {
|
||||||
|
_histories.add(_current!);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteMatchList matchList = delegate.currentConfiguration;
|
||||||
|
if (_histories.isNotEmpty && matchList == _histories.last
|
||||||
|
|| matchList.isEmpty
|
||||||
|
) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String uri = matchList.last.matchedLocation;
|
||||||
|
if (exclusives.contains(uri)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_recode(matchList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将 [history] 加入历史记录
|
||||||
|
void _recode(RouteMatchList history) {
|
||||||
|
_current = history;
|
||||||
|
_histories.add(history);
|
||||||
|
if (hasHistory) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasHistory => _histories.length > 1;
|
||||||
|
|
||||||
|
bool get hasBackHistory => _backHistories.isNotEmpty;
|
||||||
|
|
||||||
|
/// 历史回退操作
|
||||||
|
/// 将当前顶层移除,并加入 [_backHistories] 撤销列表
|
||||||
|
/// 并转到前一路径 [_histories.last]
|
||||||
|
void back() {
|
||||||
|
if (!hasHistory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RouteMatchList top = _histories.removeLast();
|
||||||
|
_backHistories.add(top);
|
||||||
|
if (_histories.isNotEmpty) {
|
||||||
|
delegate.setNewRoutePath(_histories.last);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 撤销回退操作
|
||||||
|
/// 取出回退列表的最后元素,跳转到该路径
|
||||||
|
void revocation() {
|
||||||
|
RouteMatchList target = _backHistories.removeLast();
|
||||||
|
delegate.setNewRoutePath(target);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(RouteMatchList history) {
|
||||||
|
_histories.remove(history);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_histories.clear();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(RouteMatchList history) {
|
||||||
|
_histories.remove(history);
|
||||||
|
delegate.setNewRoutePath(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
lib/v12/app/navigation/router/app.dart
Normal file
44
lib/v12/app/navigation/router/app.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/counter/counter_page.dart';
|
||||||
|
import '../../../pages/user/user_page.dart';
|
||||||
|
import '../../../pages/settings/settings_page.dart';
|
||||||
|
import '../../../pages/empty/empty_panel.dart';
|
||||||
|
import '../views/app_navigation.dart';
|
||||||
|
import 'color.dart';
|
||||||
|
import 'sort.dart';
|
||||||
|
|
||||||
|
|
||||||
|
final RouteBase appRoute = ShellRoute(
|
||||||
|
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||||
|
return AppNavigation(navigator: child);
|
||||||
|
},
|
||||||
|
routes: <RouteBase>[
|
||||||
|
colorRouters,
|
||||||
|
GoRoute(
|
||||||
|
path: 'counter',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const CounterPage();
|
||||||
|
}),
|
||||||
|
sortRouters,
|
||||||
|
GoRoute(
|
||||||
|
path: 'user',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const UserPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'settings',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const SettingPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '404',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
String msg = '无法访问: ${state.extra}';
|
||||||
|
return EmptyPanel(msg: msg);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
35
lib/v12/app/navigation/router/color.dart
Normal file
35
lib/v12/app/navigation/router/color.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/color/color_add_page.dart';
|
||||||
|
import '../../../pages/color/color_detail_page.dart';
|
||||||
|
import '../../../pages/color/color_page.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
final RouteBase colorRouters = GoRoute(
|
||||||
|
path: 'color',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const ColorPage();
|
||||||
|
},
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: 'detail',
|
||||||
|
name: 'colorDetail',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
String? selectedColor = state.uri.queryParameters['color'];
|
||||||
|
Color color = Colors.black;
|
||||||
|
if (selectedColor != null) {
|
||||||
|
color = Color(int.parse(selectedColor, radix: 16));
|
||||||
|
}
|
||||||
|
return ColorDetailPage(color: color);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'add',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const ColorAddPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
27
lib/v12/app/navigation/router/root.dart
Normal file
27
lib/v12/app/navigation/router/root.dart
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/login/login.dart';
|
||||||
|
import 'app.dart';
|
||||||
|
|
||||||
|
final RouteBase rootRoute = GoRoute(
|
||||||
|
path: '/',
|
||||||
|
redirect: _redirect,
|
||||||
|
routes: [
|
||||||
|
appRoute,
|
||||||
|
GoRoute(
|
||||||
|
path: 'login',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const LoginPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
FutureOr<String?> _redirect(BuildContext context, GoRouterState state) {
|
||||||
|
if(state.fullPath=='/'){
|
||||||
|
return '/color';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
44
lib/v12/app/navigation/router/sort.dart
Normal file
44
lib/v12/app/navigation/router/sort.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import '../../../pages/sort/provider/state.dart';
|
||||||
|
|
||||||
|
import '../../../pages/sort/views/player/sort_player.dart';
|
||||||
|
import '../../../pages/sort/views/settings/sort_setting.dart';
|
||||||
|
import '../../../pages/sort/views/sort_page/sort_page.dart';
|
||||||
|
|
||||||
|
final RouteBase sortRouters = ShellRoute(
|
||||||
|
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||||
|
return SortNavigation(navigator: child);
|
||||||
|
},
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'sort',
|
||||||
|
redirect: _redirectSort,
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: 'player/:name',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
print(state.pathParameters);// 获取路径参数
|
||||||
|
return const SortPlayer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: 'settings',
|
||||||
|
builder: (BuildContext context, GoRouterState state) {
|
||||||
|
return const SortSettings();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
FutureOr<String?> _redirectSort(BuildContext context, GoRouterState state) {
|
||||||
|
if (state.fullPath == '/sort') {
|
||||||
|
String name = SortStateScope.read(context).config.name;
|
||||||
|
return '/sort/player/$name';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -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/v12/app/navigation/transition/fade_transition_page.dart
Normal file
52
lib/v12/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/v12/app/navigation/transition/no_transition_page.dart
Normal file
47
lib/v12/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;
|
||||||
|
}
|
||||||
50
lib/v12/app/navigation/views/app_navigation.dart
Normal file
50
lib/v12/app/navigation/views/app_navigation.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:flutter/material.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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/v12/app/navigation/views/app_navigation_rail.dart
Normal file
65
lib/v12/app/navigation/views/app_navigation_rail.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
|
||||||
|
class AppNavigationRail extends StatefulWidget {
|
||||||
|
const AppNavigationRail({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AppNavigationRail> createState() => _AppNavigationRailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppNavigationRailState extends State<AppNavigationRail> {
|
||||||
|
final List<MenuMeta> deskNavBarMenus = const [
|
||||||
|
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined, path: '/color'),
|
||||||
|
MenuMeta(label: '计数器', icon: Icons.add_chart, path: '/counter'),
|
||||||
|
MenuMeta(label: '排序', icon: Icons.sort, path: '/sort'),
|
||||||
|
MenuMeta(label: '我的', icon: Icons.person, path: '/user'),
|
||||||
|
MenuMeta(label: '设置', icon: Icons.settings, path: '/settings'),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
@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.1.1',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: const Color(0xff3975c6),
|
||||||
|
onDestinationSelected: _onDestinationSelected,
|
||||||
|
selectedIndex: activeIndex,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final RegExp _segReg = RegExp(r'/\w+');
|
||||||
|
|
||||||
|
int? get activeIndex {
|
||||||
|
final String path = GoRouterState.of(context).uri.toString();
|
||||||
|
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==4){
|
||||||
|
GoRouter.of(context).push(path);
|
||||||
|
}else{
|
||||||
|
GoRouter.of(context).go(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.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();
|
||||||
|
late GoRouterDelegate _delegate ;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
|
||||||
|
_onRouteChange();
|
||||||
|
_delegate.addListener(_onRouteChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRouteChange() {
|
||||||
|
|
||||||
|
List<RouteMatch> matches = _delegate.currentConfiguration.matches;
|
||||||
|
if(matches.isEmpty) return;
|
||||||
|
RouteMatch match = matches.last;
|
||||||
|
if(match is ImperativeRouteMatch){
|
||||||
|
_controller.text = match.matches.uri.toString();
|
||||||
|
}else{
|
||||||
|
_controller.text = match.matchedLocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
_delegate.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
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
137
lib/v12/app/navigation/views/app_top_bar/app_top_bar.dart
Normal file
137
lib/v12/app/navigation/views/app_top_bar/app_top_bar.dart
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import '../../helper/function.dart';
|
||||||
|
import '../route_back_indicator.dart';
|
||||||
|
import 'app_router_editor.dart';
|
||||||
|
import 'route_history_button.dart';
|
||||||
|
import 'history_view_icon.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(),
|
||||||
|
// Spacer(),
|
||||||
|
Expanded(
|
||||||
|
child: Row(textDirection: TextDirection.rtl, children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 12.0),
|
||||||
|
child: VerticalDivider(
|
||||||
|
width: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
HistoryViewIcon(),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: AppRouterEditor(
|
||||||
|
onSubmit: (path) {
|
||||||
|
GoRouter.of(context).go(path);
|
||||||
|
// => router.changePath(path)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
RouteHistoryButton(),
|
||||||
|
])),
|
||||||
|
const WindowButtons()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterIndicator extends StatefulWidget {
|
||||||
|
const RouterIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouterIndicator> createState() => _RouterIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouterIndicatorState extends State<RouterIndicator> {
|
||||||
|
late GoRouterDelegate _delegate;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
_delegate.addListener(_onRouterChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_delegate.removeListener(_onRouterChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<RouteMatch> matches = _delegate.currentConfiguration.matches;
|
||||||
|
if(matches.isEmpty) return const SizedBox();
|
||||||
|
RouteMatch match = _delegate.currentConfiguration.matches.last;
|
||||||
|
|
||||||
|
print(
|
||||||
|
"=========_RouterIndicatorState:build==${match.matchedLocation}========");
|
||||||
|
|
||||||
|
return TolyBreadcrumb(
|
||||||
|
items: pathToBreadcrumbItems(context, match.matchedLocation),
|
||||||
|
onTapItem: (item) {
|
||||||
|
if (item.to != null) {
|
||||||
|
GoRouter.of(context).go(item.to!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onRouterChange() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BreadcrumbItem> pathToBreadcrumbItems(
|
||||||
|
BuildContext context, 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 = calcRouteName(context, to);
|
||||||
|
if (label.isNotEmpty) {
|
||||||
|
result
|
||||||
|
.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> kRouteLabelMap = {
|
||||||
|
'': '',
|
||||||
|
'/color': '颜色板',
|
||||||
|
'/color/add': '添加颜色',
|
||||||
|
'/color/detail': '颜色详情',
|
||||||
|
'/counter': '计数器',
|
||||||
|
'/sort': '排序算法',
|
||||||
|
'/sort/player': '演示',
|
||||||
|
'/sort/settings': '排序配置',
|
||||||
|
'/user': '我的',
|
||||||
|
'/settings': '系统设置',
|
||||||
|
};
|
||||||
156
lib/v12/app/navigation/views/app_top_bar/history_view_icon.dart
Normal file
156
lib/v12/app/navigation/views/app_top_bar/history_view_icon.dart
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:iroute/components/components.dart';
|
||||||
|
import 'package:iroute/v11/app/navigation/helper/function.dart';
|
||||||
|
import '../../helper/router_history.dart';
|
||||||
|
import 'app_top_bar.dart';
|
||||||
|
|
||||||
|
class HistoryViewIcon extends StatelessWidget{
|
||||||
|
const HistoryViewIcon({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
RouterHistory routerHistory = RouterHistoryScope.read(context);
|
||||||
|
|
||||||
|
return MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: PopPanel(
|
||||||
|
offset: const Offset(0, 10),
|
||||||
|
panel: SizedBox(
|
||||||
|
height: 350,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildTopBar(routerHistory),
|
||||||
|
const Expanded(
|
||||||
|
child:HistoryPanel(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.history,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTopBar( RouterHistory routerHistory) {
|
||||||
|
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: routerHistory.clear, child: const Text('清空历史'))
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HistoryItem extends StatefulWidget {
|
||||||
|
final RouteMatchList 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) {
|
||||||
|
String path = widget.history.matches.last.matchedLocation;
|
||||||
|
return MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: widget.onPressed,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(path),
|
||||||
|
const SizedBox(
|
||||||
|
height: 2,
|
||||||
|
),
|
||||||
|
Text(calcRouteName(context, 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> {
|
||||||
|
late RouterHistory _history ;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_history = RouterHistoryScope.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<RouteMatchList> histories = _history.histories.reversed.toList();
|
||||||
|
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: (){
|
||||||
|
_history.close(histories[index]);
|
||||||
|
},
|
||||||
|
onPressed: ()async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
_history.select(histories[index]);
|
||||||
|
// router.changeRoute(histories[index].copyWith(recordHistory: false));
|
||||||
|
},
|
||||||
|
history: histories[index]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onChange() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
|
||||||
|
|
||||||
|
import '../../helper/router_history.dart';
|
||||||
|
|
||||||
|
class RouteHistoryButton extends StatefulWidget {
|
||||||
|
const RouteHistoryButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouteHistoryButton> createState() => _RouteHistoryButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouteHistoryButtonState extends State<RouteHistoryButton> {
|
||||||
|
|
||||||
|
late RouterHistory _history ;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
_history = RouterHistoryScope.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
bool hasHistory = _history.hasHistory;
|
||||||
|
bool hasBackHistory = _history.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?_history.back:null,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8,),
|
||||||
|
HoverIconButton(
|
||||||
|
size: 20,
|
||||||
|
hoverColor: backHistoryColor,
|
||||||
|
defaultColor: backHistoryColor,
|
||||||
|
icon: CupertinoIcons.arrow_right_circle,
|
||||||
|
onPressed: hasBackHistory?_history.revocation:null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
lib/v12/app/navigation/views/not_find_view.dart
Normal file
25
lib/v12/app/navigation/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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
lib/v12/app/navigation/views/route_back_indicator.dart
Normal file
58
lib/v12/app/navigation/views/route_back_indicator.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
class RouteBackIndicator extends StatefulWidget {
|
||||||
|
const RouteBackIndicator({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RouteBackIndicator> createState() => _RouteBackIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RouteBackIndicatorState extends State<RouteBackIndicator> {
|
||||||
|
|
||||||
|
late GoRouterDelegate _delegate ;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_delegate = GoRouter.of(context).routerDelegate;
|
||||||
|
_delegate.addListener(_onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_delegate.removeListener(_onChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
bool hasPush = _delegate.currentConfiguration.matches
|
||||||
|
.whereType<ImperativeRouteMatch>().isNotEmpty;
|
||||||
|
if(hasPush){
|
||||||
|
return MouseRegion(
|
||||||
|
cursor: SystemMouseCursors.click,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: context.pop,
|
||||||
|
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(() {
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user