Final results of this article

Add a search bar on the home page, click to jump to a new page, input can be real-time search.

Analysis methods

ItemCount: _datas.length + 1 = itemCount: _datas.length + 1 = itemBuilder = itemCount: _datas.length + 1 = itemBuilder = itemCount: _datas.length + 1 = itemBuilder = itemCount: _datas.length + 1 = itemBuilder = itemCount: _datas.length + 1

Method extract shortcut key command+option+M, pop-up dialog box input method name Refactor ok

Add a search box here when index=0, to make sure the following data is not messed up, we need index–

  Widget _itemBuilderForRow(BuildContext context, int index) {
    if (index == 0) {
      return Container(color: Colors.red, height: 44);
    }
    index--;
    returnListTile( title: Text(_datas[index].name!) , subtitle: Container( alignment: Alignment.bottomCenter, padding: EdgeInsets.only(right:10),
        height: 20,
        child: Text(
          _datas[index].message!,
          overflow: TextOverflow.ellipsis,
        ),
      ),
      leading: CircleAvatar(
        backgroundImage: NetworkImage(_datas[index].imgUrl!),
      ),
    );
  }
Copy the code

SearchCell

Click trigger can use GestureDetector, layout using Stack + row mix and match can be achieved

import 'package:flutter/material.dart';

class SearchCell extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Container(
        color: Color.fromRGBO(238.238.238.1),
        height: 44,
        padding: EdgeInsets.all(5),
        child: Stack(
          children: [
            Container(
              decoration: BoxDecoration(
                  color: Colors.white, borderRadius: BorderRadius.circular(5)),
            ),
            Container(
              height: 44,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Image.asset(
                    'Images/Magnifying glass B.png',
                    width: 15,
                    color: Colors.grey,
                  ),
                  Text(
                    'search',
                    style: TextStyle(fontSize: 15, color: Colors.grey), ) ], ), ) ], ), ), ); }}Copy the code

SearchPage

First, the search page is stateful and a view of a Column layout without an AppBar. Above is a SearchBar, below is a ListView and supports rich text display. So when SearchCell clicks, it jumps to a new page

Navigator.of(context).push(
            MaterialPageRoute(builder: (BuildContext context) => SearchPage()));
Copy the code

In the SearchPage layout, we do not set the appBar property for that Scaffold

class _SearchPageState extends State<SearchPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          SearchBar(),
          Expanded(
              child: ListView.builder(
                  itemCount: 10,
                  itemBuilder: (BuildContext context, int index) {
                    return Text('123,$index'); }))],),); }}Copy the code

The running effect is as follows:

We found a margin on the ListView. Let’s write it like this to get rid of it:

  Expanded(
    child: MediaQuery.removePadding(
             context: context,
             removeTop: true,
             child: ListView.builder(
                      itemCount: 10,
                      itemBuilder: (BuildContext context, int index) {
                        return Text('123,$index'); })))Copy the code

SearchBar

The SearchBar is a Column structure with a placeholder SizeBox on top and a Container on the bottom. Inside the Container is a rounded decorator with a Row layout and a Text on the right

class _SearchBarState extends State<SearchBar> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 84,
      color: Colors.red,
      child: Column(
        children: [
          SizedBox(height: 40),
          Container(
            height: 44,
            color: Colors.grey,
            child: Row(
              children: [
                Container(
                  margin: EdgeInsets.only(left: 6, right: 6),
                  width: screenWidth(context) - 45,
                  height: 34,
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(6),
                      color: Colors.white),
                ),
                Text('cancel'() [() [() [() [() }}Copy the code

Effect after running:

Next, inside the Container, continue the Row layout with a search image on the left, a TextField in the middle, and a clear button on the right

Row(
  children: [
    Image.asset('Images/Magnifying glass B.png',
      width: 20, color: Colors.grey),
    Expanded(
      child: TextField()),
    Icon(
      Icons.clear,
      color: Colors.grey,
      size: 20,),),Copy the code

Several problems are apparent

  • Too close left and right
  • The cursor color is wrong
  • TextField borders are colored
  • Text input is positioned downward, not centered
  • There is no default placeholder text

In view of the above problems, the following details are added:

padding: EdgeInsets.only(left: 6, right: 6),
TextField(
  cursorColor: Colors.green,
  style: TextStyle(
    fontWeight: FontWeight.w300,
    fontSize: 16,
	),
  decoration: InputDecoration(
    hintText: 'search',
    border: InputBorder.none,
    contentPadding: EdgeInsets.only(bottom: 12, left: 6)))Copy the code

Continue to fill in the details: when the cancel button is clicked, it returns to the next level of the page, and when the TextView has words, the Clear button shows them, and vice versa. Typedef ValueChanged

= void Function(T value); , add a variable _showClear to record whether to display. During layout, show _showClear if it is true, otherwise give a blank Container()

 void _onChanged(String value) {
    setState(() {
      _showClear = value.length > 0 
    });
  }
Copy the code

Then, when we click the Clear button, we need to clear the current text input field, so we need to add a click event to the Clear button.

if (_showClear)
   GestureDetector(
      onTap: () {
        _controller.clear();
         _onChanged(' ');
      },
      child: Icon(
        Icons.clear,
        color: Colors.grey,
        size: 20,),)Copy the code

Okay, so now that you’ve implemented the input text field, the clear button shows and the hide button hides. And when the clear button is clicked, the text box will be synchronously cleared.

The data processing

Datas ==> SearchCell.datas ==>SearchPage.datas callback: When initializing the SearchBar, define a callback, somewhat like a closure.

final ValueChanged<String>? onChanged;
const SearchBar({this.onChanged});
Copy the code

This function is then called on TextField’s onChanged

if(widget.onChanged ! =null) { widget.onChanged! (value); }Copy the code

Finally, the callback is used in the SearchPage where the SearchBar is initialized

SearchBar(onChanged: (value) {
            print('to search for$value');
}),
Copy the code

This allows you to listen to text input from the SearchBar in real time and retrieve data from the SearchPage.datas. The retrieval method is relatively simple, noting that setState is called after each retrieval to update the data.

  List<ChatModel> _models = [];
  void _searchData(String value) {
    _models.clear();
    if (value.length > 0&& widget.data ! =null) {
      for (int i = 0; i < widget.data! .length; i++) {Stringname = widget.data! [i].name ??' ';
        print(name);
        if(name.contains(value)) _models.add(widget.data! [i]); } } setState(() {}); }Copy the code

The input text needs to be highlighted in the corresponding cell. Let’s continue to implement it. For Text here, we can define a special method to customize the return. So what I’m going to do here is split.

// The normal text style
TextStyle _normalStyle = TextStyle(color: Colors.black, fontSize: 16);
// Retrieve the highlighted text style
TextStyle _highlightedStyle = TextStyle(color: Colors.green, fontSize: 16);

Widget _text(String name) {
  List<TextSpan> spans = [];
  List<String> strTexts = name.split(_searchText);
  print(strTexts);
  if (strTexts.length > 0) {
    for (int i = 0; i < strTexts.length; i++) {
       String indexStr = strTexts[i];
       if (indexStr == ' ') {
         // Do not add the end of the last line
         if (i < strTexts.length - 1) { spans.add(TextSpan(text: _searchText, style: _highlightedStyle)); }}else {
         spans.add(TextSpan(text: indexStr, style: _normalStyle));
          // The end of the last line of characters is not added
         if (i < strTexts.length - 1) { spans.add(TextSpan(text: _searchText, style: _highlightedStyle)); }}}}return RichText(text: TextSpan(children: spans));
 }
Copy the code