这是我参与11月更文应战的第11天,活动详情检查:2021最后一次更文应战

Flutter 仿写微信搜索页

如上图所示,咱们用 Flutter 来仿写查找页面,这里谈天首页点击查找栏会跳转到查找页,查找页面包括顶部查找框跟底部 ListView,在查找框内咱们输入查找词会检索谈天列表模型中 name 特点中包括查找词的模型,并在底部列表中展现,且查找词高亮显现。下面咱们别离来介绍下这些功能的完成。

顶部查找栏

Flutter 仿写微信搜索页

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: Column(
        children: [
          SizedBox(height: 40,), //上半部分留空
          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/放大镜b.png'), width: 20, color: Colors.grey,), //放大镜
                      Expanded(child: TextField(
                        controller: _editingController,
                        onChanged: _onChange,
                        autofocus: true,
                        cursorColor: Colors.green,
                        style: TextStyle(
                          fontSize: 16.0,
                          color: Colors.black87,
                          fontWeight: FontWeight.w400,
                        ),
                        decoration: InputDecoration(
                          contentPadding: EdgeInsets.only(left: 5, right: 5, bottom: 12,),
                          border: InputBorder.none,
                          hintText: '查找',
                          hintStyle: TextStyle(
                            fontSize: 16.0,
                            color: Colors.grey,
                            fontWeight: FontWeight.w400,
                          ),
                        ),
                      ),),
                      if (_showClose) GestureDetector(
                        onTap: () {
                          //清空查找框
                          _editingController.clear();
                          setState(() {
                            _onChange('');
                          });
                        },
                        child: Icon(
                          Icons.cancel,
                          color: Colors.grey,
                          size: 20,
                        ),
                      ),
                    ],
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  ),
                ), //左面白色背景
                GestureDetector (
                  onTap: () {
                    Navigator.pop(context);
                  },
                  child: Text('撤销'),
                ), //右边撤销按钮
              ],
            ),
          ), //查找条部分
        ],
      ),
    );
  }
}

针对查找页的整体布局咱们能够分为顶部的查找栏跟底部的查找列表两部分,针对查找栏咱们独自界说了一个 SearchBar 的类来完成,底部查找列表咱们用 Expanded 部件进行了包装。

SearchBar 完成细节

针对 SearchBar 咱们这里整体高度设置为了 84,这里最好高度界说为一个变量,依据机型来设置。针对查找栏咱们分为上下两部分,顶部留白及内容部分,顶部留白用 SizedBox 部件完成,底部内容用 Row 部件来完成,分为左面查找框及右边撤销按钮。

左面查找框完成

查找框整体部分咱们用 Container 部件来完成,能够设置宽、高及圆角特点。child 特点咱们也是用 Row 部件来完成,分为左面查找图标、中心 TextField 及右边 封闭按钮

TextField 完成细节

TextField 部件有两个比较重要的特点,onChangedcontrolleronChanged 咱们传入了一个外部办法 _onChange,能够监听输入框文字的改变,当有文字时展现封闭按钮,没有文字时隐藏封闭按钮。controller 咱们传入了一个外部界说的特点 _editingController,能够用来控制 TextField 的修改。

封闭按钮的完成

封闭按钮咱们依据 _showClose 来判别是否展现,且经过 GestureDetector 部件进行包装,点击的时分清空输入框及调用 _onChange 办法,参数传入空字符串,_onChange 办法中字符串为空的时分就会隐藏封闭按钮。

右边撤销按钮完成

撤销按钮点击的时分便是回来到上一个页面。

内容的检索

咱们监听到文字输入框的改变后,就需要进行内容的检索,而且在底部列表中进行展现。

内容的传递

class SearchCell extends StatelessWidget {
  final List<ChatModel>? datas;
  const SearchCell({this.datas});
}
class SearchPage extends StatefulWidget {
  final List<ChatModel>? datas;
  const SearchPage({this.datas});
}

这里咱们是依据谈天页面列表数据模型进行检索,找到名称中包括查找词的模型进行展现。所以咱们在 SearchCell 中界说了 datas 特点,而且在 SearchPage 中也界说了 datas 特点,别离在初始化的时分进行传递,这样咱们在查找页面就能够拿到了谈天列表的模型数据。

内容的检索

//满意查找条件的数据数组
  List<ChatModel> _models = [];
  //正在查找的内容
  String _searchStr = '';
  // 查找数据查找
  void _searchData(String text) {
    //每次查找前先清空
    _models.clear();
    _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(() {});
  }

咱们把检索逻辑都抽取到了 _searchData 办法中,输入内容改变的时分调用 _searchData 办法。这里咱们界说了一个 _models 数组,专门寄存检索数据,咱们遍历 datas,把检索到的模型添加到 _models 中。

查找列表完成

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));
  }

查找列表的 cell 便是复用谈天列表的 cell,可是需要改变的是查找内容的高亮显现,所以 ListTile 部件的 title 特点咱们用 RichText 来完成,完成内容这里抽取到了 _searchTitle 办法中。name.split(_searchStr) 会回来一个依据查找词 _searchStr 进行分割的数组,可是当字符串的最初或许结束有跟 _searchStr 相同的字符的时分在 searchStrs 数组中的元素便是空字符串,所以咱们就需要进行特别判 str == ''。咱们循环遍历 searchStrs 数组来创建 TextSpan 部件,而且依据内容是否跟查找内容相同来别离指定款式 _normalStyle_hightlightedStyle