search.dart 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*
  2. * @Author: XianKaiQun
  3. * @Date: 2020-09-14 09:34:57
  4. * @LastEditors: wjc
  5. * @LastEditTime: 2023-01-11 09:42:06
  6. * @Description:
  7. */
  8. import 'package:flutter/material.dart';
  9. import 'package:wisdom_cli/src/assets.gen.dart';
  10. import 'package:wisdom_cli/wisdom_cli.dart';
  11. ///搜索组件
  12. class WSearch extends StatefulWidget {
  13. WSearch({
  14. Key? key,
  15. this.hintText,
  16. this.controller,
  17. this.onSubmitted,
  18. this.onCancel,
  19. this.onChanged,
  20. this.moonlight = true,
  21. this.isDark = false,
  22. this.padding,
  23. this.backColor,
  24. this.isStatic = false,
  25. this.isControlOutside = false,
  26. this.initValue,
  27. this.onInput,
  28. }) : super(key: key);
  29. final String? hintText;
  30. final TextEditingController? controller;
  31. final void Function(String value)? onSubmitted;
  32. final void Function(String value)? onChanged;
  33. final void Function()? onCancel;
  34. final void Function(bool value)? onInput;
  35. final EdgeInsetsGeometry? padding;
  36. final backColor;
  37. final String? initValue;
  38. final bool? isControlOutside;
  39. /// 搜索文字是否固定显示
  40. final bool isStatic;
  41. ///朦胧层
  42. final bool moonlight;
  43. /// 背景是否深色 默认是白色
  44. final bool isDark;
  45. @override
  46. _WSearchState createState() => _WSearchState();
  47. }
  48. class _WSearchState extends State<WSearch> {
  49. FocusNode? focusNode;
  50. TextEditingController? controller;
  51. GlobalKey globalKey = GlobalKey();
  52. String? value;
  53. Size? size;
  54. Offset? offset;
  55. OverlayEntry? entry;
  56. bool hasEntry = false;
  57. @override
  58. void initState() {
  59. controller = new TextEditingController();
  60. focusNode = new FocusNode();
  61. value = widget.initValue ?? '';
  62. if (widget.isControlOutside! && controller?.value != null) {
  63. controller!.value = TextEditingValue(
  64. text: widget.initValue ?? '',
  65. selection: TextSelection.fromPosition(
  66. TextPosition(
  67. affinity: TextAffinity.downstream, offset: value!.length),
  68. ),
  69. );
  70. }
  71. focusNode!.addListener(onfocus);
  72. super.initState();
  73. }
  74. @override
  75. void didUpdateWidget(WSearch oldWidget) {
  76. super.didUpdateWidget(oldWidget);
  77. if (widget.isControlOutside! && controller?.value != null) {
  78. controller!.value = TextEditingValue(
  79. text: widget.initValue ?? '',
  80. selection: TextSelection.fromPosition(
  81. TextPosition(
  82. affinity: TextAffinity.downstream, offset: value!.length),
  83. ),
  84. );
  85. setState(() {});
  86. }
  87. }
  88. ///获取焦点时,显示朦胧层。
  89. ///通过globalKey获取当前input组件的绝对位置,
  90. ///然后通过[OverlayEntry]显示朦胧层铺满剩余位置,
  91. ///点击朦胧层触发`onCancel`事件
  92. onfocus() {
  93. if (focusNode!.hasFocus && widget.moonlight) {
  94. final RenderBox renderBox =
  95. globalKey.currentContext!.findRenderObject() as RenderBox;
  96. offset = renderBox.localToGlobal(Offset.zero);
  97. size = renderBox.size;
  98. entry = OverlayEntry(
  99. builder: (_) {
  100. return Positioned.fill(
  101. top: offset!.dy + size!.height,
  102. child: GestureDetector(
  103. onTap: () => onCancel(),
  104. child: ColoredBox(
  105. color: Colors.black26,
  106. ),
  107. ),
  108. );
  109. },
  110. );
  111. Overlay.of(context).insert(entry!);
  112. hasEntry = true;
  113. } else {
  114. hasEntry = false;
  115. entry?.remove();
  116. }
  117. setState(() {});
  118. }
  119. @override
  120. void dispose() {
  121. value = '';
  122. focusNode!.dispose();
  123. controller!.dispose();
  124. if (hasEntry) {
  125. entry!.remove();
  126. }
  127. super.dispose();
  128. }
  129. void onCancel() {
  130. setState(() {
  131. value = '';
  132. controller!.clear();
  133. focusNode!.unfocus();
  134. if (widget.onInput != null) {
  135. widget.onInput!(false);
  136. }
  137. if (widget.onCancel != null) {
  138. widget.onCancel!();
  139. }
  140. });
  141. }
  142. void onSubmitted(String v) {
  143. value = v;
  144. controller!.value = TextEditingValue(
  145. text: value!,
  146. selection: TextSelection.fromPosition(
  147. TextPosition(affinity: TextAffinity.downstream, offset: value!.length),
  148. ),
  149. );
  150. setState(() {});
  151. if (widget.onSubmitted != null) {
  152. widget.onSubmitted!(v);
  153. }
  154. if (widget.onInput != null) {
  155. widget.onInput!(false);
  156. }
  157. }
  158. void onChanged(String v) {
  159. value = v;
  160. setState(() {});
  161. if (widget.onChanged != null) {
  162. widget.onChanged!(v);
  163. }
  164. if (widget.onInput != null) {
  165. widget.onInput!(false);
  166. }
  167. }
  168. @override
  169. Widget build(BuildContext context) {
  170. final style = WTheme.of(context);
  171. final colorScheme = style.colorScheme;
  172. final _isStatic = widget.isStatic;
  173. return Wisdom.row(
  174. key: globalKey,
  175. padding: widget.padding ?? EdgeInsets.symmetric(vertical: 12.pt),
  176. color: widget.backColor ?? Colors.transparent,
  177. children: [
  178. SizedBox(width: 15.pt),
  179. TextFormField(
  180. onTap: () => {
  181. if (widget.onInput != null)
  182. {
  183. widget.onInput!(true),
  184. }
  185. },
  186. controller: controller,
  187. onFieldSubmitted: onSubmitted,
  188. focusNode: focusNode,
  189. onChanged: onChanged,
  190. textInputAction: TextInputAction.search,
  191. style: TextStyle(
  192. fontSize: 15.pt,
  193. textBaseline: TextBaseline.alphabetic,
  194. ),
  195. decoration: InputDecoration(
  196. hintText: widget.hintText,
  197. isCollapsed: true,
  198. hintStyle: TextStyle(
  199. color: Color(0xff999999),
  200. fontSize: 14.pt,
  201. textBaseline: TextBaseline.alphabetic,
  202. ),
  203. prefixIcon: Image(
  204. image: AssetList.$search_png_image,
  205. height: 15.pt,
  206. ),
  207. prefixIconConstraints: BoxConstraints(
  208. minWidth: 32.pt,
  209. ),
  210. // suffix: Text.rich(
  211. // WidgetSpan(
  212. // alignment: PlaceholderAlignment.middle,
  213. // baseline: TextBaseline.alphabetic,
  214. // child: WDot.delete(
  215. // onTap: () => {
  216. // value = '',
  217. // controller.clear(),
  218. // setState(() {}),
  219. // },
  220. // color: Colors.black12,
  221. // iconTheme: IconThemeData(color: Colors.grey, size: 13.pt),
  222. // ),
  223. // ),
  224. // ),
  225. filled: true,
  226. isDense: true,
  227. fillColor: widget.isDark ? colorScheme.background4 : Colors.white,
  228. contentPadding: EdgeInsets.symmetric(vertical: 11.pt),
  229. enabledBorder: UnderlineInputBorder(
  230. /// 边框不设置一个值会有个黑边
  231. borderSide: BorderSide(color: Colors.white, width: 0.1.pt),
  232. borderRadius: BorderRadius.circular(25.pt),
  233. ),
  234. focusedBorder: UnderlineInputBorder(
  235. /// 边框不设置一个值会有个黑边
  236. borderSide: BorderSide(color: Colors.white, width: 0.1.pt),
  237. borderRadius: BorderRadius.circular(25.pt),
  238. ),
  239. ),
  240. ).nestExpanded(),
  241. if (!focusNode!.hasFocus && !_isStatic) SizedBox(width: 15.pt),
  242. if (_isStatic)
  243. Wisdom(
  244. onTap: () => {
  245. onSubmitted(value!),
  246. focusNode!.unfocus(),
  247. },
  248. alignment: Alignment.center,
  249. margin: EdgeInsets.symmetric(horizontal: 5.pt),
  250. padding: EdgeInsets.symmetric(horizontal: 10.pt),
  251. child: WisText(
  252. '搜索',
  253. size: 14.pt,
  254. color: colorScheme.primary,
  255. weight: FontWeight.w400,
  256. textAlign: TextAlign.center,
  257. ),
  258. ),
  259. if (focusNode!.hasFocus && !_isStatic)
  260. Wisdom(
  261. onTap: () => onCancel(),
  262. height: 25.pt,
  263. alignment: Alignment.center,
  264. margin: EdgeInsets.symmetric(horizontal: 5.pt),
  265. padding: EdgeInsets.symmetric(horizontal: 10.pt),
  266. child: Text(
  267. '取消',
  268. textAlign: TextAlign.center,
  269. style: TextStyle(fontSize: 13.pt),
  270. ),
  271. ),
  272. ],
  273. );
  274. }
  275. }