前言
在很多 App 中,当用户输入搜索内容时,往往会自动匹配一些候选搜索内容,以便让用户快速完成搜索内容的输入。同时,也可以在候选的搜索内容中根据用户偏好“加塞”广告,提高成交转化率。比如在淘宝搜索“苹果”这个词时,就会出现苹果相关的候选选项。类似这样的功能就需要用到输入内容的自动填充。
这种功能可以自己去写两个独立的组件,比如一个搜索框,一个候选内容弹层,通过内容联动来实现。不过,Flutter 中其实有自带的组件,叫做 AutoComplete
。
AutoComplete 简介
AutoComplete是一个泛型类,组件定义如下。
class Autocomplete<T extends Object> extends StatelessWidget {
const Autocomplete({
super.key,
required this.optionsBuilder,
this.displayStringForOption = RawAutocomplete.defaultStringForOption,
this.fieldViewBuilder = _defaultFieldViewBuilder,
this.onSelected,
this.optionsMaxHeight = 200.0,
this.optionsViewBuilder,
this.initialValue,
});
//……
其中泛型指的是候选内容对象的类型,最简单的就是 String
类型,但是我们后台返回的可能会是一个完整的对象,这个时候我们就可以定义为后台对象对应的实体类。具体的参数说明如下:
optionsBuilder
:选项构造器,实际上就是匹配的规则,我们可以基于用户输入的内容去做二次处理,比如模糊匹配用户输入的内容。displayStringForOption
:后续内容如何显示,通常会显示名称、标题之类的内容,也可以根据需要自定义显示内容,例如地址就可以将省市区和详细地址拼接起来。fieldViewBuilder
:输入框的构建函数,这个函数需要返回一个输入框供用户输入,输入框样式可以自定义。onSelected
:选中候选内容项的回调,通常我们会在这个方法里触发搜索请求。optionsMaxHeight
:候选项弹层的最大高度,超过这个高度后可以滚动。optionsViewBuilder
:候选项的弹层组件的构建函数,默认是使用 Material 风格的下拉列表视图组件,也可以自定义候选弹层的组件。initialValue
:初始值,比如根据用户近期浏览记录推荐一个候选的初始值。
实际应用
下面我们以一个输入国家检索的应用为例,我们准备了一个Country
类,其中有id
和name
字段。当检测到国家名称匹配用户输入的文字时,就会筛选出候选的国家,从而简化用户的输入过程。例如,用户输入“中”字,就会把“中国”这个候选项筛选出来。类似的场景也可以用于商品搜索推荐、支持输入的下拉框等等。实现的效果如下图所示。
示例代码如下:
class AutoCompleteDemo extends StatefulWidget {
const AutoCompleteDemo({super.key});
@override
// ignore: library_private_types_in_public_api
_AutoCompleteDemoState createState() => _AutoCompleteDemoState();
}
class _AutoCompleteDemoState extends State<AutoCompleteDemo> {
final List<Country> countries = [
Country(id: 1, name: '中国'),
Country(id: 2, name: '美国'),
Country(id: 3, name: '印度'),
Country(id: 4, name: '俄罗斯'),
Country(id: 5, name: '英国'),
Country(id: 6, name: '巴西'),
Country(id: 7, name: '法国'),
Country(id: 8, name: '德国'),
];
var selectedCountry = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AutoComplete Demo'),
),
body: Center(
child: Autocomplete<Country>(
optionsBuilder: (TextEditingValue textEditingValue) {
return Future.delayed(const Duration(milliseconds: 2000), () {
return countries
.where(
(country) => country.name.contains(textEditingValue.text))
.toList();
});
},
onSelected: (Country country) {
selectedCountry = country.name;
},
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextField(
controller: textEditingController,
focusNode: focusNode,
onChanged: (text) {
selectedCountry = text;
},
decoration: const InputDecoration(
labelText: '国家',
),
);
},
displayStringForOption: (Country country) =>
'[${country.id}] ${country.name}',
initialValue: TextEditingValue(text: selectedCountry),
),
),
);
}
}
class Country {
final int id;
final String name;
Country({required this.id, required this.name});
}
这里我们使用了Country
这个类作为AutoComplete
的泛型参数,这样就可以支持对Country
列表数据进行处理了。具体的逻辑说明如下:
optionsBuilder
:我们采用了一个Future
延时来模拟后台数据请求,当然,如果选项数据是固定的,可以进入页面的时候一次请求好就行。如果是要后台配合搜索的,那么每次输入都可以请求匹配的候选数据。这里实际处理就是筛选出国家名称中包含用户输入内容的列表。onSelected
:选中后的操作,这里只是更新选项值,实际如果是搜索商品就可以直接携带搜索关键词跳转到搜索结果页面。fieldViewBuilder
:这里用于构建一个输入框,输入框的样式可以根据自己需要定义。如果要单独控制输入框,比如输入内容,获取焦点等,可以定义一个TextEditingController
对象和FocusNode
对象来存储回调函数的textEditingController
和focusNode
。这两个参数是在AutoComplete
类里面定义的。displayStringForOption
:候选项的显示(包括选中后的文本框),这种可以用于将一个对象的几个字符串拼接起来,例如前面讲到的地址,这里我们演示是将id
和name
拼接起来了。initialValue
:输入框的初始值,如果是商品搜索,可以根据用户近期的浏览记录(或广告追踪记录)展示推荐的搜索内容,这个在电商App经常看到。
我是岛上码农,微信公众号同名。如有问题可以加本人微信交流,微信号:
island-coder
。👍🏻:觉得有收获请点个赞鼓励一下!
🌟:收藏文章,方便回看哦!
💬:评论交流,互相进步!