Flutter TypeAhead

一个 Flutter 的 TypeAhead (自动完成) 小部件,您可以在其中向用户显示键入时的建议

功能

  • 在其他小部件的顶部显示建议,浮动在叠加层中
  • 允许您通过构建器函数来指定建议的外观
  • 允许您指定用户点击建议时会发生什么
  • 接受传统TextField接受的所有参数,例如装饰、自定义TextEditingController、文本样式等。
  • 提供两个版本,一个普通版本和一个FormField版本,后者接受验证、提交等。
  • 提供高度的可定制性;您可以自定义建议框装饰、加载条、动画、去抖动持续时间等。

安装

请参阅pub上的安装说明

用法示例

您可以通过以下方式导入包

import 'package:flutter_typeahead/flutter_typeahead.dart';

对于Cupertino用户,请导入

import 'package:flutter_typeahead/cupertino_flutter_typeahead.dart';

像这样使用它

Material示例1

TypeAheadField(
  textFieldConfiguration: TextFieldConfiguration(
    autofocus: true,
    style: DefaultTextStyle.of(context).style.copyWith(
      fontStyle: FontStyle.italic
    ),
    decoration: InputDecoration(
      border: OutlineInputBorder()
    )
  ),
  suggestionsCallback: (pattern) async {
    return await BackendService.getSuggestions(pattern);
  },
  itemBuilder: (context, suggestion) {
    return ListTile(
      leading: Icon(Icons.shopping_cart),
      title: Text(suggestion['name']),
      subtitle: Text('\$${suggestion['price']}'),
    );
  },
  onSuggestionSelected: (suggestion) {
    Navigator.of(context).push(MaterialPageRoute(
      builder: (context) => ProductPage(product: suggestion)
    ));
  },
)

在上面的代码中,textFieldConfiguration属性允许我们按照需要配置显示的TextField。在此示例中,我们正在配置autofocusstyledecoration属性。

suggestionsCallback会根据用户输入的搜索字符串被调用,并应同步或异步返回数据列表。在此示例中,我们调用一个名为BackendService.getSuggestions的异步函数,该函数获取建议列表。

itemBuilder用于为每个建议构建一个小部件。在此示例中,我们构建一个简单的ListTile,它显示项目的名称和价格。请注意,您不应在此处提供onTap回调。TypeAhead小部件会处理此事。

onSuggestionSelected是用户点击建议时调用的回调。在此示例中,当用户点击建议时,我们会导航到一个显示被点击产品信息的页面。

Material示例2

这是另一个示例,我们在其中将TypeAheadFormField用于Form

final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _typeAheadController = TextEditingController();
String _selectedCity;
...
Form(
  key: this._formKey,
  child: Padding(
    padding: EdgeInsets.all(32.0),
    child: Column(
      children: <Widget>[
        Text(
          'What is your favorite city?'
        ),
        TypeAheadFormField(
          textFieldConfiguration: TextFieldConfiguration(
            controller: this._typeAheadController,
            decoration: InputDecoration(
              labelText: 'City'
            )
          ),          
          suggestionsCallback: (pattern) {
            return CitiesService.getSuggestions(pattern);
          },
          itemBuilder: (context, suggestion) {
            return ListTile(
              title: Text(suggestion),
            );
          },
          transitionBuilder: (context, suggestionsBox, controller) {
            return suggestionsBox;
          },
          onSuggestionSelected: (suggestion) {
            this._typeAheadController.text = suggestion;
          },
          validator: (value) {
            if (value.isEmpty) {
              return 'Please select a city';
            }
          },
          onSaved: (value) => this._selectedCity = value,
        ),
        SizedBox(height: 10.0,),
        RaisedButton(
          child: Text('Submit'),
          onPressed: () {
            if (this._formKey.currentState.validate()) {
              this._formKey.currentState.save();
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Your Favorite City is ${this._selectedCity}')
              ));
            }
          },
        )
      ],
    ),
  ),
)

在这里,我们将textFieldConfigurationcontroller属性设置为我们称为_typeAheadControllerTextEditingController。我们在onSuggestionSelected回调中使用此控制器来设置TextField的值为选定的建议。

validator回调可以像任何FormField.validator函数一样使用。在我们的示例中,它检查是否输入了值,如果没有则显示错误消息。onSaved回调用于将字段的值保存到_selectedCity成员变量。

transitionBuilder允许我们自定义建议框的动画。在此示例中,我们直接返回suggestionsBox,这意味着我们不希望有任何动画。

Cupertino示例

请参阅示例项目中的Cupertino代码。

已知问题

动画

将TypeAheadField放置在带有动画的小部件中可能会导致建议框的大小调整不正确。由于动画时间是可变的,因此必须在动画结束时手动更正。您需要添加下面描述的SuggestionsBoxController以及以下代码来处理AnimationController。

void Function(AnimationStatus) _statusListener;

@override
void initState() {
  super.initState();
  _statusListener = (AnimationStatus status) {
    if (status == AnimationStatus.completed ||
        status == AnimationStatus.dismissed) {
      _suggestionsBoxController.resize();
    }
  };

  _animationController.addStatusListener(_statusListener);
}

@override
  void dispose() {
    _animationController.removeStatusListener(_statusListener);
    _animationController.dispose();
    super.dispose();
}

对话框

打开对话框时存在一个已知问题,即建议框有时会显得太小。这是由上述动画引起的时序问题。目前,showDialog的动画持续时间为150毫秒。TypeAheadField将延迟170毫秒以弥补此问题。在能够使用上述解决方案正确检测和修复动画结束之前,此临时修复程序在大多数情况下都可以工作。如果建议框太小,关闭并重新打开键盘通常可以解决此问题。

Cupertino

TypeAhead中的Cupertino类仍是新的。Cupertino小部件与Material小部件之间也存在差异。在两者之间移动时,某些行为将不会转移。

自定义

TypeAhead小部件由一个TextField和一个在用户输入时显示的建议框组成。两者都高度可定制

自定义TextField

您可以使用textFieldConfiguration属性来自定义文本字段。您可以为此属性提供TextFieldConfiguration的实例,它允许您配置TextField的所有常规属性,如decorationstylecontrollerfocusNodeautofocusenabled等。

自定义建议框

TypeAhead为建议框提供默认配置。但是,您可以覆盖其中大部分。这是通过将SuggestionsBoxDecoration传递给suggestionsBoxDecoration属性来完成的。

使用SuggestionsBoxDecoration中的offsetX属性沿x轴移动建议框。您还可以将BoxConstraints传递给SuggestionsBoxDecoration中的constraints以调整建议框的宽度和高度。将两者结合使用将允许建议框几乎放置在任何地方。

自定义加载器、错误和“未找到项目”消息

您可以使用loadingBuildererrorBuildernoItemsFoundBuilder来自定义相应的窗口小部件。例如,要显示自定义错误小部件

errorBuilder: (BuildContext context, Object error) =>
  Text(
    '$error',
    style: TextStyle(
      color: Theme.of(context).errorColor
    )
  )

默认情况下,在检索新建议时,建议框将保留旧建议。要改为在检索期间显示圆形进度指示器,请将keepSuggestionsOnLoading设置为false。

隐藏建议框

在三种情况下可以隐藏建议框。

hideOnLoading设置为true以在检索建议时隐藏框。这将忽略loadingBuilder。将hideOnEmpty设置为true以在没有建议时隐藏框。这将忽略noItemsFoundBuilder。将hideOnError设置为true以在检索建议时出错时隐藏框。这将忽略errorBuilder

默认情况下,当键盘隐藏时,建议框会自动隐藏。要更改此行为,请将hideSuggestionsOnKeyboardHide设置为false。

自定义动画

您可以通过3个参数来自定义建议框动画:animationDurationanimationStarttransitionBuilder

animationDuration指定动画应持续多长时间,而animationStart指定动画应从哪个点(0.0到1.0之间)开始。transitionBuilder接受suggestionsBoxanimationController作为参数,并应返回一个使用animationController来为suggestionsBox的显示设置动画的小部件。例如

transitionBuilder: (context, suggestionsBox, animationController) =>
  FadeTransition(
    child: suggestionsBox,
    opacity: CurvedAnimation(
      parent: animationController,
      curve: Curves.fastOutSlowIn
    ),
  )

这使用FadeTransitionsuggestionsBox淡入视图。请注意,animationController如何作为动画的父级提供。

为了完全移除动画,transitionBuilder应该只返回suggestionsBox。此回调还可以用于用任何所需的窗口小部件包装suggestionsBox,而不一定是用于动画。

自定义去抖动持续时间

建议框不会为用户输入的每个字符触发。相反,我们等到用户空闲一段时间,然后调用suggestionsCallback。持续时间默认为300毫秒,但可以使用debounceDuration参数进行配置。

自定义建议框的偏移量

默认情况下,建议框显示在TextField下方5像素处。您可以通过更改suggestionsBoxVerticalOffset属性来更改此设置。

自定义建议框的装饰

您还可以使用suggestionsBoxDecoration属性来自定义建议框的装饰。例如,要删除建议框的阴影,您可以编写

suggestionsBoxDecoration: SuggestionsBoxDecoration(
  elevation: 0.0
)

自定义建议列表的生长方向

默认情况下,列表向底部生长。但是,您可以使用direction属性将生长方向自定义为AxisDirection.downAxisDirection.up之一,后者将导致列表向上生长,其中第一个建议位于列表底部,最后一个建议位于列表顶部。

autoFlipDirection设置为true,以允许建议列表在检测到当前方向没有足够空间时自动翻转方向。这对于TypeAheadField位于可滚动小部件中或开发人员希望确保列表始终可见(尽管用户屏幕尺寸不同)的情况很有用。

控制建议框

可以通过创建SuggestionsBoxController的实例并将其传递给suggestionsBoxController属性来实现建议框的手动控制。这将允许您手动打开、关闭、切换或调整建议框的大小。

更多信息

访问API文档

团队

AbdulRahman AlHamali S McDowall Kenneth Liang

感谢各位贡献者!

本项目是各位贡献者共同努力的结果,他们通过提交拉取请求、报告问题和回答问题来积极参与。感谢您的积极主动,我们希望flutter_typeahead能让您的生活至少轻松一点!

您如何提供帮助

贡献指南

GitHub

查看 Github