最近项目中在实现一个搜索的功能,根据 Flutter
的类似组件的调用习惯,输入 showSearch
后发现还真有,跳进源码中一看,Flutter
已经实现了相应的 Widget
和交互,简直不要太方便了,先来看看如何调用的。
showSearch
方法介绍
1 | Future<T?> showSearch<T>({ |
上面函数定义在源码 flutter/lib/src/material/search.dart
文件中,根据该函数要求须传入一个 context
和 delegate
,context
是我们的老朋友,这里就无需过多介绍了。但是这个 delegate
(SearchDelegate
类)是干啥的?继续跳到 SearchDelegate
发现SearchDelegate
是一个抽象类,SearchDelegate
第一句介绍 Delegate for [showSearch] to define the content of the search page.
定义搜索页面的内容,也就是说需要我们创建一个继承自 SearchDelegate
的子类来实例化参数 delegate
,下面是这个子类CustomSearchPageDelegate
的代码。
1 | class CustomSearchPageDelegate extends SearchDelegate<String> { |
从上面可以看出我们需要返回4个 Widget
来显示内容,其中 buildLeading
和 buildActions
分别对应搜索框左右两边的内容,通常是 button
,如 buildLeading
是返回按钮,buildActions
右边是搜索按钮。buildResults
则表示搜索的结果展示,通常是一个列表,而 buildSuggestions
展示当用户在输入框输入内容时给出的提示,展示多条提示内容时也会用到列表(ListView
)。
实现 CustomSearchPageDelegate
接下来以搜索文章为例子利用自定义的CustomSearchPageDelegate
类实现一下搜索功能。
1 | import 'package:flutter/material.dart'; |
这里要说明一下,query
关键字是输入框的文本内容。调用的时候实例化一下该类,传递给 shwoSearch
的 delegate
参数。下图就是我们看到的效果:
总结问题
以上图片的搜索框还可以通过重写 appBarTheme
来定制自己想要的 UI
效果,虽然可以这样,但是和我们要实现的效果比起来还相差甚远,尤其是顶部的搜索框,其左右两边的留白区域过多,背景颜色无法调整,内部的输入框 TextField
也无法定制自己想要的效果,如不能调整其圆角、背景颜色以及添加额外控件等等。
还有一点就是当我们点击返回按钮调用 close
时,这里返回值是泛型 T
却不支持 null
类型,在文章的开头,我们可以看到 shwoSearch
的 delegate
参数类型是 SearchDelegate<T>
,所以创建 CustomSearchPageDelegate
时必须这样去声明。
1 | class CustomSearchPageDelegate extends SearchDelegate<DataItemModel> |
而我们想要实现这样去声明
1 | class CustomSearchPageDelegate extends SearchDelegate<DataItemModel?> |
这样当我们调用 close
时可以做到传 null
,在外面调用的位置可以对返回值进行判断,返回值为 null
就不作任何处理。
交互上,在点击键盘上的 搜索
按键时,直接调用的是 showResults
函数,而通常的操作是需要调用搜索的接口拿到数据后,再去调用 showResults
函数来展示搜索结果的数据。
对于上述问题,我们可以做什么呢?
源码分析
想要到达我们需要的效果,还是需要看看 Flutter
的源码是怎么实现的,我们再次来到 flutter/lib/src/material/search.dart
文件中,可以看到该文件中定义了除上面提到的抽象类 SearchDelegate
和全局函数 showSearch
之外,还有内部类 _SearchPageRoute
和 _SearchPage
。 _SearchPageRoute
继承自 PageRoute
,顾名思义就是负责路由跳转及转场动画的。
以下是 _SearchPageRoute
部分代码:
1 | class _SearchPageRoute<T> extends PageRoute<T> { |
重写父类的 buildPage
方法,将 delegate
传递给 _SearchPage
并将其返回,而所有的 UI
逻辑都在这个 _SearchPage
中,来到 _SearchPage
的 build
函数中就可以看到下面的实现。
_SearchPage
的 build
函数代码
1 |
|
_SearchPage
中的实现也非常简单,就是一个嵌入到 AppBar
中的搜索框和呈现 suggestion list
及 result list
的 body
。想要定制自己的 UI
效果,改的也是该位置的代码。
优化实现
UI
方面主要针对 TextField
和 AppBar
代码修改,怎么改就看想要实现什么效果了。参考 Flutter
官方的源码,重新实现一个的 _SearchPage
类,然后在 _SearchPageRoute
替换成自己写的 _SearchPage
,再去 SearchDelegate
替换一下修改过的 _SearchPageRoute
。
还一个问题怎么实现调用 close
时可以返回 null
的结果内呢?除了上面提到的这样去声明
1 | class CustomSearchPageDelegate extends SearchDelegate<DataItemModel?> |
之外,还需要修改 _SearchPageRoute
。
1 | // 改之后 |
重新定义一个全局函数 showSearchWithCustomiseSearchDelegate
,和官方的区分开来。
1 | Future<T?> showSearchWithCustomiseSearchDelegate<T>({ |
来看看最终调用上面的函数
1 | DataItemModel? result = |
解决交互上的问题,需要在我们自己抽象类 SearchDelegate
单独定义一个函数 onSubmit
,点击键盘上的搜索按键和右边的搜索按钮调用 onSubmit
函数,如:widget.delegate.onSubmit(context, text);
,在 SearchDelegate
子类的 onSubmit
中来实现具体的逻辑,如发送网络请求,返回数据后在调用 showResults
。
1 |
|
整体实现的代码量多,就不在文中贴出来了,具体实现大家可以参考这里的代码:
1 | https://github.com/joedrm/flutter_todo/blob/master/lib/pages/search_page/search_page_delegate.dart |
下图是最终实现效果:
小结
自定义搜索框的实现整体来说还是比较简单的,相比于源码改动的地方并不多,就可以显示想要的效果。当然还有其它更多的实现方式,这里只是提供了一种分析思路。我们还可以发散一下,去看看其它的如:showBottomSheet
、showDialog
等等和 SearchDelegate
,他们直之间也有不少类似的地方,当我想要自定义自己的控件时,会发现其实很多答案就在官方的源码里,动手改吧改吧就出来了。最后聊一下近况,近期有一些想法在忙着实现,时间有点安排不过来,文章的更新就有点儿偷懒了,跟大家说声抱歉,后面有机会单独来分享一下最近忙的事情,最后感谢大家耐心的阅读!
v1.5.2