This commit is contained in:
toly
2023-12-16 12:40:32 +08:00
parent ab2778a22b
commit 01fdf966c5
593 changed files with 8995 additions and 27753 deletions

View File

@@ -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(() {});
}
}

View File

@@ -1,6 +1,9 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> animaMenus = {
'path' : '/anima',
'label' : '动画-流光幻影',
'icon': FxIcon.icon_anima,
'children': [
{
'path' : '/chapter1',

View 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': '第三交流区',
},
]
},
],
};

View File

@@ -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': [
{

View File

@@ -1,6 +1,9 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> dreamMenus = {
'path' : '/dream',
'label' : '基础-梦始之地',
'icon': FxIcon.icon_dream,
'children': [
{
'path' : '/chapter1',

View File

@@ -1,6 +1,10 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> layoutMenus = {
'path' : '/layout',
'label' : '布局-薪火相传',
'icon': FxIcon.icon_layout,
'children': [
{
'path' : '/chapter1',

View File

@@ -0,0 +1,9 @@
class MenuHistory {
final String menuLabel;
final String menuPath;
MenuHistory({
required this.menuLabel,
required this.menuPath,
});
}

View 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);
}
}
}

View File

@@ -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,

View File

@@ -1,6 +1,10 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> renderMenus = {
'path' : '/render',
'label' : '渲染-聚沙成塔',
'icon': FxIcon.icon_sun,
'children': [
{
'path' : '/chapter1',

View File

@@ -1,6 +1,10 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> scrollMenus = {
'path' : '/scroll',
'label' : '滑动-珠联璧合',
'icon': FxIcon.icon_scroll,
'children': [
{
'path' : '/chapter1',

View File

@@ -1,6 +1,9 @@
import '../../../app/res/fx_icon.dart';
Map<String,dynamic> touchMenus = {
'path' : '/touch',
'label' : '手势-执掌天下',
'icon': FxIcon.fill_shoushi,
'children': [
{
'path' : '/chapter1',

View 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: '暂未实现');
},
);

View File

@@ -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) {

View File

@@ -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: '暂未实现');
},
);

View 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: '暂未实现');
},
);

View 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: '暂未实现');
},
);

View 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: '暂未实现');
},
);

View 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: '暂未实现');
},
);

View 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: '暂未实现');
},
);

View 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,
);
}
}

View 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);
}
}

View 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;
});
}
}

View File

@@ -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;
}
}