This commit is contained in:
toly
2023-11-04 08:48:16 +08:00
parent 8ef81ddb33
commit 88cd6fb3b4
130 changed files with 2563 additions and 5327 deletions

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import '../router/app_router_delegate.dart';
import 'app_navigation_rail.dart';
import 'app_top_bar/app_top_bar.dart';
class AppNavigation extends StatelessWidget {
const AppNavigation({super.key});
@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: Router(
routerDelegate: router,
backButtonDispatcher: RootBackButtonDispatcher(),
),
),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../router/app_router_delegate.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
void initState() {
super.initState();
router.addListener(_onRouterChange);
}
@override
void dispose() {
router.removeListener(_onRouterChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
return DragToMoveWrap(
child: TolyNavigationRail(
items: deskNavBarMenus,
leading: const Padding(
padding: EdgeInsets.symmetric(vertical: 18.0),
child: FlutterLogo(),
),
tail: Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: Text('V0.0.7',style: TextStyle(color: Colors.white,fontSize: 12),),
),
backgroundColor: const Color(0xff3975c6),
onDestinationSelected: _onDestinationSelected,
selectedIndex: activeIndex,
),
);
}
RegExp _segReg = RegExp(r'/\w+');
int? get activeIndex{
String path = router.path;
RegExpMatch? match = _segReg.firstMatch(path);
if(match==null) return null;
String? target = match.group(0);
int index = deskNavBarMenus.indexWhere((menu) => menu.path==target);
if(index==-1) return null;
return index;
}
void _onDestinationSelected(int index) {
String path = deskNavBarMenus[index].path!;
if(index==1){
router.changePath(path,keepAlive: true);
}else{
router.changePath(path);
}
}
void _onRouterChange() {
setState(() {});
}
}

View File

@@ -0,0 +1,64 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
import '../../router/app_router_delegate.dart';
class AppRouterEditor extends StatefulWidget {
final ValueChanged<String>? onSubmit;
const AppRouterEditor({super.key, this.onSubmit});
@override
State<AppRouterEditor> createState() => _AppRouterEditorState();
}
class _AppRouterEditorState extends State<AppRouterEditor> {
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
_onRouteChange();
router.addListener(_onRouteChange);
}
void _onRouteChange() {
_controller.text=router.path;
}
@override
void dispose() {
_controller.dispose();
router.removeListener(_onRouteChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.centerRight,
children: [
SizedBox(
child: CupertinoTextField(
controller: _controller,
style: TextStyle(fontSize: 14),
padding: EdgeInsets.only(left:12,top: 6,bottom: 6,right: 32),
placeholder: '输入路由地址导航',
onSubmitted: widget.onSubmit,
decoration: BoxDecoration(color: Color(0xffF1F2F3),borderRadius: BorderRadius.circular(6)),
),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: HoverIconButton(
icon: Icons.directions_outlined,
defaultColor: Color(0xff68696B),
onPressed:()=>widget.onSubmit?.call(_controller.text),
size: 20
),
)
],
);
}
}

View File

@@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../../router/app_router_delegate.dart';
import 'app_router_editor.dart';
import 'history_view_icon.dart';
import 'route_history_button.dart';
class AppTopBar extends StatelessWidget {
const AppTopBar({super.key});
@override
Widget build(BuildContext context) {
return DragToMoveWrap(
child: Container(
alignment: Alignment.center,
height: 46,
child: Row(
children: [
const SizedBox(width: 16),
const RouterIndicator(),
Expanded(
child: Row(children: [
const Spacer(),
RouteHistoryButton(),
const SizedBox(width: 12,),
SizedBox(
width: 250,
child: AppRouterEditor(
onSubmit: (path) => router.changePath(path),
)),
const SizedBox(width: 12,),
HistoryViewIcon(),
const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: VerticalDivider(
width: 32,
),
)
])),
const WindowButtons()
],
),
),
);
}
}
class RouterIndicator extends StatefulWidget {
const RouterIndicator({super.key});
@override
State<RouterIndicator> createState() => _RouterIndicatorState();
}
Map<String, String> kRouteLabelMap = {
'/color': '颜色板',
'/color/add': '添加颜色',
'/color/detail': '颜色详情',
'/counter': '计数器',
'/sort': '可视化排序算法',
'/user': '我的',
'/settings': '系统设置',
};
class _RouterIndicatorState extends State<RouterIndicator> {
@override
void initState() {
super.initState();
router.addListener(_onRouterChange);
}
@override
void dispose() {
router.removeListener(_onRouterChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
return TolyBreadcrumb(
items: pathToBreadcrumbItems(router.path),
onTapItem: (item) {
if (item.to != null) {
router.changePath(item.to!);
}
},
);
}
void _onRouterChange() {
setState(() {});
}
List<BreadcrumbItem> pathToBreadcrumbItems(String path) {
Uri uri = Uri.parse(path);
List<BreadcrumbItem> result = [];
String to = '';
String distPath = '';
for (String segment in uri.pathSegments) {
distPath += '/$segment';
}
for (String segment in uri.pathSegments) {
to += '/$segment';
String label = kRouteLabelMap[to] ?? '未知路由';
result.add(BreadcrumbItem(to: to, label: label, active: to == distPath));
}
return result;
}
}

View File

@@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import 'package:iroute/components/components.dart';
import '../../router/app_router_delegate.dart';
import '../../router/iroute.dart';
import '../../router/iroute_config.dart';
import 'app_top_bar.dart';
class HistoryViewIcon extends StatelessWidget{
const HistoryViewIcon({super.key});
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: PopPanel(
offset: const Offset(0, 10),
panel: SizedBox(
height: 350,
child: Column(
children: [
_buildTopBar(),
const Expanded(
child:HistoryPanel(),
),
],
),
),
child: const Icon(
Icons.history,
size: 20,
),
),
);
}
Widget _buildTopBar() {
return Container(
decoration: BoxDecoration(
color: const Color(0xffFAFAFC),
borderRadius: BorderRadius.circular(6),
),
padding:
const EdgeInsets.only(top: 10, left: 12, right: 12, bottom: 8),
child: Row(
children: [
const Text(
'浏览历史',
style: TextStyle(fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(onPressed: router.clearHistory, child: const Text('清空历史'))
],
));
}
}
class HistoryItem extends StatefulWidget {
final IRouteConfig history;
final VoidCallback onPressed;
final VoidCallback onDelete;
const HistoryItem({super.key, required this.history, required this.onPressed, required this.onDelete});
@override
State<HistoryItem> createState() => _HistoryItemState();
}
class _HistoryItemState extends State<HistoryItem> {
@override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: widget.onPressed,
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.history.path),
const SizedBox(
height: 2,
),
Text(kRouteLabelMap[widget.history.path]??'未知路由'),
],
)),
GestureDetector(
onTap: widget.onDelete,
child: const Icon(
Icons.close,
size: 18,
color: Color(0xff8E92A9),
),
),
],
),
),
);
}
}
class HistoryPanel extends StatefulWidget {
const HistoryPanel({super.key});
@override
State<HistoryPanel> createState() => _HistoryPanelState();
}
class _HistoryPanelState extends State<HistoryPanel> {
@override
void initState() {
super.initState();
router.addListener(_onChange);
}
@override
void dispose() {
router.removeListener(_onChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
List<IRouteConfig> histories = router.historyManager.histories;
if(histories.isEmpty){
return const Center(
child: Text(
'暂无浏览历史记录',
style: TextStyle(fontSize: 14, color: Colors.grey),
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
itemExtent: 46,
itemCount: histories.length,
itemBuilder: (_, index) =>
HistoryItem(
onDelete: (){
int fixIndex = histories.length - 1 - index;
router.closeHistory(fixIndex);
},
onPressed: (){
router.changeRoute(histories[index].copyWith(recordHistory: false));
Navigator.of(context).pop();
},
history: histories[index]),
);
}
void _onChange() {
setState(() {});
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:iroute/components/toly_ui/button/hover_icon_button.dart';
import '../../router/app_router_delegate.dart';
class RouteHistoryButton extends StatefulWidget {
const RouteHistoryButton({super.key});
@override
State<RouteHistoryButton> createState() => _RouteHistoryButtonState();
}
class _RouteHistoryButtonState extends State<RouteHistoryButton> {
@override
void initState() {
super.initState();
router.addListener(_onChange);
}
@override
void dispose() {
router.removeListener(_onChange);
super.dispose();
}
@override
Widget build(BuildContext context) {
bool hasHistory = router.historyManager.hasHistory;
bool hasBackHistory = router.historyManager.hasBackHistory;
Color activeColor = const Color(0xff9195AC);
Color inActiveColor = const Color(0xffC7CAD5);
Color historyColor = hasHistory?activeColor:inActiveColor;
Color backHistoryColor = hasBackHistory?activeColor:inActiveColor;
return Wrap(
children: [
HoverIconButton(
size: 20,
hoverColor: historyColor,
defaultColor: historyColor,
icon: CupertinoIcons.arrow_left_circle,
onPressed: hasHistory?router.back:null,
),
const SizedBox(width: 8,),
HoverIconButton(
size: 20,
hoverColor: backHistoryColor,
defaultColor: backHistoryColor,
icon: CupertinoIcons.arrow_right_circle,
onPressed: hasBackHistory?router.revocation:null,
),
],
);
}
void _onChange() {
setState(() {});
}
}