v3
This commit is contained in:
2
lib/components/components.dart
Normal file
2
lib/components/components.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'toly_ui/toly_ui.dart';
|
||||
export 'windows/window_buttons.dart';
|
||||
37
lib/components/project/colors_panel.dart
Normal file
37
lib/components/project/colors_panel.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ColorsPanel extends StatelessWidget {
|
||||
final List<Color> colors;
|
||||
final ValueChanged<Color> onSelect;
|
||||
const ColorsPanel({super.key, required this.colors, required this.onSelect});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: colors
|
||||
.asMap()
|
||||
.keys
|
||||
.map((int index) => GestureDetector(
|
||||
onTap: () => onSelect(colors[index]),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: colors[index],
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
child: Text(
|
||||
'$index',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
18
lib/components/toly_ui/navigation/menu_meta.dart
Normal file
18
lib/components/toly_ui/navigation/menu_meta.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class MenuMeta {
|
||||
// 标签
|
||||
final String label;
|
||||
|
||||
// 图标数据
|
||||
final IconData icon;
|
||||
|
||||
// 图标颜色
|
||||
final Color? color;
|
||||
|
||||
const MenuMeta({
|
||||
required this.label,
|
||||
required this.icon,
|
||||
this.color,
|
||||
});
|
||||
}
|
||||
104
lib/components/toly_ui/navigation/toly_breadcrumb.dart
Normal file
104
lib/components/toly_ui/navigation/toly_breadcrumb.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/gestures/events.dart';
|
||||
|
||||
class TolyBreadcrumb extends StatelessWidget {
|
||||
final List<BreadcrumbItem> items;
|
||||
final ValueChanged<BreadcrumbItem>? onTapItem;
|
||||
|
||||
const TolyBreadcrumb({super.key, required this.items, this.onTapItem});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> children = [];
|
||||
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
children.add(TolyBreadcrumbItem(
|
||||
item: items[i], onTapItem: onTapItem,
|
||||
));
|
||||
if (i != items.length - 1) {
|
||||
children.add(Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Text(
|
||||
'/',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Wrap(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TolyBreadcrumbItem extends StatefulWidget {
|
||||
final BreadcrumbItem item;
|
||||
final ValueChanged<BreadcrumbItem>? onTapItem;
|
||||
const TolyBreadcrumbItem({super.key, required this.item, required this.onTapItem});
|
||||
|
||||
@override
|
||||
State<TolyBreadcrumbItem> createState() => _TolyBreadcrumbItemState();
|
||||
}
|
||||
|
||||
class _TolyBreadcrumbItemState extends State<TolyBreadcrumbItem> {
|
||||
|
||||
bool _hover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool hasTarget = (widget.item.to != null);
|
||||
Color? color = (_hover&&hasTarget)?Colors.blue:null;
|
||||
MouseCursor cursor = hasTarget?SystemMouseCursors.click:SystemMouseCursors.basic;
|
||||
|
||||
if(widget.item.active) {
|
||||
color = null;
|
||||
cursor = SystemMouseCursors.basic;
|
||||
}
|
||||
|
||||
TextStyle style = TextStyle(
|
||||
fontWeight: hasTarget ? FontWeight.bold : null,
|
||||
color: color
|
||||
);
|
||||
|
||||
return MouseRegion(
|
||||
cursor: cursor,
|
||||
onEnter: _onEnter,
|
||||
onExit: _onExit,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if(!widget.item.active){
|
||||
widget.onTapItem?.call(widget.item);
|
||||
}
|
||||
},
|
||||
child: Text(widget.item.label, style: style)),
|
||||
);
|
||||
}
|
||||
|
||||
void _onEnter(PointerEnterEvent event) {
|
||||
setState(() {
|
||||
_hover = true;
|
||||
});
|
||||
}
|
||||
|
||||
void _onExit(PointerExitEvent event) {
|
||||
setState(() {
|
||||
_hover = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class BreadcrumbItem {
|
||||
final String? to;
|
||||
final String label;
|
||||
final bool active;
|
||||
|
||||
BreadcrumbItem( {this.to, required this.label,this.active=false,});
|
||||
}
|
||||
|
||||
//<el-breadcrumb separator="/">
|
||||
// <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
||||
// <el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item>
|
||||
// <el-breadcrumb-item>活动列表</el-breadcrumb-item>
|
||||
// <el-breadcrumb-item>活动详情</el-breadcrumb-item>
|
||||
// </el-breadcrumb>
|
||||
163
lib/components/toly_ui/navigation/toly_navigation_rail.dart
Normal file
163
lib/components/toly_ui/navigation/toly_navigation_rail.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'menu_meta.dart';
|
||||
|
||||
// final List<NavigationRailDestination> destinations = const [
|
||||
// NavigationRailDestination(icon: Icon(Icons.color_lens_outlined), label: Text("颜色板")),
|
||||
// NavigationRailDestination(icon: Icon(Icons.add_chart), label: Text("计数器")),
|
||||
// NavigationRailDestination(icon: Icon(Icons.person), label: Text("我的")),
|
||||
// NavigationRailDestination(icon: Icon(Icons.settings), label: Text("设置")),
|
||||
// ];
|
||||
|
||||
const List<MenuMeta> kDeskNavBarMenus = [
|
||||
MenuMeta(label: '颜色板', icon: Icons.color_lens_outlined),
|
||||
MenuMeta(
|
||||
label: '计数器',
|
||||
icon: Icons.add_chart,
|
||||
),
|
||||
MenuMeta(
|
||||
label: '我的',
|
||||
icon: Icons.person,
|
||||
),
|
||||
MenuMeta(label: '设置', icon: Icons.settings),
|
||||
];
|
||||
|
||||
class TolyNavigationRail extends StatelessWidget {
|
||||
final ValueChanged<int> onDestinationSelected;
|
||||
final Color backgroundColor;
|
||||
final int? selectedIndex;
|
||||
final Widget? leading;
|
||||
|
||||
const TolyNavigationRail({
|
||||
Key? key,
|
||||
required this.onDestinationSelected,
|
||||
required this.selectedIndex,
|
||||
this.leading,
|
||||
required this.backgroundColor,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 64,
|
||||
color: backgroundColor,
|
||||
child: Column(
|
||||
children: [
|
||||
if(leading!=null) leading!,
|
||||
Expanded(
|
||||
child: LeftNavigationMenu(
|
||||
items: kDeskNavBarMenus,
|
||||
selectedIndex: selectedIndex,
|
||||
onTapItem: onDestinationSelected,
|
||||
)),
|
||||
// if (selectedIndex == 0) HelpButton()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LeftNavigationMenu extends StatelessWidget {
|
||||
final List<MenuMeta> items;
|
||||
final ValueChanged<int> onTapItem;
|
||||
|
||||
final int? selectedIndex;
|
||||
|
||||
const LeftNavigationMenu(
|
||||
{Key? key,
|
||||
required this.items,
|
||||
required this.selectedIndex,
|
||||
required this.onTapItem})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: items.asMap().keys.map((int index) {
|
||||
return LeftNavigationBarItemWidget(
|
||||
item: items[index],
|
||||
selected: index == selectedIndex,
|
||||
onTap: () {
|
||||
onTapItem.call(index);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LeftNavigationBarItemWidget extends StatefulWidget {
|
||||
final MenuMeta item;
|
||||
final bool selected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const LeftNavigationBarItemWidget(
|
||||
{Key? key,
|
||||
required this.item,
|
||||
required this.selected,
|
||||
required this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<LeftNavigationBarItemWidget> createState() =>
|
||||
_LeftNavigationBarItemWidgetState();
|
||||
}
|
||||
|
||||
class _LeftNavigationBarItemWidgetState
|
||||
extends State<LeftNavigationBarItemWidget> {
|
||||
bool _hover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color? bgColor;
|
||||
Color iconColor;
|
||||
if (widget.selected) {
|
||||
bgColor = Colors.white.withOpacity(0.2);
|
||||
iconColor = Colors.white;
|
||||
} else {
|
||||
bgColor = _hover ? Colors.white.withOpacity(0.1) : null;
|
||||
iconColor = Colors.white.withOpacity(0.8);
|
||||
}
|
||||
|
||||
return InkWell(
|
||||
onTap: widget.selected
|
||||
? null
|
||||
: () {
|
||||
widget.onTap();
|
||||
setState(() {
|
||||
_hover = false;
|
||||
});
|
||||
},
|
||||
onHover: widget.selected
|
||||
? null
|
||||
: (v) {
|
||||
setState(() {
|
||||
_hover = v;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(bottom: 10),
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor, borderRadius: BorderRadius.circular(8)),
|
||||
child: Wrap(
|
||||
direction: Axis.vertical,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 2,
|
||||
children: [
|
||||
Icon(
|
||||
widget.item.icon,
|
||||
color: iconColor,
|
||||
),
|
||||
Text(
|
||||
widget.item.label,
|
||||
style: TextStyle(color: iconColor, fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
4
lib/components/toly_ui/toly_ui.dart
Normal file
4
lib/components/toly_ui/toly_ui.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
export 'navigation/menu_meta.dart';
|
||||
export 'navigation/toly_breadcrumb.dart';
|
||||
export 'navigation/toly_navigation_rail.dart';
|
||||
80
lib/components/windows/window_buttons.dart
Normal file
80
lib/components/windows/window_buttons.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class WindowButtons extends StatefulWidget {
|
||||
final List<Widget>? actions;
|
||||
const WindowButtons({Key? key, this.actions}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<WindowButtons> createState() => _WindowButtonsState();
|
||||
}
|
||||
|
||||
class _WindowButtonsState extends State<WindowButtons> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Brightness brightness = Theme.of(context).brightness;
|
||||
return Align(
|
||||
alignment:Alignment.topRight,child: Wrap(
|
||||
spacing: 5,
|
||||
children: [
|
||||
if(widget.actions!=null)
|
||||
...widget.actions!,
|
||||
SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: WindowCaptionButton.minimize(
|
||||
brightness:brightness,
|
||||
onPressed: () async {
|
||||
bool isMinimized = await windowManager.isMinimized();
|
||||
if (isMinimized) {
|
||||
windowManager.restore();
|
||||
} else {
|
||||
windowManager.minimize();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: FutureBuilder<bool>(
|
||||
future: windowManager.isMaximized(),
|
||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
||||
if (snapshot.data == true) {
|
||||
return WindowCaptionButton.unmaximize(
|
||||
brightness: brightness,
|
||||
onPressed: () async{
|
||||
await windowManager.unmaximize();
|
||||
setState(() {
|
||||
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
return WindowCaptionButton.maximize(
|
||||
brightness: brightness,
|
||||
onPressed: () async{
|
||||
await windowManager.maximize();
|
||||
setState(() {
|
||||
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
width: 30,
|
||||
child: WindowCaptionButton.close(
|
||||
brightness: brightness,
|
||||
onPressed: () {
|
||||
windowManager.close();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user