v7
This commit is contained in:
177
lib/v8/app/navigation/router/app_router_delegate.dart
Normal file
177
lib/v8/app/navigation/router/app_router_delegate.dart
Normal file
@@ -0,0 +1,177 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.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();
|
||||
|
||||
class AppRouterDelegate extends RouterDelegate<Object> with ChangeNotifier {
|
||||
String _path = '/color';
|
||||
|
||||
String get path => _path;
|
||||
|
||||
final IRoutePageBuilder? notFindPageBuilder;
|
||||
|
||||
AppRouterDelegate({this.notFindPageBuilder}) {
|
||||
_historyManager.recode(IRouteConfig(uri: Uri.parse(path)));
|
||||
}
|
||||
|
||||
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 Map<String, Completer<dynamic>> _completerMap = {};
|
||||
final Map<String, dynamic> _pathExtraMap = {};
|
||||
final List<String> keepAlivePath = [];
|
||||
|
||||
FutureOr<dynamic> changeRoute(IRouteConfig config) {
|
||||
String value = config.uri.path;
|
||||
if (_path == value) null;
|
||||
if (config.forResult) {
|
||||
_completerMap[value] = Completer();
|
||||
}
|
||||
if (config.keepAlive) {
|
||||
if (keepAlivePath.contains(value)) {
|
||||
keepAlivePath.remove(value);
|
||||
}
|
||||
keepAlivePath.add(value);
|
||||
}
|
||||
if (config.extra != null) {
|
||||
_pathExtraMap[value] = config.extra;
|
||||
}
|
||||
|
||||
if (config.recordHistory) {
|
||||
_historyManager.recode(config);
|
||||
}
|
||||
|
||||
_path = value;
|
||||
notifyListeners();
|
||||
if (config.forResult) {
|
||||
return _completerMap[value]!.future;
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<dynamic> changePath(
|
||||
String value, {
|
||||
bool forResult = false,
|
||||
Object? extra,
|
||||
bool keepAlive = false,
|
||||
bool recordHistory = true,
|
||||
}) {
|
||||
return changeRoute(IRouteConfig(
|
||||
uri: Uri.parse(value),
|
||||
forResult: forResult,
|
||||
extra: extra,
|
||||
keepAlive: keepAlive,
|
||||
recordHistory: recordHistory,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Navigator(
|
||||
onPopPage: _onPopPage,
|
||||
pages: _buildPages(context, path),
|
||||
);
|
||||
}
|
||||
|
||||
List<Page> _buildPages(BuildContext context, String path) {
|
||||
List<Page> pages = [];
|
||||
List<Page> topPages = _buildPageByPathFromTree(context, path);
|
||||
|
||||
if (keepAlivePath.isNotEmpty) {
|
||||
for (String alivePath in keepAlivePath) {
|
||||
if (alivePath != path) {
|
||||
pages.addAll(_buildPageByPathFromTree(context, alivePath));
|
||||
}
|
||||
}
|
||||
|
||||
/// 去除和 topPages 中重复的界面
|
||||
pages.removeWhere(
|
||||
(element) => topPages.map((e) => e.key).contains(element.key));
|
||||
}
|
||||
|
||||
pages.addAll(topPages);
|
||||
return pages;
|
||||
}
|
||||
|
||||
List<Page> _buildPageByPathFromTree(BuildContext context, String path) {
|
||||
List<Page> result = [];
|
||||
List<IRouteNode> iRoutes = rootRoute.find(path);
|
||||
if (iRoutes.isNotEmpty) {
|
||||
for (int i = 0; i < iRoutes.length; i++) {
|
||||
IRouteNode iroute = iRoutes[i];
|
||||
String path = iroute.path;
|
||||
Object? extra = _pathExtraMap[path];
|
||||
bool keepAlive = keepAlivePath.contains(path);
|
||||
bool forResult = _completerMap.containsKey(path);
|
||||
IRouteConfig config = IRouteConfig(
|
||||
uri: Uri.parse(path),
|
||||
extra: extra,
|
||||
keepAlive: keepAlive,
|
||||
forResult: forResult,
|
||||
);
|
||||
Page? page;
|
||||
if (iroute is NotFindNode) {
|
||||
page = (notFindPageBuilder ?? _defaultNotFindPageBuilder)(context, config);
|
||||
} else {
|
||||
page = iroute.createPage(context, config);
|
||||
}
|
||||
if (page != null) {
|
||||
result.add(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> popRoute() async {
|
||||
print('=======popRoute=========');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _onPopPage(Route route, result) {
|
||||
if (_completerMap.containsKey(path)) {
|
||||
_completerMap[path]?.complete(result);
|
||||
_completerMap.remove(path);
|
||||
}
|
||||
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(configuration) async {}
|
||||
}
|
||||
115
lib/v8/app/navigation/router/iroute.dart
Normal file
115
lib/v8/app/navigation/router/iroute.dart
Normal file
@@ -0,0 +1,115 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
import 'iroute_config.dart';
|
||||
|
||||
typedef IRoutePageBuilder = Page? Function(
|
||||
BuildContext context,
|
||||
IRouteConfig data,
|
||||
);
|
||||
|
||||
typedef IRouteWidgetBuilder = Widget? Function(
|
||||
BuildContext context,
|
||||
IRouteConfig data,
|
||||
);
|
||||
|
||||
abstract class IRouteNode {
|
||||
final String path;
|
||||
final List<IRouteNode> children;
|
||||
|
||||
const IRouteNode({
|
||||
required this.path,
|
||||
required this.children,
|
||||
});
|
||||
|
||||
Page? createPage(BuildContext context, IRouteConfig config);
|
||||
|
||||
List<IRouteNode> find(String input,) {
|
||||
return findNodes(this, Uri.parse(input), 0, '/', []);
|
||||
}
|
||||
|
||||
List<IRouteNode> findNodes(
|
||||
IRouteNode node,
|
||||
Uri uri,
|
||||
int deep,
|
||||
String prefix,
|
||||
List<IRouteNode> result,
|
||||
) {
|
||||
List<String> parts = uri.pathSegments;
|
||||
if (deep > parts.length - 1) {
|
||||
return result;
|
||||
}
|
||||
String target = parts[deep];
|
||||
if (node.children.isNotEmpty) {
|
||||
target = prefix + target;
|
||||
List<IRouteNode> nodes = node.children.where((e) => e.path == target).toList();
|
||||
bool match = nodes.isNotEmpty;
|
||||
if (match) {
|
||||
IRouteNode matched = nodes.first;
|
||||
result.add(matched);
|
||||
String nextPrefix = '${matched.path}/';
|
||||
findNodes(matched, uri, ++deep, nextPrefix, result);
|
||||
}else{
|
||||
result.add(NotFindNode(path: target));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// 优先调用 [pageBuilder] 构建 Page
|
||||
/// 没有 [pageBuilder] 时, 使用 [widgetBuilder] 构建组件
|
||||
/// 没有 [pageBuilder] 和 [widgetBuilder] 时, 使用 [widget] 构建组件
|
||||
class IRoute extends IRouteNode {
|
||||
final IRoutePageBuilder? pageBuilder;
|
||||
final IRouteWidgetBuilder? widgetBuilder;
|
||||
final Widget? widget;
|
||||
|
||||
const IRoute({
|
||||
required super.path,
|
||||
super.children = const [],
|
||||
this.widget,
|
||||
this.pageBuilder,
|
||||
this.widgetBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
Page? createPage(BuildContext context, IRouteConfig config) {
|
||||
if (pageBuilder != null) {
|
||||
return pageBuilder!(context, config);
|
||||
}
|
||||
Widget? child;
|
||||
if (widgetBuilder != null) {
|
||||
child = widgetBuilder!(context, config);
|
||||
}
|
||||
child ??= widget;
|
||||
if (child != null) {
|
||||
return MaterialPage(child: child, key: config.pageKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// 未知路由
|
||||
class NotFindNode extends IRouteNode{
|
||||
NotFindNode({required super.path, super.children= const[]});
|
||||
|
||||
@override
|
||||
Page? createPage(BuildContext context, IRouteConfig config) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class CellIRouter extends IRoute {
|
||||
final CellBuilder cellBuilder;
|
||||
|
||||
CellIRouter(
|
||||
{required super.path,
|
||||
super.pageBuilder,
|
||||
super.children,
|
||||
required this.cellBuilder});
|
||||
}
|
||||
|
||||
typedef CellBuilder = Widget Function(BuildContext context, Widget child);
|
||||
|
||||
35
lib/v8/app/navigation/router/iroute_config.dart
Normal file
35
lib/v8/app/navigation/router/iroute_config.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class IRouteConfig {
|
||||
final Object? extra;
|
||||
final bool forResult;
|
||||
final Uri uri;
|
||||
final bool keepAlive;
|
||||
final bool recordHistory;
|
||||
|
||||
const IRouteConfig({
|
||||
this.extra,
|
||||
required this.uri,
|
||||
this.forResult = false,
|
||||
this.keepAlive = false,
|
||||
this.recordHistory = false,
|
||||
});
|
||||
|
||||
String get path => uri.path;
|
||||
|
||||
IRouteConfig copyWith({
|
||||
Object? extra,
|
||||
bool? forResult,
|
||||
bool? keepAlive,
|
||||
bool? recordHistory,
|
||||
}) =>
|
||||
IRouteConfig(
|
||||
extra: extra ?? this.extra,
|
||||
forResult: forResult ?? this.forResult,
|
||||
keepAlive: keepAlive ?? this.keepAlive,
|
||||
recordHistory: recordHistory ?? this.recordHistory,
|
||||
uri: uri,
|
||||
);
|
||||
|
||||
ValueKey get pageKey => ValueKey(path);
|
||||
}
|
||||
47
lib/v8/app/navigation/router/route_history_manager.dart
Normal file
47
lib/v8/app/navigation/router/route_history_manager.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'iroute_config.dart';
|
||||
|
||||
typedef OnRouteChange = void Function(IRouteConfig config);
|
||||
|
||||
class RouteHistoryManager{
|
||||
final List<IRouteConfig> _histories = [];
|
||||
final List<IRouteConfig> _backHistories = [];
|
||||
|
||||
List<IRouteConfig> get histories => _histories.reversed.toList();
|
||||
|
||||
bool get hasHistory => _histories.length > 1;
|
||||
|
||||
bool get hasBackHistory => _backHistories.isNotEmpty;
|
||||
|
||||
/// 将 [config] 加入历史记录
|
||||
void recode(IRouteConfig config){
|
||||
if (_histories.isNotEmpty && config.path == _histories.last.path) return;
|
||||
_histories.add(config);
|
||||
}
|
||||
|
||||
/// 历史回退操作
|
||||
/// 将当前顶层移除,并加入 [_backHistories] 撤销列表
|
||||
/// 并转到前一路径 [_histories.last]
|
||||
void back(OnRouteChange callback) {
|
||||
if (!hasHistory) return;
|
||||
IRouteConfig top = _histories.removeLast();
|
||||
_backHistories.add(top);
|
||||
if (_histories.isNotEmpty) {
|
||||
callback(_histories.last);
|
||||
}
|
||||
}
|
||||
|
||||
/// 撤销回退操作
|
||||
/// 取出回退列表的最后元素,跳转到该路径
|
||||
void revocation(OnRouteChange callback) {
|
||||
IRouteConfig target = _backHistories.removeLast();
|
||||
callback(target);
|
||||
}
|
||||
|
||||
void close(int index) {
|
||||
_histories.removeAt(index);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_histories.clear();
|
||||
}
|
||||
}
|
||||
41
lib/v8/app/navigation/router/routes.dart
Normal file
41
lib/v8/app/navigation/router/routes.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'iroute_config.dart';
|
||||
import 'iroute.dart';
|
||||
import '../../../pages/color/color_add_page.dart';
|
||||
import '../../../pages/color/color_detail_page.dart';
|
||||
import '../../../pages/color/color_page.dart';
|
||||
import '../../../pages/counter/counter_page.dart';
|
||||
import '../../../pages/user/user_page.dart';
|
||||
import '../../../pages/settings/settings_page.dart';
|
||||
import '../../../pages/sort/views/sort_page.dart';
|
||||
|
||||
|
||||
IRoute rootRoute = const IRoute(
|
||||
path: 'root',
|
||||
children: [
|
||||
IRoute(
|
||||
path: '/color',
|
||||
widget: ColorPage(),
|
||||
children: [
|
||||
IRoute(path: '/color/detail', widgetBuilder: _buildColorDetail),
|
||||
IRoute(path: '/color/add', widget: ColorAddPage()),
|
||||
],
|
||||
),
|
||||
IRoute(path: '/counter', widget: CounterPage()),
|
||||
IRoute(path: '/sort', widget: SortPage()),
|
||||
IRoute(path: '/user', widget: UserPage()),
|
||||
IRoute(path: '/settings', widget: SettingPage()),
|
||||
],
|
||||
);
|
||||
|
||||
Widget? _buildColorDetail(BuildContext context, IRouteConfig data) {
|
||||
final Map<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);
|
||||
}
|
||||
25
lib/v8/app/navigation/router/views/not_find_view.dart
Normal file
25
lib/v8/app/navigation/router/views/not_find_view.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NotFindPage extends StatelessWidget {
|
||||
const NotFindPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Material(
|
||||
child: Center(
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
direction: Axis.vertical,
|
||||
children: [
|
||||
Icon(Icons.nearby_error,size: 64, color: Colors.redAccent),
|
||||
Text(
|
||||
'404 Page Not Find',
|
||||
style: TextStyle(fontSize: 24, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user