这是我参与11月更文应战的第24天,活动详情检查:2021最终一次更文应战
在谈天界面增加一个查找框。那么就在ListView里边增加一个cell,那么就需求itemCount里边加1。
itemCount: _datas.length + 1,
创立一个chat package,然后将chat_page拖进来并且从头创立一个search_cell文件。
将ListView里边的itemBuilder的代码抽取成一个办法,然后在里边判别假如index == 0 则回来 SearchCell。这儿后边需求index–,否则index就会从1开端。
接下来写search cell 的界面,先赋予一个高度和色彩,来看是否能显现。
return Container(
height: 45,
width: 200,
color: Colors.red,
);
运转后看到显现了出来。
查找框需求响应点击时间,所以这儿需求用GestureDetector包一下,然后色彩改为weChatThemColor,里边用Stack来写。
return GestureDetector(
child: Container(
height: 45,
width: 200,
color: weChatThemColor,
padding: EdgeInsets.all(5),
child: Stack(
children: [
],
),
),
);
在stack里边先增加一个白底
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
),//白底
然后增加Row里边放图片和文字,为了让其居中设置row的mainAxisAlignment为MainAxisAlignment.center,而让row居中则将stack的alignment改为Alignment.center。
child: Stack(
alignment: Alignment.center,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
),//白底
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(image: AssetImage('images/放大镜b.png'),width: 15,color: Colors.grey,),
Text(' 查找',style: TextStyle(fontSize: 15,color: Colors.grey),),
],),
],
),
这样查找框页面就完成了
接下来点进去需求到一个新的界面,那么就增加onTap,然后创立一个文件来写新页面SearchPage。
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchBar(),
Expanded(
flex: 1,
child: ListView.builder(
itemBuilder: itemBuilder,
itemCount: 3,
),
),
],
),
);
}
Widget itemBuilder(BuildContext context, int index) {
return Container(
child:Text("Column$index"),
);
}
}
class SearchBar extends StatefulWidget {
const SearchBar({Key? key}) : super(key: key);
@override
_SearchBarState createState() => _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
@override
Widget build(BuildContext context) {
return Container(
height: 84,
color: weChatThemColor,
);
}
}
然后在SearchCell的GestureDetector里边增加页面push。
onTap: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
SearchPage()));
},
运转后得到下面的界面:
发现这儿有距离,那么就运用removePadding将ListView头部距离去掉。
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemBuilder: itemBuilder,
itemCount: 3,
),
),
然后开端编写SearchBar。
return Container(
height: 84,
color: weChatThemColor,
child: Column(
children: [
SizedBox(height: 40,),
Container(
height: 44,
color:Colors.red,
child: Row(
children: [
Container(
width: screenWidth(context) - 40,
height: 34,
color: Colors.yellow,
),//圆角布景
Text('撤销'),//撤销按钮
],
),
),
],
),
);
运转后看到:
然后在增加圆角,距离以及按钮等。
return Container(
height: 84,
color: weChatThemColor,
child: Column(
children: [
SizedBox(
height: 40,
),
Container(
height: 44,
color: Colors.red,
child: Row(
children: [
Container(
width: screenWidth(context) - 50,
height: 34,
margin: EdgeInsets.only(left: 5, right: 5),
padding: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image(
image: AssetImage('images/放大镜b.png'),
width: 20,
color: Colors.grey,
), //放大镜
Icon(Icons.cancel),
],
),
), //圆角布景
Text('撤销'), //撤销按钮
],
),
),
],
),
);
然后还需求增加TextField。
return Container(
height: 84,
color: weChatThemColor,
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: 5),
padding: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Image(
image: AssetImage('images/放大镜b.png'),
width: 20,
color: Colors.grey,
), //放大镜
Expanded(
child: TextField(
cursorColor: Colors.green,
autofocus: true,
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w300,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 5,bottom: 10),
border:InputBorder.none,
hintText:'查找',
),
),
),
Icon(Icons.cancel,size: 20,color: Colors.grey,),
],
),
), //圆角布景
Text('撤销'), //撤销按钮
],
),
),
],
),
);
之前用Expanded包着ListView是因为ListView没有巨细,当咱们设置了 shrinkWrap: true之后,那么就会依据ListView的内容巨细来展示,就不需求Expanded了。咱们这儿还是用之前的写法,让ListView占满屏幕下面部分。
return Scaffold(
body: Column(
children: [
SearchBar(),
ListView.builder(
shrinkWrap: true,
itemBuilder: itemBuilder,
itemCount: 3,
),
],
),
);
接下来处理点击和逻辑事情
这儿先为撤销增加一个pop的点击事情。
GestureDetector(
child: Text('撤销'),
onTap: () {
Navigator.pop(context);
},
),
接下来需求监听查找框,当没有任何东西的时分,那么就不显现cancel icon。创立一个TextEditingController变量。
final TextEditingController _textEditingController = TextEditingController();
为TextField增加Controller
controller: _textEditingController,
然后监听TextField的onChanged
onChanged: _onChanged,
void _onChanged(value) {}
然后创立_showClear来判别是否显现TextField后边的撤销按钮,默认为false。
bool _showClear = false;
在_onChanged办法里边进行字符串长度的判别以及_showClear的赋值。
void _onChanged(String text) {
if (text.length > 0) {
setState(() {
_showClear = true;
});
} else {
setState(() {
_showClear = false;
});
}
}
依据_showClear的值觉得是否显现Icon。
if (_showClear) Icon(
Icons.cancel,
size: 20,
color: Colors.grey,
)
为 cancel Icon增加点击手势清空输入内容。
if (_showClear) GestureDetector(
child: Icon(
Icons.cancel,
size: 20,
color: Colors.grey,
),
onTap: () {
_textEditingController.clear();
setState(() {
_onChanged("");
});
},
)
这样页面部分就完成了。这儿有个问题,ChatPage点击进去SearchPage的时分那么SearchPage一定要有数据,那么SearchBar 是否有数据呢?这儿有两种状况,一种是有,将数据传给SearchBar,查找完之后将成果回来给SearchPage,第二种是没有,SearchBar直接经过回调将输入内容给SearchPage。 先在SearchCell增加
final List<Chat>? datas;
const SearchCell({ this.datas});
然后在ChatPage为其赋值,这样数据就给了SearchCell。
if (index == 0) {
return SearchCell(datas: _datas,);
}
然后再传给SearchPage。在SearchPage增加:
final List<Chat>? datas;
const SearchPage({ this.datas});
然后在SearchCell进来的地方将data传进来
SearchPage(datas: datas,)
在SearchBar里边增加一个回调:
const SearchBar({Key? key, this.onChanged}) : super(key: key);
final ValueChanged<String>? onChanged;
然后在_onChanged里边调用这个回调。这儿if else 判别太长了,能够直接运用 _showClear = text.length > 0进行设值。
void _onChanged(String text) {
if (widget.onChanged != null) {
widget.onChanged!(text);
}
setState(() {
_showClear = text.length > 0;
});
}
这个时分SearchPage就能够传回调了
SearchBar(onChanged: ( String text){},),
创立一个 _searchData办法来处理回调传过来的文字。
SearchBar(onChanged: (String text){
_searchData(text);
},),
void _searchData(String text) {
}
接下来创立一个数组来装契合条件的数据
List<Chat> _models = [];
_searchData里边循环检索然后增加契合条件的数据到_models。
void _searchData(String text) {
_models.clear(); // 每次查找先状况
if (text.length > 0 ) {
if (widget.datas != null){ // 循环检索
for (int i = 0; i < widget.datas!.length;i++ ) {
String? name = widget.datas![i].name;
if ((name ?? "").contains(text)) {
_models.add(widget.datas![i]);
}
}
}
}
setState(() {
});
}
接下来依据_models显现ListView的内容,将itemCount改为_models.length。
itemCount: _models.length,
在itemBuilder里边回来
Widget itemBuilder(BuildContext context, int index) {
return Container(
color: Colors.red,
child: Text("${_models[index].name}"),
);
}
这样查找就能够显现契合条件的Model了。
接下来需求来编写Cell的界面,这儿能够直接把之前谈天界面的款式仿制过来。
ListTile(
title: Text(_models[index].name ?? ""),
subtitle: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(right: 10),
height: 25,
child: Text(
_models[index].message ?? "",
overflow: TextOverflow.ellipsis,
),
),
leading: ClipRRect(
//剪裁为圆角矩形
borderRadius: BorderRadius.circular(5.0),
child: Image(image: NetworkImage(_models[index].imageUrl ?? "")),
),
);
运转后:
这儿需求对姓名做高亮处理,那么就把title部分提取出来做处理。
title: _title(_models[index].name ?? ""),
Text _title(String name) {
return Text(name);
}
咱们要知道查找的姓名是什么,所以这儿声明一个变量
String _searchStr = "";
然后在_searchData里边赋值。
_searchStr = text
然后在声明2个TextStyle
TextStyle _normalStyle = TextStyle(fontSize: 16,color: Colors.black);
TextStyle _highLightedStyle = TextStyle(fontSize: 16,color: Colors.green);
然后再_title办法里边处理。
idget _title(String name) {
List<TextSpan> spans = [];
List<String> strs = name.split(_searchStr);
for (int i = 0; i < strs.length; i ++) {
String str = strs[i];
print(strs);
if (str == "" && i < strs.length - 1) {
print('here1');
spans.add(TextSpan(text: _searchStr,style:_highLightedStyle));
} else {
print('here2');
spans.add(TextSpan(text: str,style:_normalStyle));
if (i < strs.length - 1) {
print('here3');
spans.add(TextSpan(text: _searchStr,style:_highLightedStyle));
}
}
}
这样查找高亮就完成了。