This commit is contained in:
toly
2023-10-30 16:12:23 +08:00
parent a1a70fae78
commit 8ef81ddb33
64 changed files with 6856 additions and 171 deletions

View File

@@ -2,32 +2,47 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HoverIconButton extends StatefulWidget {
final VoidCallback onPressed;
final IconData icon;
final double size;
final Color? hoverColor;
final Color? defaultColor;
const HoverIconButton({super.key,required this.onPressed ,required this.icon,this.hoverColor,this.size=24,this.defaultColor});
final VoidCallback? onPressed;
final IconData icon;
final double size;
final Color? hoverColor;
final Color? defaultColor;
final MouseCursor cursor;
const HoverIconButton({
super.key,
required this.onPressed,
required this.icon,
this.hoverColor,
this.size = 24,
this.defaultColor,
this.cursor = SystemMouseCursors.cell,
});
@override
State<HoverIconButton> createState() => _HoverIconButtonState();
}
class _HoverIconButtonState extends State<HoverIconButton> {
bool _hover = false;
@override
Widget build(BuildContext context) {
Color? color = (_hover)?widget.hoverColor??Theme.of(context).primaryColor:(widget.defaultColor??null);
Color? color = (_hover)
? widget.hoverColor ?? Theme.of(context).primaryColor
: (widget.defaultColor ?? null);
return MouseRegion(
cursor: SystemMouseCursors.click,
cursor: widget.cursor,
onEnter: _onEnter,
onExit: _onExit,
child: GestureDetector(
onTap: widget.onPressed,
child: Icon(widget.icon,size: widget.size,color: color,)),
child: Icon(
widget.icon,
size: widget.size,
color: color,
)),
);
}
@@ -42,4 +57,4 @@ class _HoverIconButtonState extends State<HoverIconButton> {
_hover = false;
});
}
}
}

View File

@@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'toly_pop_menu.dart';
class PopPanel<T> extends StatefulWidget {
final Widget child;
final Size size;
final Offset offset;
/// Builds the context menu.
final Widget panel;
const PopPanel({
super.key,
required this.child,
required this.panel,
this.offset = Offset.zero,
this.size = const Size(250, 0),
});
@override
State<PopPanel> createState() => _PopPanelState();
}
class _PopPanelState<T> extends State<PopPanel<T>> {
final ContextMenuController _contextMenuController = ContextMenuController();
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _onTap,
child: widget.child,
);
}
void _show() {
TolyPopupMenuEntry<T> item = CustomTolyMenuItem(
widget.panel,
size: widget.size,
);
final RenderBox button = context.findRenderObject()! as RenderBox;
final RenderBox overlay =
Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;
final Offset offset = Offset(button.size.width / 2, button.size.height)+widget.offset;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(offset, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero) + offset,
ancestor: overlay),
),
Offset.zero & overlay.size,
);
showTolyMenu<T?>(
elevation: 0,
color: Colors.transparent,
context: context,
items: [item],
position: position,
).then<void>((T? newValue) {
if (!mounted) {
return null;
}
if (newValue == null) {
// widget.onCanceled?.call();
return null;
}
// widget.onSelected?.call(newValue);
});
}
void _onTap() {
_show();
}
}
class CustomTolyMenuItem<T> extends TolyPopupMenuEntry<T> {
final Size size;
final Widget content;
const CustomTolyMenuItem(this.content, {super.key, required this.size});
@override
State<StatefulWidget> createState() => _CustomTolyMenuItemState();
@override
// TODO: implement height
double get height => kMinInteractiveDimension;
@override
bool represents(value) => true;
}
class _CustomTolyMenuItemState<T> extends State<CustomTolyMenuItem> {
@override
void didUpdateWidget(covariant CustomTolyMenuItem oldWidget) {
print('============_CustomTolyMenuItemState#didUpdateWidget');
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.size.width,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 6)
]),
// padding: EdgeInsets.symmetric(horizontal: 12, vertical: 16),
child: widget.content,
// color: Colors.lightBlueAccent,
);
}
}

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
/// A builder that includes an Offset to draw the context menu at.
typedef ContextMenuBuilder = Widget Function(BuildContext context, Offset offset);
class Popover extends StatefulWidget {
final Widget child;
/// Builds the context menu.
final ContextMenuBuilder contextMenuBuilder;
const Popover({
super.key,
required this.child,
required this.contextMenuBuilder,
});
@override
State<Popover> createState() => _PopoverState();
}
class _PopoverState extends State<Popover> {
final ContextMenuController _contextMenuController = ContextMenuController();
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _onTap,
child: widget.child,
);
}
void _show(Offset position) {
_contextMenuController.show(
context: context,
contextMenuBuilder: (BuildContext context) {
return widget.contextMenuBuilder(context, position);
},
);
}
final double width = 200;
void _onTap() {
final RenderBox button = context.findRenderObject()! as RenderBox;
final RenderBox overlay = Navigator.of(context).overlay!.context.findRenderObject()! as RenderBox;
final Offset offset = Offset(button.size.width / 2, button.size.height);
Offset boxOffset = button.localToGlobal(offset, ancestor: overlay);
_show(boxOffset.translate(-width, 0));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,4 +2,6 @@
export 'navigation/menu_meta.dart';
export 'navigation/toly_breadcrumb.dart';
export 'navigation/toly_navigation_rail.dart';
export 'popable/drop_selectable_widget.dart';
export 'popable/drop_selectable_widget.dart';
export 'popable/popover.dart';
export 'popable/pop_menu.dart';