204 lines
4.5 KiB
Dart
Raw Normal View History

2022-08-30 12:51:59 +08:00
import 'package:flutter/gestures.dart';
2022-08-29 14:00:27 +08:00
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
2022-08-29 13:56:16 +08:00
2022-08-30 14:02:06 +08:00
class PopoverMutex {
2022-08-30 12:51:59 +08:00
PopoverController? controller;
2022-08-29 15:29:39 +08:00
}
2022-08-30 12:51:59 +08:00
class PopoverController {
PopoverState? state;
2022-08-30 14:02:06 +08:00
PopoverMutex? mutex;
2022-08-29 15:29:39 +08:00
2022-08-30 14:02:06 +08:00
PopoverController({this.mutex});
2022-08-29 14:00:27 +08:00
close() {
state?.close();
2022-08-30 14:02:06 +08:00
if (mutex != null && mutex!.controller == this) {
mutex!.controller = null;
2022-08-29 15:29:39 +08:00
}
2022-08-29 14:00:27 +08:00
}
show() {
2022-08-30 14:02:06 +08:00
if (mutex != null) {
2022-08-29 15:29:39 +08:00
debugPrint("show popover");
2022-08-30 14:02:06 +08:00
mutex!.controller?.close();
mutex!.controller = this;
2022-08-29 15:29:39 +08:00
}
2022-08-29 14:00:27 +08:00
state?.showOverlay();
}
}
2022-08-30 12:51:59 +08:00
class Popover extends StatefulWidget {
2022-08-29 14:00:27 +08:00
final Widget child;
2022-08-30 12:51:59 +08:00
final PopoverController? controller;
2022-08-29 14:00:27 +08:00
final Offset? offset;
final Decoration? maskDecoration;
final Alignment targetAnchor;
final Alignment followerAnchor;
final Widget Function(BuildContext context) popupBuilder;
2022-08-30 12:51:59 +08:00
const Popover({
2022-08-29 14:00:27 +08:00
Key? key,
required this.child,
required this.popupBuilder,
this.controller,
this.offset,
this.maskDecoration,
this.targetAnchor = Alignment.topLeft,
this.followerAnchor = Alignment.topLeft,
}) : super(key: key);
@override
2022-08-30 12:51:59 +08:00
State<Popover> createState() => PopoverState();
2022-08-29 14:00:27 +08:00
}
2022-08-30 12:51:59 +08:00
class PopoverState extends State<Popover> {
2022-08-29 14:00:27 +08:00
final LayerLink layerLink = LayerLink();
OverlayEntry? _overlayEntry;
bool hasMask = true;
2022-08-30 12:51:59 +08:00
late TapGestureRecognizer _recognizer;
2022-08-29 14:00:27 +08:00
2022-08-30 12:51:59 +08:00
static PopoverState? _popoverWithMask;
2022-08-29 16:07:54 +08:00
2022-08-29 14:00:27 +08:00
@override
void initState() {
widget.controller?.state = this;
2022-08-30 12:51:59 +08:00
_recognizer = TapGestureRecognizer();
_recognizer.onTapDown = (details) {
debugPrint("ggg tapdown");
};
_recognizer.onTap = (() {
debugPrint("ggg tap");
});
2022-08-30 14:02:06 +08:00
WidgetsBinding.instance.pointerRouter
.addGlobalRoute(_handleGlobalPointerEvent);
2022-08-29 14:00:27 +08:00
super.initState();
}
2022-08-30 14:02:06 +08:00
_handleGlobalPointerEvent(PointerEvent event) {
// debugPrint("mouse down: ${event}");
}
2022-08-29 14:00:27 +08:00
showOverlay() {
debugPrint("show overlay");
2022-08-29 16:07:54 +08:00
close();
2022-08-29 14:00:27 +08:00
2022-08-29 16:07:54 +08:00
if (_popoverWithMask == null) {
_popoverWithMask = this;
} else {
2022-08-29 14:00:27 +08:00
hasMask = false;
}
debugPrint("has mask: $hasMask");
final newEntry = OverlayEntry(builder: (context) {
final children = <Widget>[];
if (hasMask) {
children.add(_PopoverMask(
decoration: widget.maskDecoration,
2022-08-29 16:07:54 +08:00
onTap: () => close(),
onExit: () => close(),
2022-08-29 14:00:27 +08:00
));
}
children.add(CompositedTransformFollower(
link: layerLink,
2022-08-29 16:07:54 +08:00
showWhenUnlinked: false,
2022-08-29 14:00:27 +08:00
offset: widget.offset ?? Offset.zero,
targetAnchor: widget.targetAnchor,
followerAnchor: widget.followerAnchor,
child: widget.popupBuilder(context),
));
return Stack(children: children);
});
_overlayEntry = newEntry;
Overlay.of(context)?.insert(newEntry);
}
close() {
2022-08-29 16:07:54 +08:00
if (_overlayEntry != null) {
_overlayEntry!.remove();
_overlayEntry = null;
if (_popoverWithMask == this) {
_popoverWithMask = null;
}
2022-08-29 14:00:27 +08:00
}
}
@override
2022-08-29 16:07:54 +08:00
void deactivate() {
debugPrint("deactivate");
2022-08-30 14:02:06 +08:00
WidgetsBinding.instance.pointerRouter
.removeGlobalRoute(_handleGlobalPointerEvent);
2022-08-29 16:07:54 +08:00
close();
super.deactivate();
2022-08-29 14:00:27 +08:00
}
2022-08-30 12:51:59 +08:00
@override
void dispose() {
_recognizer.dispose();
super.dispose();
}
2022-08-29 14:00:27 +08:00
@override
Widget build(BuildContext context) {
return CompositedTransformTarget(link: layerLink, child: widget.child);
}
}
class _PopoverMask extends StatefulWidget {
final void Function() onTap;
final void Function()? onExit;
final Decoration? decoration;
const _PopoverMask(
{Key? key, required this.onTap, this.onExit, this.decoration})
: super(key: key);
@override
State<StatefulWidget> createState() => _PopoverMaskState();
}
class _PopoverMaskState extends State<_PopoverMask> {
@override
void initState() {
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
super.initState();
}
bool _handleGlobalKeyEvent(KeyEvent event) {
if (event.logicalKey == LogicalKeyboardKey.escape) {
if (widget.onExit != null) {
widget.onExit!();
}
return true;
}
return false;
}
@override
2022-08-29 16:07:54 +08:00
void deactivate() {
2022-08-29 14:00:27 +08:00
HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent);
2022-08-29 16:07:54 +08:00
super.deactivate();
2022-08-29 14:00:27 +08:00
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
child: Container(
// decoration: widget.decoration,
decoration: widget.decoration ??
const BoxDecoration(
color: Color.fromARGB(0, 244, 67, 54),
),
),
);
}
2022-08-29 13:56:16 +08:00
}