Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

339 rivejä
9.3 KiB

  1. import 'package:chat/utils/screen.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/foundation.dart';
  4. class GZXDropdownMenuController extends ChangeNotifier {
  5. double dropDownHearderHeight;
  6. int menuIndex = 0;
  7. bool isShow = false;
  8. void show(int index) {
  9. isShow = true;
  10. menuIndex = index;
  11. notifyListeners();
  12. }
  13. void hide() {
  14. isShow = false;
  15. notifyListeners();
  16. }
  17. }
  18. typedef OnItemTap<T> = void Function(T value);
  19. class GZXDropDownHeader extends StatefulWidget {
  20. final Color color;
  21. final double borderWidth;
  22. final Color borderColor;
  23. final TextStyle style;
  24. final TextStyle dropDownStyle;
  25. final double iconSize;
  26. final Color iconColor;
  27. final Color iconDropDownColor;
  28. // final List<String> menuStrings;
  29. final double height;
  30. final double dividerHeight;
  31. final Color dividerColor;
  32. final GZXDropdownMenuController controller;
  33. final OnItemTap onItemTap;
  34. final List<GZXDropDownHeaderItem> items;
  35. final GlobalKey stackKey;
  36. GZXDropDownHeader({
  37. Key key,
  38. @required this.items,
  39. @required this.controller,
  40. @required this.stackKey,
  41. this.style = const TextStyle(color: Color(0xFF666666), fontSize: 13),
  42. this.dropDownStyle,
  43. this.height = 40,
  44. this.iconColor = const Color(0xFFafada7),
  45. this.iconDropDownColor,
  46. this.iconSize = 20,
  47. this.borderWidth = 0,
  48. this.borderColor = const Color(0xFFeeede6),
  49. this.dividerHeight = 20,
  50. this.dividerColor = const Color(0xFFeeede6),
  51. this.onItemTap,
  52. this.color = Colors.white,
  53. }) : super(key: key);
  54. @override
  55. _GZXDropDownHeaderState createState() => _GZXDropDownHeaderState();
  56. }
  57. class _GZXDropDownHeaderState extends State<GZXDropDownHeader>
  58. with SingleTickerProviderStateMixin {
  59. bool _isShowDropDownItemWidget = false;
  60. int _menuCount;
  61. GlobalKey _keyDropDownHearder = GlobalKey();
  62. @override
  63. void initState() {
  64. super.initState();
  65. widget.controller.addListener(_onController);
  66. }
  67. _onController() {
  68. // print(widget.controller.menuIndex);
  69. }
  70. @override
  71. Widget build(BuildContext context) {
  72. // print('_GZXDropDownHeaderState.build');
  73. _menuCount = widget.items.length;
  74. var gridView = GridView.count(
  75. physics: new NeverScrollableScrollPhysics(),
  76. crossAxisCount: _menuCount,
  77. childAspectRatio: (Screen.width / _menuCount) / widget.height,
  78. children: widget.items.map<Widget>((item) {
  79. return _menu(item);
  80. }).toList(),
  81. );
  82. return Container(
  83. key: _keyDropDownHearder,
  84. height: widget.height,
  85. margin: EdgeInsets.zero,
  86. padding: EdgeInsets.symmetric(horizontal: 10),
  87. decoration: BoxDecoration(
  88. border:
  89. Border.all(color: widget.borderColor, width: widget.borderWidth),
  90. ),
  91. child: gridView,
  92. );
  93. }
  94. dispose() {
  95. super.dispose();
  96. }
  97. _menu(GZXDropDownHeaderItem item) {
  98. int index = widget.items.indexOf(item);
  99. int menuIndex = widget.controller.menuIndex;
  100. _isShowDropDownItemWidget = index == menuIndex && widget.controller.isShow;
  101. return GestureDetector(
  102. onTap: () {
  103. final RenderBox overlay =
  104. widget.stackKey.currentContext.findRenderObject();
  105. final RenderBox dropDownItemRenderBox =
  106. _keyDropDownHearder.currentContext.findRenderObject();
  107. var position =
  108. dropDownItemRenderBox.localToGlobal(Offset.zero, ancestor: overlay);
  109. // print("POSITION : $position ");
  110. var size = dropDownItemRenderBox.size;
  111. // print("SIZE : $size");
  112. widget.controller.dropDownHearderHeight = size.height + position.dy;
  113. if (widget.controller.isShow) {
  114. widget.controller.hide();
  115. } else {
  116. widget.controller.show(index);
  117. }
  118. if (widget.onItemTap != null) {
  119. widget.onItemTap(index);
  120. }
  121. setState(() {});
  122. },
  123. child: Container(
  124. color: widget.color,
  125. width: Screen.width/3-20,
  126. child: Row(
  127. mainAxisAlignment: MainAxisAlignment.center,
  128. children: <Widget>[
  129. Expanded(
  130. child: Row(
  131. mainAxisAlignment: MainAxisAlignment.center,
  132. children: <Widget>[
  133. Flexible(
  134. child: Text(
  135. item.title, textScaleFactor: 1.0,
  136. maxLines: 1,
  137. overflow: TextOverflow.ellipsis,
  138. style: _isShowDropDownItemWidget
  139. ? widget.dropDownStyle
  140. ??TextStyle(color: Theme.of(context).primaryColor, fontSize: 13)
  141. : widget.style,
  142. )),
  143. Icon(
  144. !_isShowDropDownItemWidget
  145. ? item.iconData ?? Icons.arrow_drop_down
  146. : item.iconData ?? Icons.arrow_drop_up,
  147. color: _isShowDropDownItemWidget
  148. ? widget.iconDropDownColor ?? Theme.of(context).primaryColor
  149. : widget.iconColor,
  150. size: item.iconSize ?? widget.iconSize,
  151. ),
  152. ],
  153. ),
  154. )
  155. ],
  156. )),
  157. );
  158. }
  159. }
  160. class GZXDropDownHeaderItem {
  161. final String title;
  162. final IconData iconData;
  163. final double iconSize;
  164. GZXDropDownHeaderItem(
  165. this.title, {
  166. this.iconData,
  167. this.iconSize,
  168. });
  169. }
  170. class GZXDropdownMenuBuilder {
  171. final Widget dropDownWidget;
  172. final double dropDownHeight;
  173. final callback;
  174. GZXDropdownMenuBuilder(
  175. {@required this.dropDownWidget,
  176. @required this.dropDownHeight,
  177. @required this.callback});
  178. }
  179. class GZXDropDownMenu extends StatefulWidget {
  180. final GZXDropdownMenuController controller;
  181. final List<GZXDropdownMenuBuilder> menus;
  182. final int animationMilliseconds;
  183. const GZXDropDownMenu(
  184. {Key key,
  185. @required this.controller,
  186. @required this.menus,
  187. this.animationMilliseconds = 100})
  188. : super(key: key);
  189. @override
  190. _GZXDropDownMenuState createState() => _GZXDropDownMenuState();
  191. }
  192. class _GZXDropDownMenuState extends State<GZXDropDownMenu>
  193. with SingleTickerProviderStateMixin {
  194. bool _isShowDropDownItemWidget = false;
  195. bool _isShowMask = false;
  196. bool _isControllerDisposed = false;
  197. Animation<double> _animation;
  198. AnimationController _controller;
  199. @override
  200. void initState() {
  201. super.initState();
  202. widget.controller.addListener(_onController);
  203. _controller = new AnimationController(
  204. duration: Duration(milliseconds: widget.animationMilliseconds),
  205. vsync: this);
  206. }
  207. _onController() {
  208. // print('_GZXDropDownMenuState._onController ${widget.controller.menuIndex}');
  209. _showDropDownItemWidget();
  210. }
  211. @override
  212. Widget build(BuildContext context) {
  213. // print('_GZXDropDownMenuState.build');
  214. _controller.duration = Duration(milliseconds: widget.animationMilliseconds);
  215. return _buildDropDownWidget();
  216. }
  217. dispose() {
  218. _controller.dispose();
  219. _isControllerDisposed = true;
  220. super.dispose();
  221. }
  222. _showDropDownItemWidget() {
  223. int menuIndex = widget.controller.menuIndex;
  224. if (menuIndex >= widget.menus.length || widget.menus[menuIndex] == null) {
  225. return;
  226. }
  227. _isShowDropDownItemWidget = !_isShowDropDownItemWidget;
  228. _isShowMask = !_isShowMask;
  229. _animation =
  230. new Tween(begin: 0.0, end: widget.menus[menuIndex].dropDownHeight)
  231. .animate(_controller)
  232. ..addListener(() {
  233. //这行如果不写,没有动画效果
  234. setState(() {});
  235. });
  236. if (_isControllerDisposed) return;
  237. if (_animation.status == AnimationStatus.completed) {
  238. _controller.reverse();
  239. } else {
  240. _controller.forward();
  241. }
  242. }
  243. _hideDropDownItemWidget() {
  244. _isShowDropDownItemWidget = !_isShowDropDownItemWidget;
  245. _isShowMask = !_isShowMask;
  246. _controller.reverse();
  247. widget.controller.isShow = false;
  248. }
  249. Widget _mask(callback) {
  250. if (_isShowMask) {
  251. return GestureDetector(
  252. onTap: () {
  253. print('到mask了');
  254. _hideDropDownItemWidget();
  255. if (callback != null) callback();
  256. },
  257. child: Container(
  258. width: MediaQuery.of(context).size.width,
  259. height: MediaQuery.of(context).size.height * 2,
  260. color: Color.fromRGBO(0, 0, 0, 0.1),
  261. ),
  262. );
  263. } else {
  264. return Container(
  265. height: 0,
  266. );
  267. }
  268. }
  269. Widget _buildDropDownWidget() {
  270. int menuIndex = widget.controller.menuIndex;
  271. return Positioned(
  272. width: MediaQuery.of(context).size.width,
  273. top: widget.controller.dropDownHearderHeight,
  274. left: 0,
  275. child: Column(
  276. children: <Widget>[
  277. Container(
  278. color: Colors.white,
  279. width: MediaQuery.of(context).size.width,
  280. height: _animation == null ? 0 : _animation.value,
  281. child: widget.menus[menuIndex].dropDownWidget,
  282. ),
  283. _mask(widget.menus[menuIndex].callback)
  284. ],
  285. ));
  286. }
  287. }