popover.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import 'package:flutter/gestures.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. class PopoverMutex {
  5. PopoverController? controller;
  6. }
  7. class PopoverController {
  8. PopoverState? state;
  9. PopoverMutex? mutex;
  10. PopoverController({this.mutex});
  11. close() {
  12. state?.close();
  13. if (mutex != null && mutex!.controller == this) {
  14. mutex!.controller = null;
  15. }
  16. }
  17. show() {
  18. if (mutex != null) {
  19. debugPrint("show popover");
  20. mutex!.controller?.close();
  21. mutex!.controller = this;
  22. }
  23. state?.showOverlay();
  24. }
  25. }
  26. class Popover extends StatefulWidget {
  27. final Widget child;
  28. final PopoverController? controller;
  29. final Offset? offset;
  30. final Decoration? maskDecoration;
  31. final Alignment targetAnchor;
  32. final Alignment followerAnchor;
  33. final Widget Function(BuildContext context) popupBuilder;
  34. const Popover({
  35. Key? key,
  36. required this.child,
  37. required this.popupBuilder,
  38. this.controller,
  39. this.offset,
  40. this.maskDecoration,
  41. this.targetAnchor = Alignment.topLeft,
  42. this.followerAnchor = Alignment.topLeft,
  43. }) : super(key: key);
  44. @override
  45. State<Popover> createState() => PopoverState();
  46. }
  47. class PopoverState extends State<Popover> {
  48. final LayerLink layerLink = LayerLink();
  49. OverlayEntry? _overlayEntry;
  50. bool hasMask = true;
  51. late TapGestureRecognizer _recognizer;
  52. static PopoverState? _popoverWithMask;
  53. @override
  54. void initState() {
  55. widget.controller?.state = this;
  56. _recognizer = TapGestureRecognizer();
  57. _recognizer.onTapDown = (details) {
  58. debugPrint("ggg tapdown");
  59. };
  60. _recognizer.onTap = (() {
  61. debugPrint("ggg tap");
  62. });
  63. WidgetsBinding.instance.pointerRouter
  64. .addGlobalRoute(_handleGlobalPointerEvent);
  65. super.initState();
  66. }
  67. _handleGlobalPointerEvent(PointerEvent event) {
  68. // debugPrint("mouse down: ${event}");
  69. }
  70. showOverlay() {
  71. debugPrint("show overlay");
  72. close();
  73. if (_popoverWithMask == null) {
  74. _popoverWithMask = this;
  75. } else {
  76. hasMask = false;
  77. }
  78. debugPrint("has mask: $hasMask");
  79. final newEntry = OverlayEntry(builder: (context) {
  80. final children = <Widget>[];
  81. if (hasMask) {
  82. children.add(_PopoverMask(
  83. decoration: widget.maskDecoration,
  84. onTap: () => close(),
  85. onExit: () => close(),
  86. ));
  87. }
  88. children.add(CompositedTransformFollower(
  89. link: layerLink,
  90. showWhenUnlinked: false,
  91. offset: widget.offset ?? Offset.zero,
  92. targetAnchor: widget.targetAnchor,
  93. followerAnchor: widget.followerAnchor,
  94. child: widget.popupBuilder(context),
  95. ));
  96. return Stack(children: children);
  97. });
  98. _overlayEntry = newEntry;
  99. Overlay.of(context)?.insert(newEntry);
  100. }
  101. close() {
  102. if (_overlayEntry != null) {
  103. _overlayEntry!.remove();
  104. _overlayEntry = null;
  105. if (_popoverWithMask == this) {
  106. _popoverWithMask = null;
  107. }
  108. }
  109. }
  110. @override
  111. void deactivate() {
  112. debugPrint("deactivate");
  113. WidgetsBinding.instance.pointerRouter
  114. .removeGlobalRoute(_handleGlobalPointerEvent);
  115. close();
  116. super.deactivate();
  117. }
  118. @override
  119. void dispose() {
  120. _recognizer.dispose();
  121. super.dispose();
  122. }
  123. @override
  124. Widget build(BuildContext context) {
  125. return CompositedTransformTarget(link: layerLink, child: widget.child);
  126. }
  127. }
  128. class _PopoverMask extends StatefulWidget {
  129. final void Function() onTap;
  130. final void Function()? onExit;
  131. final Decoration? decoration;
  132. const _PopoverMask(
  133. {Key? key, required this.onTap, this.onExit, this.decoration})
  134. : super(key: key);
  135. @override
  136. State<StatefulWidget> createState() => _PopoverMaskState();
  137. }
  138. class _PopoverMaskState extends State<_PopoverMask> {
  139. @override
  140. void initState() {
  141. HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
  142. super.initState();
  143. }
  144. bool _handleGlobalKeyEvent(KeyEvent event) {
  145. if (event.logicalKey == LogicalKeyboardKey.escape) {
  146. if (widget.onExit != null) {
  147. widget.onExit!();
  148. }
  149. return true;
  150. }
  151. return false;
  152. }
  153. @override
  154. void deactivate() {
  155. HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent);
  156. super.deactivate();
  157. }
  158. @override
  159. Widget build(BuildContext context) {
  160. return GestureDetector(
  161. onTap: widget.onTap,
  162. child: Container(
  163. // decoration: widget.decoration,
  164. decoration: widget.decoration ??
  165. const BoxDecoration(
  166. color: Color.fromARGB(0, 244, 67, 54),
  167. ),
  168. ),
  169. );
  170. }
  171. }