books
This commit is contained in:
@@ -1,113 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:iroute/components/components.dart';
|
||||
import 'package:toly_menu/src/menu.dart';
|
||||
import 'package:toly_menu/src/model/menu_state.dart';
|
||||
import 'package:toly_menu/toly_menu.dart';
|
||||
|
||||
import '../v12/app/navigation/transition/fade_page_transitions_builder.dart';
|
||||
import 'router/menus/menu_tree.dart';
|
||||
import 'views/top_logo.dart';
|
||||
import 'views/top_bar.dart';
|
||||
import 'package:iroute/navigation/router/routers/app.dart';
|
||||
|
||||
class TolyBooksApp extends StatelessWidget {
|
||||
final GoRouter _router = GoRouter(
|
||||
initialLocation: '/dashboard/view',
|
||||
routes: <RouteBase>[appRoute],
|
||||
onException: (BuildContext ctx, GoRouterState state, GoRouter router) {
|
||||
router.go('/404', extra: state.uri.toString());
|
||||
},
|
||||
// redirect: _authRedirect
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
routerConfig: _router,
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
fontFamily: "宋体",
|
||||
primarySwatch: Colors.blue,
|
||||
pageTransitionsTheme: const PageTransitionsTheme(builders: {
|
||||
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
||||
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
||||
TargetPlatform.macOS: FadePageTransitionsBuilder(),
|
||||
TargetPlatform.windows: FadePageTransitionsBuilder(),
|
||||
TargetPlatform.linux: FadePageTransitionsBuilder(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BookAppNavigation extends StatefulWidget {
|
||||
final Widget content;
|
||||
const BookAppNavigation({super.key, required this.content});
|
||||
|
||||
@override
|
||||
State<BookAppNavigation> createState() => _BookAppNavigationState();
|
||||
}
|
||||
|
||||
class _BookAppNavigationState extends State<BookAppNavigation> {
|
||||
MenuState state = MenuState(
|
||||
expandMenus: ['/dashboard'],
|
||||
activeMenu: '/dashboard/view',
|
||||
items: rootMenu.children);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
DragToMoveWrap(
|
||||
child: Container(
|
||||
color: const Color(0xff001529),
|
||||
width: 210,
|
||||
child: Column(
|
||||
children: [
|
||||
TopLogo(),
|
||||
Expanded(child: TolyMenu(state: state, onSelect: _onSelect)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [AppTopBar(), Expanded(child: widget.content)],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onSelect(MenuNode menu) {
|
||||
if (menu.isLeaf) {
|
||||
state = state.copyWith(activeMenu: menu.path);
|
||||
print(menu.path);
|
||||
// print(;
|
||||
context.go(menu.path);
|
||||
} else {
|
||||
List<String> menus = [];
|
||||
String path = menu.path.substring(1);
|
||||
List<String> parts = path.split('/');
|
||||
|
||||
if (parts.isNotEmpty) {
|
||||
String path = '';
|
||||
for (String part in parts) {
|
||||
path += '/$part';
|
||||
menus.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.expandMenus.contains(menu.path)) {
|
||||
menus.remove(menu.path);
|
||||
}
|
||||
|
||||
state = state.copyWith(expandMenus: menus);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> animaMenus = {
|
||||
'path' : '/anima',
|
||||
'label' : '动画-流光幻影',
|
||||
'icon': FxIcon.icon_anima,
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
32
lib/navigation/router/menus/dashboard.dart
Normal file
32
lib/navigation/router/menus/dashboard.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String, dynamic> dashboard = {
|
||||
'path': '/dashboard',
|
||||
'label': '面板总览',
|
||||
'icon': FxIcon.dashboard,
|
||||
'children': [
|
||||
{
|
||||
'path': '/view',
|
||||
'label': '小册全集',
|
||||
},
|
||||
{
|
||||
'path': '/chat',
|
||||
'label': '读者交流',
|
||||
'children': [
|
||||
{
|
||||
'path': '/a',
|
||||
'label': '第一交流区',
|
||||
},
|
||||
{
|
||||
'path': '/b',
|
||||
'label': '第二交流区',
|
||||
},
|
||||
{
|
||||
'path': '/c',
|
||||
'label': '第三交流区',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:iroute/app/res/fx_icon.dart';
|
||||
|
||||
Map<String, dynamic> drawMenus = {
|
||||
'path': '/draw',
|
||||
'icon': FxIcon.icon_paint,
|
||||
'label': '绘制-妙笔生花',
|
||||
'children': [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> dreamMenus = {
|
||||
'path' : '/dream',
|
||||
'label' : '基础-梦始之地',
|
||||
'icon': FxIcon.icon_dream,
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> layoutMenus = {
|
||||
'path' : '/layout',
|
||||
'label' : '布局-薪火相传',
|
||||
'icon': FxIcon.icon_layout,
|
||||
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
9
lib/navigation/router/menus/menu_scope/menu_history.dart
Normal file
9
lib/navigation/router/menus/menu_scope/menu_history.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
class MenuHistory {
|
||||
final String menuLabel;
|
||||
final String menuPath;
|
||||
|
||||
MenuHistory({
|
||||
required this.menuLabel,
|
||||
required this.menuPath,
|
||||
});
|
||||
}
|
||||
200
lib/navigation/router/menus/menu_scope/menu_scope.dart
Normal file
200
lib/navigation/router/menus/menu_scope/menu_scope.dart
Normal file
@@ -0,0 +1,200 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:toly_menu/toly_menu.dart';
|
||||
|
||||
import '../menu_tree.dart';
|
||||
import 'menu_history.dart';
|
||||
|
||||
class MenuScope extends InheritedNotifier<MenuStore> {
|
||||
const MenuScope({super.key, required super.child, super.notifier});
|
||||
|
||||
static MenuStore of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<MenuScope>()!.notifier!;
|
||||
}
|
||||
|
||||
static MenuStore read(BuildContext context) {
|
||||
return context.getInheritedWidgetOfExactType<MenuScope>()!.notifier!;
|
||||
}
|
||||
}
|
||||
|
||||
class MenuStore with ChangeNotifier {
|
||||
late MenuState _state;
|
||||
MenuState get state => _state;
|
||||
final GoRouter goRouter;
|
||||
final List<MenuHistory> _history = [];
|
||||
List<MenuHistory> get history => _history;
|
||||
|
||||
MenuStore({
|
||||
List<String> expandMenus = const [],
|
||||
required this.goRouter,
|
||||
required String activeMenu,
|
||||
}) {
|
||||
_state = MenuState(
|
||||
expandMenus: expandMenus,
|
||||
activeMenu: activeMenu,
|
||||
items: rootMenu.children,
|
||||
);
|
||||
|
||||
_history.add(MenuHistory(
|
||||
menuLabel: pathName(activeMenu) ?? activeMenu,
|
||||
menuPath: activeMenu,
|
||||
));
|
||||
goRouter.routerDelegate.addListener(_onRouterChange);
|
||||
}
|
||||
|
||||
MenuNode? get currentNode => queryMenuNodeByPath(
|
||||
MenuNode(
|
||||
path: '',
|
||||
label: '',
|
||||
deep: -1,
|
||||
children: _state.items,
|
||||
),
|
||||
_state.activeMenu);
|
||||
|
||||
String? pathName(String path) => queryMenuNodeByPath(
|
||||
MenuNode(
|
||||
path: '',
|
||||
label: '',
|
||||
deep: -1,
|
||||
children: _state.items,
|
||||
),
|
||||
path)
|
||||
?.label;
|
||||
|
||||
void selectMenuPath(String path) {
|
||||
print("======================selectMenuPath:$path======${_shouldAddHistory}==============");
|
||||
MenuNode root = MenuNode(
|
||||
path: '',
|
||||
label: '',
|
||||
deep: -1,
|
||||
children: _state.items,
|
||||
);
|
||||
List<MenuNode> result = findNodes(root, Uri.parse(path), 0, '/', []);
|
||||
if (result.isNotEmpty) {
|
||||
List<String> expandMenus = [];
|
||||
if (result.length > 1) {
|
||||
expandMenus.addAll(
|
||||
result.sublist(0, result.length - 1).map((e) => e.path).toList());
|
||||
}
|
||||
if(_shouldAddHistory){
|
||||
_history.add(MenuHistory(
|
||||
menuLabel: result.last.label,
|
||||
menuPath: result.last.path,
|
||||
));
|
||||
}else{
|
||||
_shouldAddHistory = true;
|
||||
}
|
||||
|
||||
_state = state.copyWith(
|
||||
activeMenu: result.last.path, expandMenus: expandMenus);
|
||||
}
|
||||
}
|
||||
|
||||
bool _shouldAddHistory = true;
|
||||
void selectHistory(String path) {
|
||||
_shouldAddHistory = false;
|
||||
goRouter.go(path);
|
||||
}
|
||||
|
||||
void closeHistory(MenuHistory history) {
|
||||
int index = _history.indexOf(history);
|
||||
if(_history.length==1){
|
||||
return;
|
||||
}
|
||||
if(state.activeMenu!=history.menuPath){
|
||||
_history.remove(history);
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
MenuHistory nextActiveHistory;
|
||||
if(index==_history.length-1){
|
||||
///
|
||||
nextActiveHistory = _history[_history.length-2];
|
||||
}else{
|
||||
nextActiveHistory = _history[index+1];
|
||||
}
|
||||
_history.remove(history);
|
||||
selectHistory(nextActiveHistory.menuPath);
|
||||
}
|
||||
|
||||
List<MenuNode> findNodes(
|
||||
MenuNode node,
|
||||
Uri uri,
|
||||
int deep,
|
||||
String prefix,
|
||||
List<MenuNode> 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<MenuNode> nodes =
|
||||
node.children.where((e) => e.path == target).toList();
|
||||
bool match = nodes.isNotEmpty;
|
||||
if (match) {
|
||||
MenuNode matched = nodes.first;
|
||||
result.add(matched);
|
||||
String nextPrefix = '${matched.path}/';
|
||||
findNodes(matched, uri, ++deep, nextPrefix, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MenuNode? queryMenuNodeByPath(MenuNode node, String path) {
|
||||
if (node.path == path) {
|
||||
return node;
|
||||
}
|
||||
if (node.children.isNotEmpty) {
|
||||
for (int i = 0; i < node.children.length; i++) {
|
||||
MenuNode? result = queryMenuNodeByPath(node.children[i], path);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void select(MenuNode menu) {
|
||||
if (menu.isLeaf) {
|
||||
_state = state.copyWith(activeMenu: menu.path);
|
||||
goRouter.go(menu.path);
|
||||
_history.add(MenuHistory(
|
||||
menuLabel: menu.label,
|
||||
menuPath: menu.path,
|
||||
));
|
||||
// notifyListeners();
|
||||
} else {
|
||||
List<String> menus = [];
|
||||
String path = menu.path.substring(1);
|
||||
List<String> parts = path.split('/');
|
||||
|
||||
if (parts.isNotEmpty) {
|
||||
String path = '';
|
||||
for (String part in parts) {
|
||||
path += '/$part';
|
||||
menus.add(path);
|
||||
}
|
||||
}
|
||||
if (state.expandMenus.contains(menu.path)) {
|
||||
menus.remove(menu.path);
|
||||
}
|
||||
|
||||
_state = state.copyWith(expandMenus: menus);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void _onRouterChange() {
|
||||
String path =
|
||||
goRouter.routerDelegate.currentConfiguration.last.matchedLocation;
|
||||
if (path != state.activeMenu) {
|
||||
selectMenuPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:toly_menu/toly_menu.dart';
|
||||
|
||||
import 'anima.dart';
|
||||
import 'dashboard.dart';
|
||||
import 'draw.dart';
|
||||
import 'dream.dart';
|
||||
import 'layout.dart';
|
||||
@@ -23,40 +25,13 @@ Map<String, dynamic> root = {
|
||||
]
|
||||
};
|
||||
|
||||
Map<String, dynamic> dashboard = {
|
||||
'path': '/dashboard',
|
||||
'label': '面板总览',
|
||||
'children': [
|
||||
{
|
||||
'path': '/view',
|
||||
'label': '小册全集',
|
||||
},
|
||||
{
|
||||
'path': '/chat',
|
||||
'label': '读者交流',
|
||||
'children': [
|
||||
{
|
||||
'path': '/a',
|
||||
'label': '第一交流区',
|
||||
},
|
||||
{
|
||||
'path': '/b',
|
||||
'label': '第二交流区',
|
||||
},
|
||||
{
|
||||
'path': '/c',
|
||||
'label': '第三交流区',
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
MenuNode get rootMenu => parser(root, -1, '');
|
||||
|
||||
MenuNode parser(Map<String, dynamic> data, int deep, String prefix) {
|
||||
String path = data['path'];
|
||||
String label = data['label'];
|
||||
IconData? icon = data['icon'];
|
||||
List<Map<String, dynamic>>? childrenMap = data['children'];
|
||||
List<MenuNode> children = [];
|
||||
if (childrenMap != null && childrenMap.isNotEmpty) {
|
||||
@@ -66,6 +41,7 @@ MenuNode parser(Map<String, dynamic> data, int deep, String prefix) {
|
||||
}
|
||||
}
|
||||
return MenuNode(
|
||||
icon: icon,
|
||||
path: prefix + path,
|
||||
label: label,
|
||||
deep: deep,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> renderMenus = {
|
||||
'path' : '/render',
|
||||
'label' : '渲染-聚沙成塔',
|
||||
'icon': FxIcon.icon_sun,
|
||||
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> scrollMenus = {
|
||||
'path' : '/scroll',
|
||||
'label' : '滑动-珠联璧合',
|
||||
'icon': FxIcon.icon_scroll,
|
||||
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import '../../../app/res/fx_icon.dart';
|
||||
|
||||
Map<String,dynamic> touchMenus = {
|
||||
'path' : '/touch',
|
||||
'label' : '手势-执掌天下',
|
||||
'icon': FxIcon.fill_shoushi,
|
||||
'children': [
|
||||
{
|
||||
'path' : '/chapter1',
|
||||
|
||||
36
lib/navigation/router/routers/anima.dart
Normal file
36
lib/navigation/router/routers/anima.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase animaRouters = GoRoute(
|
||||
path: '/anima/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
@@ -1,37 +1,39 @@
|
||||
import 'package:components/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:iroute/v12/pages/empty/empty_panel.dart';
|
||||
import 'package:iroute/navigation/router/routers/anima.dart';
|
||||
import 'package:iroute/navigation/router/routers/dream.dart';
|
||||
import 'package:iroute/navigation/router/routers/render.dart';
|
||||
import 'package:iroute/navigation/router/routers/scroll.dart';
|
||||
import 'package:iroute/navigation/router/routers/touch.dart';
|
||||
|
||||
import '../../../navigation/app_navigation.dart';
|
||||
import '../../views/app_navigation.dart';
|
||||
import '../../../pages/empty/empty_panel.dart';
|
||||
import 'dashboard.dart';
|
||||
import 'draw.dart';
|
||||
import 'layout.dart';
|
||||
|
||||
|
||||
final RouteBase appRoute = ShellRoute(
|
||||
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||
return BookAppNavigation(content: child);
|
||||
return TolyBookNavigation(content: child);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
dashboardRouters,
|
||||
drawRouters,
|
||||
// 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();
|
||||
// },
|
||||
// ),
|
||||
touchRouters,
|
||||
dreamRouters,
|
||||
scrollRouters,
|
||||
renderRouters,
|
||||
layoutRouters,
|
||||
animaRouters,
|
||||
GoRoute(
|
||||
path: '/code',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? path = state.uri.queryParameters['path'];
|
||||
return CodeView(path: path??'',);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/404',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
|
||||
@@ -1,82 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/dashboard/chat_room.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase drawRouters = GoRoute(
|
||||
path: '/draw',
|
||||
redirect: (_, state) {
|
||||
if (state.fullPath == '/draw') {
|
||||
return '/draw/chapter1';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: 'chapter1',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
path: '/draw/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter2',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '2':
|
||||
return const P02Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter3',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '3':
|
||||
return const P03Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter4',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '4':
|
||||
return const P04Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter5',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '5':
|
||||
return const P05Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter6',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '6':
|
||||
return const P06Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter7',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '7':
|
||||
return const P07Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter8',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '8':
|
||||
return const P08Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter9',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '9':
|
||||
return const P09Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter10',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '10':
|
||||
return const P10Page();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'chapter11',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
case '11':
|
||||
return const P11Page();
|
||||
},
|
||||
),
|
||||
],
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
|
||||
36
lib/navigation/router/routers/dream.dart
Normal file
36
lib/navigation/router/routers/dream.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase dreamRouters = GoRoute(
|
||||
path: '/dream/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
36
lib/navigation/router/routers/layout.dart
Normal file
36
lib/navigation/router/routers/layout.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase layoutRouters = GoRoute(
|
||||
path: '/layout/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
36
lib/navigation/router/routers/render.dart
Normal file
36
lib/navigation/router/routers/render.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase renderRouters = GoRoute(
|
||||
path: '/render/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
36
lib/navigation/router/routers/scroll.dart
Normal file
36
lib/navigation/router/routers/scroll.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase scrollRouters = GoRoute(
|
||||
path: '/scroll/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
36
lib/navigation/router/routers/touch.dart
Normal file
36
lib/navigation/router/routers/touch.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:idraw/idraw.dart';
|
||||
import 'package:iroute/pages/empty/empty_panel.dart';
|
||||
|
||||
final RouteBase touchRouters = GoRoute(
|
||||
path: '/touch/chapter:index',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
String? index = state.pathParameters['index'];
|
||||
switch(index){
|
||||
case '1':
|
||||
return const P01Page();
|
||||
case '2':
|
||||
return const P01Page();
|
||||
case '3':
|
||||
return const P03Page();
|
||||
case '4':
|
||||
return const P04Page();
|
||||
case '5':
|
||||
return const P05Page();
|
||||
case '6':
|
||||
return const P06Page();
|
||||
case '7':
|
||||
return const P07Page();
|
||||
case '8':
|
||||
return const P08Page();
|
||||
case '9':
|
||||
return const P09Page();
|
||||
case '10':
|
||||
return const P10Page();
|
||||
case '11':
|
||||
return const P11Page();
|
||||
}
|
||||
return const EmptyPanel(msg: '暂未实现');
|
||||
},
|
||||
);
|
||||
43
lib/navigation/transition/fade_page_transitions_builder.dart
Normal file
43
lib/navigation/transition/fade_page_transitions_builder.dart
Normal file
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
53
lib/navigation/views/app_navigation.dart
Normal file
53
lib/navigation/views/app_navigation.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:components/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:toly_menu/src/menu.dart';
|
||||
import 'package:toly_menu/toly_menu.dart';
|
||||
import '../router/menus/menu_scope/menu_scope.dart';
|
||||
import 'menu_record.dart';
|
||||
import 'top_logo.dart';
|
||||
import 'top_bar.dart';
|
||||
|
||||
class TolyBookNavigation extends StatelessWidget {
|
||||
final Widget content;
|
||||
const TolyBookNavigation({super.key, required this.content});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
DragToMoveWrap(
|
||||
child: Container(
|
||||
color: const Color(0xff001529),
|
||||
width: 210,
|
||||
child: Column(
|
||||
children: [
|
||||
TopLogo(),
|
||||
Expanded(child: MenuTreeView()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
ColoredBox(
|
||||
color: const Color(0xffF2F2F2),
|
||||
child: AppTopBar()),
|
||||
MenuRecord() ,
|
||||
Expanded(child: content)],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MenuTreeView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MenuStore store = MenuScope.of(context);
|
||||
return TolyMenu(state: store.state, onSelect: store.select);
|
||||
}
|
||||
}
|
||||
131
lib/navigation/views/menu_record.dart
Normal file
131
lib/navigation/views/menu_record.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/gestures/events.dart';
|
||||
import 'package:iroute/navigation/router/menus/menu_scope/menu_history.dart';
|
||||
import 'package:iroute/navigation/router/menus/menu_scope/menu_scope.dart';
|
||||
|
||||
class MenuRecord extends StatelessWidget {
|
||||
const MenuRecord({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
MenuStore store = MenuScope.of(context);
|
||||
List<MenuHistory> history = MenuScope.of(context).history;
|
||||
const BorderSide side = BorderSide(color: Color(0xffE8E8E8), width: 1);
|
||||
Color themeColor = Theme.of(context).primaryColor;
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xffF2F2F2),
|
||||
border: Border(top: side, bottom: side)),
|
||||
height: 28,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: history
|
||||
.map((e) => RecordTab(
|
||||
canClose: history.length > 1,
|
||||
onCloseHistory: store.closeHistory,
|
||||
onTapHistory: store.selectHistory,
|
||||
active: store.state.activeMenu == e.menuPath,
|
||||
history: e,
|
||||
))
|
||||
.toList()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RecordTab extends StatelessWidget {
|
||||
final bool active;
|
||||
final bool canClose;
|
||||
final MenuHistory history;
|
||||
final ValueChanged<MenuHistory> onCloseHistory;
|
||||
final ValueChanged<String> onTapHistory;
|
||||
const RecordTab({
|
||||
super.key,
|
||||
this.active = false,
|
||||
required this.canClose,
|
||||
required this.history,
|
||||
required this.onCloseHistory,
|
||||
required this.onTapHistory,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: ()=>onTapHistory(history.menuPath),
|
||||
child: ColoredBox(
|
||||
color: active ? Colors.white : Colors.transparent,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
history.menuLabel,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
if(canClose)
|
||||
CloseHoverButton(
|
||||
onPressed: ()=>onCloseHistory(history)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CloseHoverButton extends StatefulWidget {
|
||||
final VoidCallback onPressed;
|
||||
const CloseHoverButton({super.key, required this.onPressed});
|
||||
|
||||
@override
|
||||
State<CloseHoverButton> createState() => _CloseHoverButtonState();
|
||||
}
|
||||
|
||||
class _CloseHoverButtonState extends State<CloseHoverButton> {
|
||||
bool _isHover = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: widget.onPressed,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onExit: _onExit,
|
||||
onEnter: _onEnter,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(3),
|
||||
decoration: BoxDecoration(
|
||||
color: _isHover ? Color(0xffBFC5C8) : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
child: Icon(
|
||||
CupertinoIcons.clear_thick,
|
||||
size: 10,
|
||||
color: _isHover ? Colors.white : Colors.grey,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onExit(PointerExitEvent event) {
|
||||
setState(() {
|
||||
_isHover = false;
|
||||
});
|
||||
}
|
||||
|
||||
void _onEnter(PointerEnterEvent event) {
|
||||
setState(() {
|
||||
_isHover = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,162 @@
|
||||
import 'package:components/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../components/components.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:iroute/navigation/router/menus/menu_scope/menu_scope.dart';
|
||||
|
||||
class AppTopBar extends StatelessWidget {
|
||||
const AppTopBar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// String? lable = MenuScope.of(context).currentNode?.label;
|
||||
return DragToMoveWrap(
|
||||
child: const Row(
|
||||
children: [
|
||||
SizedBox(width: 20,),
|
||||
// Text(
|
||||
// '404 界面丢失',
|
||||
// style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
Spacer(),
|
||||
WindowButtons()
|
||||
],
|
||||
child: Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [Row(
|
||||
children: [
|
||||
SizedBox(width: 20,),
|
||||
RouteBackIndicator(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: RouterIndicator(),
|
||||
),
|
||||
// Text(
|
||||
// '$lable',
|
||||
// style: TextStyle(fontSize: 14),
|
||||
// ),
|
||||
Spacer(),
|
||||
],
|
||||
),WindowButtons()]
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
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: 20,
|
||||
height: 20,
|
||||
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(() {
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
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(
|
||||
fontSize: 12,
|
||||
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;
|
||||
if(to=='/code'){
|
||||
label = '代码详情';
|
||||
}else{
|
||||
label = MenuScope.read(context).pathName(to);
|
||||
}
|
||||
|
||||
if (label!=null&&label.isNotEmpty) {
|
||||
result
|
||||
.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user