下面是动态搜索的效果展示:
1. Autocomplete 组件最简代码
我们先一步步来了解 Autocomplete
组件,先实现如下的最简代码:
使用 Autocomplete
时,必须提供的是 optionsBuilder
参数,另外可以通过 onSelected
回调来监听选中的条目。
Autocomplete<String>(
optionsBuilder: buildOptions,
onSelected: onSelected,
)
optionsBuilder
是一个 AutocompleteOptionsBuilder<T>
类型的函数,从下面的定义中可以发现,该函数会回调 TextEditingValue
对象,且返回 FutureOr<Iterable<T>>
。这说明这个函数是一个异步函数,我们可以在此进行网络请求,数据库查询等工作,来返回一个 Iterable<T>
的可迭代对象。
用脚指头想一下也知道,这个可迭代对象,就决定这输入框下面的联想词是哪些。
代码语言:javascript复制final AutocompleteOptionsBuilder<T> optionsBuilder;
typedef AutocompleteOptionsBuilder<T extends Object> =
FutureOr<Iterable<T>> Function(TextEditingValue textEditingValue);
比如下面通过 searchByArgs
模拟网络请求,通过 args
参数搜索数据,
Future<Iterable<String>> searchByArgs(String args) async{
// 模拟网络请求
await Future.delayed(const Duration(milliseconds: 200));
const List<String> data = [
'toly', 'toly49', 'toly42', 'toly56',
'card', 'ls', 'alex', 'fan sha',
];
return data.where((String name) => name.contains(args));
}
这样,buildOptions
的逻辑如下,这就完成了 输入--> 搜索 --> 展示联想词
的流程。这也是 Autocomplete
组件最简单的使用。
Future<Iterable<String>> buildOptions( TextEditingValue textEditingValue ) async {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return searchByArgs(textEditingValue.text);
}
2. 自定义 Autocomplete 组件内容
其实上面那样的默认样式很丑,而且没有提供 直接
的属性设置样式。所以了解如何自定义是非常关键的,否则只是一个玩具罢了。如下,我们先来实现搜索高亮显示的自定义,其中也包括对输入框的自定义。
Autocomplete
中提供了 fieldViewBuilder
和 optionsViewBuilder
分别用于构造输入框
和 浮层面板
。
如下,代码中通过 _buildOptionsView
和 _buildFieldView
进行相应组件构造:
Autocomplete<String>(
optionsBuilder: buildOptions,
onSelected: onSelected,
optionsViewBuilder: _buildOptionsView,
fieldViewBuilder: _buildFieldView,
);
如下是 _buildOptionsView
方法的实现,其中会回调 onSelected
回调函数,和 options
数据,我们需要做的就是依靠数据,构建组件进行展示即可。另外,默认浮层面板和输入框底部平齐,可以通过 Padding
进行下移。另外,由于是浮层,展示文字时,上面需要嵌套 Material
组件。
至于高亮某个关键字,下面是我封装的一个小方法,拿来即用。
代码语言:javascript复制---->[高亮某些文字]----
final TextStyle lightTextStyle = const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
);
InlineSpan formSpan(String src, String pattern) {
List<TextSpan> span = [];
List<String> parts = src.split(pattern);
if (parts.length > 1) {
for (int i = 0; i < parts.length; i ) {
span.add(TextSpan(text: parts[i]));
if (i != parts.length - 1) {
span.add(TextSpan(text: pattern, style: lightTextStyle));
}
}
} else {
span.add(TextSpan(text: src));
}
return TextSpan(children: span);
}
另外,对于输入框的构建,通过如下的 _buildFieldView
实现,其中有 _controller
记录一下 TextEditingController
,是因为 optionsViewBuilder
回调中并没有回调输入的 arg
字符,所以想要输入的关键字高亮,只能出此下策。这样,在 TextFormField
构建时,你可以指定自己需要的装饰。
到此,我们就实现了上面,输入过程中,浮层面板内容关键字高亮显示的效果。
3.关于 Autocomplete 中的泛型
泛型的作用非常明显,它最主要的是对浮层面板的构建,如果浮层中的条目不止是 String
,我们就需要使用泛型,来提供某个的数据类型。比如下面的效果,其中浮层面板的条目是可以显示更多的信息:
先定义一个数据类 User
,记录信息:
class User {
final String name;
final bool man;
final String image;
const User(this.name, this.man, this.image);
@override
String toString() {
return 'User{name: $name, man: $man, image: $image}';
}
}
然后在 Autocomplete
的泛型中使用 User
即可。
这样在 _buildOptionsView
中,回调的就是 User
的可迭代对象。如下。封装一个 _UserItem
组件,对条目进行显示。
4、Autocomplete 源码简看
Autocomplete
本质上依赖于 RawAutocomplete
组件进行构建,可见它是一层简单的封装,简化使用。为我们提供了默认的 optionsViewBuilder
和 fieldViewBuilder
,显示一个很丑的界面。也就是说,如果你了解如何定制这两部分内容,你也就会了 RawAutocomplete
组件。
我们先看一下 Autocomplete
对 optionsViewBuilder
提供的默认显示,其返回的是 _AutocompleteOptions
组件。如下,其实和我们自己实现的也没有太大的区别,只是个默认存在,方便使用的小玩意而已。
另外,对于输入框的构建,使用 _defaultFieldViewBuilder
静态方法完成。
该方法,返回 _AutocompleteField
组件,本质上也就是构建了一个 TextFormField
组件。
对 Autocomplete
来说,只是 RawAutocomplete
套了个马甲,本质上的功能还是在 RawAutocomplete
的状态类中完成的。如下是 _RawAutocompleteState
的部分代码,可以看出这里的浮层面板,是通过 Overlay
实现的,另外通过 CompositedTransformTarget
和 CompositedTransformFollower
对浮层进行定位。
那本文就这样,如果想要简单地实现搜索联想词,Autocomplete
是一个很不错的选择。