flutter_autocomplete
Mobile-first autocomplete for Flutter with focused constructors:
AutocompleteField.single<T>()AutocompleteField.multiple<T>()AutocompleteField.async<T>()AutocompleteField.asyncMultiple<T>()
The package is built for touch/virtual-keyboard UX, chip-based multiple mode, async loading, creatable options, visual grouping, and popup placement that stays stable through scrolling and viewport changes.
Installation
dependencies:
flutter_autocomplete: ^0.0.10
import 'package:flutter_autocomplete/flutter_autocomplete.dart';
Quick start
AutocompleteField<String>.single(
options: const ['Apple', 'Banana', 'Cherry'],
getOptionLabel: (option) => option,
decoration: const InputDecoration(
labelText: 'Fruit',
border: OutlineInputBorder(),
),
)
What's New In 0.0.10
- All
AutocompleteFieldconstructors now supporttextStylefor per-widget input text styling. - The style is forwarded to both single-field and chip-based multiple-field input renderers instead of requiring a surrounding
Themeoverride. - The release includes regression coverage for text-style forwarding and refreshed package docs for
0.0.10.
Demo
| 1) Single select (primitives) | 2) Single select (objects) | 3) Multiple chips |
|---|---|---|
![]() |
![]() |
![]() |
| 4) Creatable | 5) Grouped options | 6) Async search-as-type |
|---|---|---|
![]() |
![]() |
![]() |
| 7) Async combobox (load once) | 8) Async pagination | 9) Form validation |
|---|---|---|
![]() |
![]() |
![]() |
Constructor cheat sheet
| Constructor | Selection | Data source | Typical use |
|---|---|---|---|
single |
One value | Local list | Plain dropdown-like autocomplete |
multiple |
Many values | Local list | Chips/tag picker |
async |
One value | API/DB | Search-as-type or combobox |
asyncMultiple |
Many values | API/DB | Remote-backed chip picker |
Use case cookbook
1) Single select with primitive options
AutocompleteField<String>.single(
options: const ['Open', 'In Progress', 'Done'],
value: status,
onChanged: (value) => setState(() => status = value),
getOptionLabel: (option) => option,
)
2) Single select with objects + custom equality
Use isOptionEqualToValue when object identity may differ.
AutocompleteField<User>.single(
options: users,
value: selectedUser,
onChanged: (value) => setState(() => selectedUser = value),
getOptionLabel: (user) => user.fullName,
isOptionEqualToValue: (option, value) => option.id == value.id,
)
3) Multiple chips with fixed values
AutocompleteField<String>.multiple(
options: const ['Owner', 'Reviewer', 'Approver', 'Observer'],
values: selectedRoles,
onChanged: (values) => setState(() => selectedRoles = values),
getOptionLabel: (option) => option,
chipConfig: const AutocompleteChipConfig<String>(
fixedValues: ['Owner'],
limitTags: 3,
showHiddenCountChip: true,
),
behaviorConfig: const AutocompleteBehaviorConfig(
closeOnSelect: false,
clearInputOnSelect: true,
),
selectionConfig: const AutocompleteSelectionConfig<String>(
selectedBackgroundColor: Color(0xFFE8F5E9),
unselectedBackgroundColor: Color(0xFFFFF8E1),
),
)
4) Creatable options
AutocompleteField<String>.multiple(
options: const ['bug', 'feature', 'blocked'],
values: tags,
onChanged: (values) => setState(() => tags = values),
getOptionLabel: (option) => option,
creatableConfig: AutocompleteCreatableConfig<String>(
createOption: (input) => input.trim().toLowerCase(),
createLabel: (input) => 'Create tag "$input"',
),
)
5) Grouping (visual only)
Grouping changes popup rendering only.
AutocompleteField<City>.single(
options: cities,
getOptionLabel: (city) => city.name,
groupingConfig: AutocompleteGroupingConfig<City>(
groupBy: (city) => city.country,
sortGroups: true,
stickyHeaders: true,
),
)
6) Async search-as-type
AutocompleteField<String>.async(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: repository.searchCities,
debounceDuration: const Duration(milliseconds: 250),
minQueryLength: 2,
reloadOnQueryChange: true,
),
getOptionLabel: (option) => option,
)
7) Async combobox (load once, then local filtering)
Useful when backend returns a bounded dataset.
AutocompleteField<String>.async(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: repository.fetchAllCities,
loadOnFocus: true,
reloadOnQueryChange: false,
loadOnlyOnce: true,
searchOnEmptyQuery: false,
debounceDuration: Duration.zero,
),
getOptionLabel: (option) => option,
)
8) Async multiple (load once)
AutocompleteField<String>.asyncMultiple(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: repository.fetchAllLabels,
loadOnFocus: true,
reloadOnQueryChange: false,
loadOnlyOnce: true,
searchOnEmptyQuery: false,
),
values: selectedLabels,
onChanged: (values) => setState(() => selectedLabels = values),
getOptionLabel: (option) => option,
behaviorConfig: const AutocompleteBehaviorConfig(
closeOnSelect: false,
clearInputOnSelect: true,
),
)
9) External patching from parent/form state
AutocompleteField<String>.asyncMultiple(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: repository.fetchAllLabels,
loadOnFocus: true,
reloadOnQueryChange: false,
loadOnlyOnce: true,
),
values: selectedLabels,
onChanged: (values) {
setState(() {
selectedLabels
..clear()
..addAll(values);
});
},
getOptionLabel: (option) => option,
)
// Later, patch externally without changing the widget key:
setState(() {
selectedLabels
..clear()
..addAll(['Urgent', 'Backend']);
});
10) Async pagination
AutocompleteField<String>.async(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: (_) async => const [],
loadOnFocus: true,
paginationConfig: AutocompleteAsyncPaginationConfig<String>(
pageSize: 20,
optionsPageBuilder: (query, page, pageSize) {
return repository.fetchPage(query: query, page: page, size: pageSize);
},
showEndOfListIndicator: true,
),
),
getOptionLabel: (option) => option,
)
11) Form validation and save
final formKey = GlobalKey<FormState>();
Form(
key: formKey,
child: Column(
children: [
AutocompleteField<String>.single(
options: const ['Low', 'Medium', 'High'],
getOptionLabel: (option) => option,
validator: (value) => value == null ? 'Priority is required' : null,
onSaved: (value) => priority = value,
),
AutocompleteField<String>.multiple(
options: const ['UI', 'Backend', 'QA'],
getOptionLabel: (option) => option,
validator: (values) {
if (values == null || values.isEmpty) {
return 'Select at least one team';
}
return null;
},
onSaved: (values) => teams = values ?? <String>[],
),
],
),
)
12) Selected-value start adornment
Use startAdornmentBuilder when you want a leading widget for the selected value while still keeping the real TextField mounted. This works well for async single fields that need to refocus, reload, or keep showing loading state.
AutocompleteField<String>.async(
asyncConfig: AutocompleteAsyncConfig<String>(
optionsBuilder: repository.fetchAllLabels,
loadOnFocus: true,
reloadOnQueryChange: false,
loadOnlyOnce: true,
),
value: selectedLabel,
onChanged: (value) => setState(() => selectedLabel = value),
getOptionLabel: (option) => option,
renderingConfig: AutocompleteRenderingConfig<String>(
startAdornmentBuilder: (context, value, label) {
return Padding(
padding: const EdgeInsets.only(right: 8),
child: Chip(label: Text(label)),
);
},
),
)
13) Custom text style
Use textStyle when you want to style the input text for one field only without changing the app-wide Theme.
AutocompleteField<String>.single(
options: const ['Apple', 'Banana', 'Cherry'],
value: selectedFruit,
onChanged: (value) => setState(() => selectedFruit = value),
getOptionLabel: (option) => option,
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.deepOrange,
),
decoration: const InputDecoration(
labelText: 'Styled fruit',
border: OutlineInputBorder(),
),
)
14) Custom rendering
AutocompleteField<String>.multiple(
options: const ['Apple', 'Banana', 'Cherry'],
getOptionLabel: (option) => option,
renderingConfig: AutocompleteRenderingConfig<String>(
optionBuilder: (context, option) {
return ListTile(
title: Text(option.label),
trailing: option.isSelected
? const Icon(Icons.check, size: 18)
: null,
);
},
),
)
15) Disabled options
AutocompleteField<String>.single(
options: const ['Open', 'Closed', 'Archived'],
getOptionLabel: (option) => option,
isOptionDisabled: (option) => option == 'Archived',
)
16) Disabled vs read-only interaction states
Use enabled: false when the field should be disabled and excluded from normal form interaction. Use readOnly: true when it should keep enabled styling but block edits, popup selection changes, clearing, and chip deletion.
AutocompleteField<String>.multiple(
options: const ['Apple', 'Banana', 'Cherry'],
values: const ['Apple', 'Banana'],
onChanged: (_) {},
readOnly: true,
getOptionLabel: (option) => option,
decoration: const InputDecoration(
labelText: 'Locked fruit list',
helperText: 'Users can review the values, but cannot change them.',
border: OutlineInputBorder(),
),
)
Async behavior reference
AutocompleteAsyncConfig gives these common patterns:
- Search-as-type:
reloadOnQueryChange: true - Load on focus:
loadOnFocus: true - One request only:
loadOnlyOnce: true - Ignore whitespace-only input:
searchOnEmptyQuery: false - Local filtering after first load:
reloadOnQueryChange: false
Useful config groups
AutocompleteBehaviorConfig: focus/open/close/clear behavior.AutocompleteFilterConfig: matching strategy and custom filter.AutocompleteSelectionConfig: keep selected rows visible, customize selected and unselected row backgrounds, and control indicator behavior.AutocompleteChipConfig: chip layout, fixed values, hidden count, max chip area height.AutocompletePopupConfig: popup size/surface styling.AutocompleteRenderingConfig: option/selected/loading/empty custom builders.
Example app
A full runnable showcase exists in example/lib/main.dart.
It includes:
- Single and multiple local constructors.
- Creatable and grouped flows.
- Async search-as-type.
- Async load-once combobox and async multiple.
- Async pagination.
- Form validation and save flows.
- Selected-value start adornment examples.
- Per-field input text styling examples.
- Disabled and read-only interaction state examples.
Accessibility and platform scope
- Uses Flutter text/chip/tap semantics.
- Validation errors are rendered via
InputDecoration. - Mobile-first behavior.
Libraries
- flutter_autocomplete
- Public library exports for the
flutter_autocompletepackage.








