layout.dart 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import 'dart:math' as math;
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/rendering.dart';
  4. import './popover.dart';
  5. class PopoverLayoutDelegate extends SingleChildLayoutDelegate {
  6. PopoverLink link;
  7. PopoverDirection direction;
  8. final Offset offset;
  9. PopoverLayoutDelegate({
  10. required this.link,
  11. required this.direction,
  12. required this.offset,
  13. });
  14. @override
  15. bool shouldRelayout(PopoverLayoutDelegate oldDelegate) {
  16. if (direction != oldDelegate.direction) {
  17. return true;
  18. }
  19. if (link != oldDelegate.link) {
  20. return true;
  21. }
  22. if (link.leaderOffset != oldDelegate.link.leaderOffset) {
  23. return true;
  24. }
  25. if (link.leaderSize != oldDelegate.link.leaderSize) {
  26. return true;
  27. }
  28. return false;
  29. }
  30. @override
  31. BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
  32. return constraints.loosen();
  33. // assert(link.leaderSize != null);
  34. // // if (link.leaderSize == null) {
  35. // // return constraints.loosen();
  36. // // }
  37. // final anchorRect = Rect.fromLTWH(
  38. // link.leaderOffset!.dx,
  39. // link.leaderOffset!.dy,
  40. // link.leaderSize!.width,
  41. // link.leaderSize!.height,
  42. // );
  43. // BoxConstraints childConstraints;
  44. // switch (direction) {
  45. // case PopoverDirection.topLeft:
  46. // childConstraints = BoxConstraints.loose(Size(
  47. // anchorRect.left,
  48. // anchorRect.top,
  49. // ));
  50. // break;
  51. // case PopoverDirection.topRight:
  52. // childConstraints = BoxConstraints.loose(Size(
  53. // constraints.maxWidth - anchorRect.right,
  54. // anchorRect.top,
  55. // ));
  56. // break;
  57. // case PopoverDirection.bottomLeft:
  58. // childConstraints = BoxConstraints.loose(Size(
  59. // anchorRect.left,
  60. // constraints.maxHeight - anchorRect.bottom,
  61. // ));
  62. // break;
  63. // case PopoverDirection.bottomRight:
  64. // childConstraints = BoxConstraints.loose(Size(
  65. // constraints.maxWidth - anchorRect.right,
  66. // constraints.maxHeight - anchorRect.bottom,
  67. // ));
  68. // break;
  69. // case PopoverDirection.center:
  70. // childConstraints = BoxConstraints.loose(Size(
  71. // constraints.maxWidth,
  72. // constraints.maxHeight,
  73. // ));
  74. // break;
  75. // case PopoverDirection.topWithLeftAligned:
  76. // childConstraints = BoxConstraints.loose(Size(
  77. // constraints.maxWidth - anchorRect.left,
  78. // anchorRect.top,
  79. // ));
  80. // break;
  81. // case PopoverDirection.topWithCenterAligned:
  82. // childConstraints = BoxConstraints.loose(Size(
  83. // constraints.maxWidth,
  84. // anchorRect.top,
  85. // ));
  86. // break;
  87. // case PopoverDirection.topWithRightAligned:
  88. // childConstraints = BoxConstraints.loose(Size(
  89. // anchorRect.right,
  90. // anchorRect.top,
  91. // ));
  92. // break;
  93. // case PopoverDirection.rightWithTopAligned:
  94. // childConstraints = BoxConstraints.loose(Size(
  95. // constraints.maxWidth - anchorRect.right,
  96. // constraints.maxHeight - anchorRect.top,
  97. // ));
  98. // break;
  99. // case PopoverDirection.rightWithCenterAligned:
  100. // childConstraints = BoxConstraints.loose(Size(
  101. // constraints.maxWidth - anchorRect.right,
  102. // constraints.maxHeight,
  103. // ));
  104. // break;
  105. // case PopoverDirection.rightWithBottomAligned:
  106. // childConstraints = BoxConstraints.loose(Size(
  107. // constraints.maxWidth - anchorRect.right,
  108. // anchorRect.bottom,
  109. // ));
  110. // break;
  111. // case PopoverDirection.bottomWithLeftAligned:
  112. // childConstraints = BoxConstraints.loose(Size(
  113. // anchorRect.left,
  114. // constraints.maxHeight - anchorRect.bottom,
  115. // ));
  116. // break;
  117. // case PopoverDirection.bottomWithCenterAligned:
  118. // childConstraints = BoxConstraints.loose(Size(
  119. // constraints.maxWidth,
  120. // constraints.maxHeight - anchorRect.bottom,
  121. // ));
  122. // break;
  123. // case PopoverDirection.bottomWithRightAligned:
  124. // childConstraints = BoxConstraints.loose(Size(
  125. // anchorRect.right,
  126. // constraints.maxHeight - anchorRect.bottom,
  127. // ));
  128. // break;
  129. // case PopoverDirection.leftWithTopAligned:
  130. // childConstraints = BoxConstraints.loose(Size(
  131. // anchorRect.left,
  132. // constraints.maxHeight - anchorRect.top,
  133. // ));
  134. // break;
  135. // case PopoverDirection.leftWithCenterAligned:
  136. // childConstraints = BoxConstraints.loose(Size(
  137. // anchorRect.left,
  138. // constraints.maxHeight,
  139. // ));
  140. // break;
  141. // case PopoverDirection.leftWithBottomAligned:
  142. // childConstraints = BoxConstraints.loose(Size(
  143. // anchorRect.left,
  144. // anchorRect.bottom,
  145. // ));
  146. // break;
  147. // case PopoverDirection.custom:
  148. // childConstraints = constraints.loosen();
  149. // break;
  150. // default:
  151. // throw UnimplementedError();
  152. // }
  153. // return childConstraints;
  154. }
  155. @override
  156. Offset getPositionForChild(Size size, Size childSize) {
  157. if (link.leaderSize == null) {
  158. return Offset.zero;
  159. }
  160. final anchorRect = Rect.fromLTWH(
  161. link.leaderOffset!.dx + offset.dx,
  162. link.leaderOffset!.dy + offset.dy,
  163. link.leaderSize!.width,
  164. link.leaderSize!.height,
  165. );
  166. Offset position;
  167. switch (direction) {
  168. case PopoverDirection.topLeft:
  169. position = Offset(
  170. anchorRect.left - childSize.width,
  171. anchorRect.top - childSize.height,
  172. );
  173. break;
  174. case PopoverDirection.topRight:
  175. position = Offset(
  176. anchorRect.right,
  177. anchorRect.top - childSize.height,
  178. );
  179. break;
  180. case PopoverDirection.bottomLeft:
  181. position = Offset(
  182. anchorRect.left - childSize.width,
  183. anchorRect.bottom,
  184. );
  185. break;
  186. case PopoverDirection.bottomRight:
  187. position = Offset(
  188. anchorRect.right,
  189. anchorRect.bottom,
  190. );
  191. break;
  192. case PopoverDirection.center:
  193. position = anchorRect.center;
  194. break;
  195. case PopoverDirection.topWithLeftAligned:
  196. position = Offset(
  197. anchorRect.left,
  198. anchorRect.top - childSize.height,
  199. );
  200. break;
  201. case PopoverDirection.topWithCenterAligned:
  202. position = Offset(
  203. anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
  204. anchorRect.top - childSize.height,
  205. );
  206. break;
  207. case PopoverDirection.topWithRightAligned:
  208. position = Offset(
  209. anchorRect.right - childSize.width,
  210. anchorRect.top - childSize.height,
  211. );
  212. break;
  213. case PopoverDirection.rightWithTopAligned:
  214. position = Offset(anchorRect.right, anchorRect.top);
  215. break;
  216. case PopoverDirection.rightWithCenterAligned:
  217. position = Offset(
  218. anchorRect.right,
  219. anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
  220. );
  221. break;
  222. case PopoverDirection.rightWithBottomAligned:
  223. position = Offset(
  224. anchorRect.right,
  225. anchorRect.bottom - childSize.height,
  226. );
  227. break;
  228. case PopoverDirection.bottomWithLeftAligned:
  229. position = Offset(
  230. anchorRect.left,
  231. anchorRect.bottom,
  232. );
  233. break;
  234. case PopoverDirection.bottomWithCenterAligned:
  235. position = Offset(
  236. anchorRect.left + anchorRect.width / 2.0 - childSize.width / 2.0,
  237. anchorRect.bottom,
  238. );
  239. break;
  240. case PopoverDirection.bottomWithRightAligned:
  241. position = Offset(
  242. anchorRect.right - childSize.width,
  243. anchorRect.bottom,
  244. );
  245. break;
  246. case PopoverDirection.leftWithTopAligned:
  247. position = Offset(
  248. anchorRect.left - childSize.width,
  249. anchorRect.top,
  250. );
  251. break;
  252. case PopoverDirection.leftWithCenterAligned:
  253. position = Offset(
  254. anchorRect.left - childSize.width,
  255. anchorRect.top + anchorRect.height / 2.0 - childSize.height / 2.0,
  256. );
  257. break;
  258. case PopoverDirection.leftWithBottomAligned:
  259. position = Offset(
  260. anchorRect.left - childSize.width,
  261. anchorRect.bottom - childSize.height,
  262. );
  263. break;
  264. default:
  265. throw UnimplementedError();
  266. }
  267. return Offset(
  268. math.max(0.0, math.min(size.width - childSize.width, position.dx)),
  269. math.max(0.0, math.min(size.height - childSize.height, position.dy)),
  270. );
  271. }
  272. }
  273. class PopoverTarget extends SingleChildRenderObjectWidget {
  274. final PopoverLink link;
  275. const PopoverTarget({
  276. super.key,
  277. super.child,
  278. required this.link,
  279. });
  280. @override
  281. PopoverTargetRenderBox createRenderObject(BuildContext context) {
  282. return PopoverTargetRenderBox(
  283. link: link,
  284. );
  285. }
  286. @override
  287. void updateRenderObject(
  288. BuildContext context, PopoverTargetRenderBox renderObject) {
  289. renderObject.link = link;
  290. }
  291. }
  292. class PopoverTargetRenderBox extends RenderProxyBox {
  293. PopoverLink link;
  294. PopoverTargetRenderBox({required this.link, RenderBox? child}) : super(child);
  295. @override
  296. bool get alwaysNeedsCompositing => true;
  297. @override
  298. void performLayout() {
  299. super.performLayout();
  300. link.leaderSize = size;
  301. }
  302. @override
  303. void paint(PaintingContext context, Offset offset) {
  304. link.leaderOffset = localToGlobal(Offset.zero);
  305. super.paint(context, offset);
  306. }
  307. @override
  308. void detach() {
  309. link.leaderOffset = null;
  310. link.leaderSize = null;
  311. super.detach();
  312. }
  313. @override
  314. void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  315. super.debugFillProperties(properties);
  316. properties.add(DiagnosticsProperty<PopoverLink>('link', link));
  317. }
  318. }
  319. class PopoverLink {
  320. Offset? leaderOffset;
  321. Size? leaderSize;
  322. }