layout.dart 9.9 KB

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