This is the 11th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021
Click the search bar on the chat homepage to jump to the search page. The search page contains the top search box and the bottom ListView. If we enter the search words in the search box, the models containing the search words in the name attribute of the chat list model will be retrieved and displayed in the bottom list. And the search term is highlighted. Let’s introduce the implementation of these functions respectively.
Top search bar
class SearchBar extends StatefulWidget { final ValueChanged<String>? onChanged; const SearchBar({this.onChanged}); @override _SearchBarState createState() => _SearchBarState(); } class _SearchBarState extends State<SearchBar> { final TextEditingController _editingController = TextEditingController(); bool _showClose = false; void _onChange(text) { if (null ! = widget.onChanged) { widget.onChanged! (text); } setState(() { _showClose = ((text as String).length > 0); }); } @override Widget build(BuildContext context) { return Container( height: 84, color: CahtThemColor, child: Container(height: 44, child: Row(children: [Container(width: screenWidth(context) - 50, height: 34, margin: EdgeInsets.only(left: 5, right: 10), padding: EdgeInsets.only(left: 5, right: 5), decoration: BoxDecoration(color: Colors. White, borderRadius: borderRadius. Circular (6.0),), child: Row(children: [Image(Image: AssetImage('images/ magnifier B.png '), width: 20, color: color.grey,), // Magnifier Expanded(child: TextField( controller: _editingController, onChanged: _onChange, autofocus: true, cursorColor: Colors.green, style: TextStyle(fontSize: 16.0, color: color.black87, fontWeight: fontweight.w400,), decoration: InputDecoration( contentPadding: EdgeInsets.only(left: 5, right: 5, bottom: 12,), border: InputBorder.none, hintText: 'search ', hintStyle: TextStyle(fontSize: 16.0, color: color. grey, fontWeight: FontWeight.w400, ), ), ),), if (_showClose) GestureDetector( onTap: () {// Clear the search box _editingController.clear(); setState(() {_onChange(");});}, child: Icon(Icons. Cancel, color: Colors.grey, size: 20, ), ), ], mainAxisAlignment: MainAxisAlignment spaceBetween,),), / / the left white background GestureDetector (onTap: () {the Navigator. Pop (context);}, the child: The Text (the 'cancel'),), / / cancel button on the right),),), / / search. Part],),); }}Copy the code
The overall layout of the search page can be divided into two parts: the SearchBar at the top and the search list at the bottom. We define a SearchBar class to realize the SearchBar, and the search list at the bottom is packaged with Expanded components.
SearchBar implementation details
For SearchBar, we set the overall height to 84. Here, it is better to define the height as a variable and set it according to the model. The search bar is divided into two parts: white space at the top and the content part. The white space at the top is realized by SizedBox, and the content at the bottom is realized by Row, which is divided into search box on the left and cancel button on the right.
Left search box implementation
The overall part of the search box is implemented using the Container widget, which allows you to set the width, height, and rounded corners. The Child property is also implemented using the Row widget, divided into the search icon on the left, the TextField in the middle, and the close button on the right.
TextField implementation details
The TextField widget has two important properties, onChanged and Controller. OnChanged We pass in an external method _onChange that listens for changes in the text of the input field, showing the close button when there is text, and hiding the close button when there is no text. Controller We pass in an externally defined property, _editingController, which controls editing of the TextField.
Close the implementation of the button
The close button is judged by _showClose and packaged by GestureDetector component. When clicked, the input box is cleared and the _onChange method is called. The parameter is passed an empty string. The close button is hidden when the string in the _onChange method is empty.
Right cancel button implementation
When the cancel button is clicked, it returns to the previous page.
Content retrieval
After we listen for changes in the text input box, we need to retrieve the content and display it in the bottom list.
Content delivery
class SearchCell extends StatelessWidget {
final List<ChatModel>? datas;
const SearchCell({this.datas});
}
Copy the code
class SearchPage extends StatefulWidget {
final List<ChatModel>? datas;
const SearchPage({this.datas});
}
Copy the code
Here we are based on the chat page list data model for retrieval, find the name of the search term in the model for display. So we define datas properties in the SearchCell and datas properties in the SearchPage, which are passed during initialization so that we can retrieve the model data of the chat list in the SearchPage.
Content retrieval
List<ChatModel> _models = []; // What is being searched String _searchStr = ''; Void _searchData(String text) {// Clear _models.clear() before each search; _searchStr = text; if (text.length < 1) { setState(() {}); return; } for (int i = 0; i < datas.length; i++) { String name = widget.datas? [i].name as String; if(name.contains(text)) { _models.add(widget.datas? [i] as ChatModel); } } setState(() {}); }Copy the code
We extract all the retrieval logic into the _searchData method, which is called when the input changes. Here we define an _Models array to hold the retrieved data, and we iterate through the datAS to add the retrieved models to the _Models.
Search list implementation
TextStyle _normalStyle = TextStyle(
fontSize: 16,
color: Colors.black,
);
TextStyle _hightlightedStyle = TextStyle(
fontSize: 16,
color: Colors.green,
);
Widget _searchTitle(String name) {
List<TextSpan> textSpans = [];
List<String> searchStrs = name.split(_searchStr);
for (int i = 0; i < searchStrs.length; i++) {
String str = searchStrs[i];
if (str == '' && i < searchStrs.length - 1) {
textSpans.add(TextSpan(text: _searchStr, style: _hightlightedStyle));
} else {
textSpans.add(TextSpan(text: str, style: _normalStyle));
if (i < searchStrs.length - 1) {
textSpans.add(TextSpan(text: _searchStr, style: _hightlightedStyle));
}
}
}
return RichText(text: TextSpan(children: textSpans));
}
Copy the code
The search list cell is the same as the chat list cell, but what needs to be changed is the highlighting of the search content, so the title property of the ListTile widget is implemented with RichText, and the content is extracted into the _searchTitle method. Name.split (_searchStr) returns an array split by the search term _searchStr, but the elements in the searchStrs array are empty strings if the string begins or ends with the same character as _searchStr. So we need to make a special judgment STR == “”. We loop through the searchStrs array to create the TextSpan widget and specify styles _normalStyle and _hightlightedStyle depending on whether the content is the same as the search content.