自定义SearchPage
新建search_page.dart
文件,创建顶部搜索栏
以及布局搜索页面
<!-- 搜索页 -->
import 'package:flutter/material.dart';
// SearchPage是有状态的
class SearchPage extends StatefulWidget {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchBar(),
// Expanded灵活布局
Expanded(
flex: 1,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
itemCount: 3,
itemBuilder: (BuildContext context, int index) {
return Text('123$index');
},
)))
],
),
);
}
}
<!-- 搜索页顶部搜索栏 -->
class SearchBar extends StatefulWidget {
@override
_SearchBarState createState() => _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
@override
Widget build(BuildContext context) {
return Container(
height: 84,
color: Colors.red,
);
}
}
- 聊天页面搜索栏
跳转搜索页
// 聊天页搜索Cell
class SearchCell extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
print('点击了搜索框!');
// 跳转搜索页
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => SearchPage()
));
},
......

布局SearchBar
SearchBar
中的TextField
输入框,是Flutter
自身渲染引擎的。iOS的图层是通过UIKit
绘制上去的,而Flutter
中只有FlutterViewController
是iOS的,页面上的其他展示内容,都是Flutter
渲染引擎绘制上的。
class SearchBar extends StatefulWidget {
@override
_SearchBarState createState() => _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
@override
Widget build(BuildContext context) {
return Container(
height: 84,
child: Column(
children: [
// 搜索栏上面部分留空
SizedBox(
height: 40,
),
Container(
height: 44,
child: Row(
children: [
// 左侧圆角背景
Container(
width: screenWidth(context) - 40,
height: 34,
// 搜索图标,清除按钮向内缩进5像素
padding: EdgeInsets.only(left: 5, right: 5),
margin: 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(
autofocus: true, // 一进来搜索框自动聚焦
// 光标颜色
cursorColor: Colors.green,
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w300,
),
decoration: InputDecoration(
contentPadding:
EdgeInsets.only(left: 5, bottom: 10),
border: InputBorder.none,
hintText: '搜索'),
),
flex: 1,
), //输入框
Icon(
Icons.cancel,
size: 20,
color: Colors.grey,
), //取消按钮
],
),
), //搜索框
Text('取消'), //取消按钮
],
),
),//下面的搜索条
],
),
);
}
}

SearchBar响应事件
SearchBar
右侧取消按钮添加手势,点击返回
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Text('取消'),
), //取消按钮
SearchBar
没有输入内容的时候,不应该显示clear
按钮,输入内容的时候才显示
class _SearchBarState extends State<SearchBar> {
// 定义_controller就可以调用clear清空输入框的方法
final TextEditingController _controller = TextEditingController();
// 是否显示右侧clear按钮
bool _showClear = false;
void _onChange(String value) {
if (value.length > 0) {
setState(() {
_showClear = true;
});
} else {
setState(() {
_showClear = false;
});
}
}
......
child: Row(
children: [
Image(
image: AssetImage('images/放大镜b.png'),
width: 20,
color: Colors.grey,
), //放大镜
Expanded(
child: TextField(
controller: _controller,
onChanged: _onChange, //用于回调清除按钮的显示隐藏
autofocus: true,
// 一进来搜索框自动聚焦
// 光标颜色
cursorColor: Colors.green,
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w300,
),
decoration: InputDecoration(
contentPadding:
EdgeInsets.only(left: 5, bottom: 10),
border: InputBorder.none,
hintText: '搜索'),
),
flex: 1,
), //输入框
_showClear
? GestureDetector(
onTap: () {
_controller.clear();
setState(() {
// 触发_onChange回调,清空输入框的时候,也隐藏掉clear按钮
_onChange('');
});
},
child: Icon(
Icons.cancel,
size: 20,
color: Colors.grey,
),
) //清空按钮
: Container(),
],
),
SearchBar
进行搜索的两种思路
-
SearchBar
将输入的内容回调出去,告诉外面输入了什么? - 直接拿到内容,将搜索的结果放进去
下面使用的第二种思路解决
- 把
聊天页面数据
传递给SearchCell
<!-- search_cell.dart文件 -->
class SearchCell extends StatelessWidget {
final List<Chat> datas;
const SearchCell({this.datas}); //接收chat_page的数据源
......
<!-- chat_page.dart文件 -->
Widget _itemBuilderForRow(BuildContext context, int index) {
if (index == 0) {
// listView第一个Cell创建SearchCell
return SearchCell(
datas: _datas,
);
}
......
- 把
SearchCell
接收的数据再传递给搜索页
<!-- search_page.dart文件 -->
class SearchPage extends StatefulWidget {
final List<Chat> datas;
const SearchPage({this.datas});
......
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: (TapDownDetails details) {
Navigator.of(context).push(MaterialPageRoute(
// 跳转SearchPage的时候把datas带过去
builder: (BuildContext context) => SearchPage(
datas: datas,
)));
},
......
-
SearchBar
把输入的内容回调给SearchPage
<!-- SearchBar类 -->
class SearchBar extends StatefulWidget {
final ValueChanged<String> onChanged;
const SearchBar({this.onChanged});
......
void _onChange(String value) {
if (widget.onChanged != null) {
widget.onChanged(value);
}
setState(() {
_showClear = (value.length > 0);
});
}
// SearchPage要把回调传递给SearchBar
<!-- SearchPage类 -->
class _SearchPageState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchBar(
onChanged: (String text) {
print('我接收到了$text');
},
),

-
SearchPage
获取到搜索框的内容之后检索出数据
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SearchBar(
onChanged: (String text) {
print('我接收到了$text');
// 调用检索方法
_searchData(text);
},
),
......
class _SearchPageState extends State<SearchPage> {
// 搜索出来的数据
List<Chat> _modals = [];
void _searchData(String text) {
_modals.clear();
if (text.length > 0) {
for (int i = 0; i < widget.datas.length; i++) {
// 循环检索
String name = widget.datas[i].name;
if (name.contains(text)) {
_modals.add(widget.datas[i]);
}
}
}
setState(() {});
}
- 把检索出来的数据
_modals
填充页面
<!-- SearchPage类 -->
Expanded(
flex: 1,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
// shrinkWrap: true,//根据listView的内容大小来展示
itemCount: _modals.length,
itemBuilder: (BuildContext context, int index) {
return Text('${_modals[index].name}');
},
)))
......

SearchPage显示
SearchPage
使用ListTile
进行展示
<!-- SearchPage类 -->
Expanded(
flex: 1,
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
// shrinkWrap: true, //根据listView的内容大小来展示
itemCount: _modals.length,
// SearchPage的展示Cell抽取出来
itemBuilder: _buildCellForRow()),
),
),
......
Function _buildCellForRow() {
return ((BuildContext context, int index) {
return ListTile(
title: Text(_modals[index].name), //名字
subtitle: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(
right: 10,
),
height: 25,
child: Text(
_modals[index].message,
overflow: TextOverflow.ellipsis,
),
), //聊天信息
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(
image: NetworkImage(_modals[index].imageUrl))),
), //头像
);
});
}
检索出来的数据色值不同,是一个富文本
,要把名字展示抽成富文本方法
// 名字展示使用抽出来的富文本方法
Function _buildCellForRow() {
return ((BuildContext context, int index) {
return ListTile(
title: _title(_modals[index].name), //名字
......
// 定义搜索框输入的内容_searchStr,用于富文本高亮
class _SearchPageState extends State<SearchPage> {
String _searchStr = '';
List<Chat> _modals = [];
void _searchData(String text) {
_modals.clear(); //每次搜索先清空!
_searchStr = text;
......
// 富文本相关展示逻辑
TextStyle _normalStyle = TextStyle(
fontSize: 16,
color: Colors.black,
);
TextStyle _highlightedStyle = TextStyle(
fontSize: 16,
color: Colors.green,
);
// 富文本方法
Widget _title(String name) {
List<TextSpan> spans = [];
// 把当前cell的name按输入内容截成数组
// 数组内是空字符的三种情况 1.以输入字符开头 2.以输入字符结尾 3.是两个相邻的输入字符串
List<String> strs = name.split(_searchStr);
for (int i = 0; i < strs.length; i++) {
String str = strs[i]; //拿出字符串
if (str == '' && i < strs.length - 1) {
spans.add(TextSpan(text: _searchStr, style: _highlightedStyle));
} else {
spans.add(TextSpan(text: str, style: _normalStyle));
if (i < strs.length - 1) {
spans.add(TextSpan(text: _searchStr, style: _highlightedStyle));
}
}
}
return RichText(
text: TextSpan(children: spans),
);
}

尝试练习:实现模糊搜索
,输入内容包含空格,依然能够检索出来...
关于Dart多线程与异步的总结
Dart中的异步
* Future对象来完成异步操作。
* 通过工厂构造方法创建Future对象。
* 参数为Dart的函数
* 函数的执行代码将被放入事件队列异步执行。
* async 和 await 。如果Future内部代码希望同步执行,则使用await修饰。被async修饰的函数为异步执行。
* Future结果处理
* Future.then 用来注册一个Future完成时要调用的回调
* Future.catchError注册一个回调,来捕捉Future的error
* Future.catchError回调只处理原始Future抛出的错误,不能处理回调函数抛出的错误
* onError只能处理当前Future的错误
* Future.whenComplete 在Future完成之后总是会调用,不管是错误导致的完成还是正常执行完毕。
Dart的事件循环
* 在Dart中,实际上有两种队列:
* 事件队列(event queue),包含所有的外来事件:I/O、mouse events、drawing events、timers、isolate之间的信息传递。
* 微任务队列(microtask queue),表示一个短时间内就会完成的异步任务。它的优先级最高,高于event queue,只要队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由 Dart内部产生。
Dart中的多线程
* Dart是单线程语言,但并不代表它不能并行执行代码。因为它拥有Isolate。
* Isolate
* Isolate可以看成是一个小的进程。
* 它拥有独立的内存空间,不同Isolate之间通过消息进行通信
* 它拥有自己的事件循环及队列(MicroTask 和 Event)
* Isolate使用
* 1、创建子线程任务:Isolate.spawn(arg1,arg2);
* arg1: 需要在子线程执行的函数
* arg2:传递给函数的参数
* 这样就在另一个线程中执行arg1的代码了。
* 2、端口通讯
* ReceivePort port = ReceivePort()//构造一个端口。
* port.listen(arg)//注册一个回调监听
* arg为回调函数。参数为消息内容
* 在子线程中.通过port.send() 发送消息
* 3、关闭端口,销毁Isolate
* 注意端口使用完毕需要调用port.close()函数关闭
* Isolate使用完毕,需要调用Isolate.kill()函数销毁
* compute
* 由于dart中的Isolate比较复杂,数据传输比较麻烦,因此flutter在foundation库中封装了一个轻量级compute操作
* 使用:compute(func,count)
* func:子线程函数!func如果有返回值会直接被compute函数返回出去!
* count: 给函数的参数
异步多线程结合
* Dart中的异步是可以和多线程结合使用的。
* 如果Future中返回子线程的返回值。那么Future的处理是异步的
* 如果Future中没有返回子线程的返回值。那么Future的处理是同步的
* Future的结果处理会在Future执行完毕立即执行。可以看做是一个任务。
网友评论