Line data Source code
1 : import 'package:flutter/material.dart';
2 : import 'package:flutter/services.dart';
3 : import 'icon.dart' as icon;
4 : import 'dart:convert';
5 :
6 : typedef void OnSubmitHandle(String text);
7 : typedef void OnChangeHandle(String text);
8 : typedef void OnFocusChangeHandle(bool focus);
9 :
10 3 : enum BarStyle {
11 : solid,
12 : border,
13 : }
14 :
15 : class SearchBar extends StatefulWidget {
16 : final String placeHolder;
17 : final bool autofocus;
18 : final bool enabled;
19 : final String? cancelText;
20 : final Color? cursorColor;
21 : final Color? searchIconColor;
22 : final Brightness keyboardAppearance;
23 : final BarStyle style;
24 : final Widget? right;
25 : final int inputCharactersLength;
26 : final Function? rightAction;
27 : final OnChangeHandle? onChangeHandle;
28 : final OnSubmitHandle? onSubmitHandle;
29 : final OnFocusChangeHandle? onFocusChangeHandle;
30 :
31 1 : SearchBar({
32 : Key? key,
33 : this.placeHolder = '请输入要搜索的内容',
34 : this.autofocus = false,
35 : this.enabled = true,
36 : this.cancelText,
37 : this.cursorColor,
38 : this.right,
39 : this.style = BarStyle.solid,
40 : this.keyboardAppearance = Brightness.light,
41 : this.rightAction,
42 : this.searchIconColor,
43 : this.onChangeHandle,
44 : this.onSubmitHandle,
45 : this.onFocusChangeHandle,
46 : this.inputCharactersLength = 100,
47 1 : }) : super(key: key);
48 :
49 1 : @override
50 1 : _SearchInputState createState() => _SearchInputState();
51 : }
52 :
53 : class _SearchInputState extends State<SearchBar> {
54 : bool _showClearIcon = false;
55 : TextEditingController _inputController = TextEditingController();
56 : FocusNode _focusNode = FocusNode();
57 :
58 1 : @override
59 : void initState() {
60 1 : super.initState();
61 2 : _focusNode.addListener(() {
62 0 : if (widget.onFocusChangeHandle != null) {
63 0 : widget.onFocusChangeHandle!(_focusNode.hasFocus);
64 : }
65 : });
66 : }
67 :
68 1 : @override
69 : void dispose() {
70 2 : _inputController.dispose();
71 1 : super.dispose();
72 : }
73 :
74 0 : void _focus() {
75 0 : _unfocus();
76 0 : FocusScope.of(context).requestFocus(_focusNode);
77 : }
78 :
79 0 : void _unfocus() {
80 0 : FocusScope.of(context).requestFocus(FocusNode());
81 : }
82 :
83 0 : void _inputSubmitHandle(String query) {
84 0 : _unfocus();
85 0 : if (widget.onSubmitHandle is OnSubmitHandle) {
86 0 : widget.onSubmitHandle!(query);
87 : }
88 : }
89 :
90 0 : void _inputChangeHandle(String query) {
91 0 : bool curClearIconStatus = query.isNotEmpty;
92 0 : if (curClearIconStatus != _showClearIcon) {
93 0 : setState(() {
94 0 : _showClearIcon = curClearIconStatus;
95 : });
96 : }
97 0 : if (widget.onChangeHandle != null) {
98 0 : widget.onChangeHandle!(query);
99 : }
100 : }
101 :
102 1 : Widget _buildSearcIcon() {
103 1 : return Container(
104 : margin: const EdgeInsets.only(right: 11.0),
105 1 : child: Image.memory(
106 1 : base64.decode(icon.searchIconData),
107 : width: 16,
108 : height: 16,
109 : ));
110 : }
111 :
112 1 : Widget _buildClearIcon() {
113 1 : if (!_showClearIcon) {
114 1 : return Container(width: 0, height: 0);
115 : }
116 0 : return GestureDetector(
117 0 : onTap: () {
118 0 : WidgetsBinding.instance!.addPostFrameCallback((_) {
119 0 : _inputController.clear();
120 0 : _focus();
121 : });
122 0 : _inputChangeHandle('');
123 : },
124 0 : child: Container(
125 0 : margin: EdgeInsets.only(left: 16.0),
126 0 : child: Image.memory(base64.decode(icon.searchClearIconData),
127 : width: 16, height: 16)),
128 : );
129 : }
130 :
131 1 : Widget _buildTextField() {
132 1 : List<TextInputFormatter> formatters = [];
133 3 : if (widget.inputCharactersLength > 0) {
134 1 : formatters = [
135 3 : LengthLimitingTextInputFormatter(widget.inputCharactersLength)
136 : ];
137 : }
138 :
139 1 : return Theme(
140 3 : data: Theme.of(context).copyWith(splashColor: Colors.transparent),
141 1 : child: TextField(
142 2 : enabled: widget.enabled,
143 : textAlignVertical: TextAlignVertical.center,
144 2 : keyboardAppearance: widget.keyboardAppearance,
145 1 : focusNode: _focusNode,
146 1 : controller: _inputController,
147 1 : onChanged: _inputChangeHandle,
148 2 : autofocus: widget.autofocus,
149 : maxLines: 1,
150 1 : onSubmitted: _inputSubmitHandle,
151 : // ignore: deprecated_member_use
152 : maxLengthEnforced: false,
153 1 : style: TextStyle(
154 : fontSize: 15.0,
155 : color: Colors.black,
156 : textBaseline: TextBaseline.alphabetic,
157 : ),
158 2 : cursorColor: widget.cursorColor ?? Colors.red,
159 1 : decoration: InputDecoration(
160 : isDense: true,
161 : contentPadding: EdgeInsets.zero,
162 : border: InputBorder.none,
163 2 : hintText: widget.placeHolder,
164 : hintMaxLines: 1,
165 1 : hintStyle: TextStyle(
166 : fontSize: 15.0,
167 3 : color: widget.style == BarStyle.border
168 : ? Colors.black38
169 : : Colors.black54,
170 : textBaseline: TextBaseline.alphabetic,
171 : )),
172 : inputFormatters: formatters,
173 : ),
174 : );
175 : }
176 :
177 1 : Widget _buildInput() {
178 1 : return Container(
179 3 : decoration: widget.style == BarStyle.border
180 0 : ? BoxDecoration(
181 : color: Colors.white,
182 : borderRadius: const BorderRadius.all(Radius.circular(6.0)),
183 0 : border: Border.all(
184 : color: Colors.black12, style: BorderStyle.solid, width: 1))
185 1 : : BoxDecoration(
186 1 : color: Colors.black.withOpacity(0.04),
187 : borderRadius: const BorderRadius.all(Radius.circular(6.0))),
188 : padding:
189 : const EdgeInsets.only(top: 9.0, left: 12.0, right: 12.0, bottom: 8.0),
190 1 : child: Flex(
191 : direction: Axis.horizontal,
192 1 : children: <Widget>[
193 1 : Expanded(
194 : flex: 0,
195 1 : child: _buildSearcIcon(),
196 : ),
197 2 : Expanded(flex: 1, child: _buildTextField()),
198 2 : Expanded(flex: 0, child: _buildClearIcon())
199 : ],
200 : ),
201 : );
202 : }
203 :
204 1 : Widget _buildClickButton() {
205 : Widget right;
206 2 : if (widget.right != null) {
207 0 : right = Container(
208 : constraints: const BoxConstraints(maxHeight: 38),
209 : padding: const EdgeInsets.only(left: 20.0, right: 20),
210 0 : child: widget.right);
211 : } else {
212 2 : if (widget.cancelText == null) {
213 1 : right = Container(width: 0, height: 0);
214 : } else {
215 0 : right = Padding(
216 : padding: const EdgeInsets.only(left: 20.0, right: 20),
217 0 : child: Text(widget.cancelText!,
218 0 : style: TextStyle(
219 : fontSize: 15.0,
220 : fontWeight: FontWeight.bold,
221 : color: Colors.black)));
222 : }
223 : }
224 1 : return GestureDetector(
225 0 : onTap: () {
226 0 : _inputSubmitHandle(_inputController.text);
227 0 : if (widget.rightAction != null) {
228 0 : widget.rightAction!();
229 : }
230 : },
231 : child: right);
232 : }
233 :
234 1 : @override
235 : Widget build(BuildContext context) {
236 1 : return Container(
237 1 : child: Flex(
238 : direction: Axis.horizontal,
239 1 : children: <Widget>[
240 1 : Expanded(
241 : flex: 1,
242 1 : child: _buildInput(),
243 : ),
244 2 : Expanded(flex: 0, child: _buildClickButton())
245 : ],
246 : ));
247 : }
248 : }
|