anchored_dropdown_field 0.1.0
anchored_dropdown_field: ^0.1.0 copied to clipboard
Animated, anchored Flutter dropdown fields with forms, single and multiple selection, local filtering, async search, and customizable menus.
anchored_dropdown_field #
An animated and form-aware Flutter dropdown built on MenuAnchor. It supports
single and multiple selection, local filtering, debounced asynchronous search,
validation, and extensive presentation customization.
Features #
- Controlled and
FormFieldAPIs for single and multiple selection. - Menus that match the field width and grow only as tall as their content.
- Local search across labels, descriptions, and semantic labels.
- Debounced asynchronous search with stale-response protection.
- Loading, error, retry, empty, header, footer, and separator customization.
- Custom item, selected-value, indicator, icon, and state styles.
- External focus, menu, and search controllers when advanced control is needed.
- Material theming, keyboard focus, hover behavior, and accessibility semantics.
- Configurable open, close, and indicator animations.
Installation #
Add the package to pubspec.yaml:
dependencies:
anchored_dropdown_field: ^0.1.0
Basic usage #
import 'package:anchored_dropdown_field/anchored_dropdown_field.dart';
DropdownFormField<String>(
value: selectedRole,
decoration: const InputDecoration(labelText: 'Role'),
items: const [
DropdownItem(value: 'design', label: 'Designer'),
DropdownItem(value: 'engineering', label: 'Engineer'),
],
onChanged: (value) => setState(() => selectedRole = value),
validator: (value) => value == null ? 'Select a role' : null,
)
DropdownField provides the same presentation without form validation.
Local search #
Enable default local filtering with one optional configuration object:
DropdownFormField<Country>(
items: countries
.map(
(country) => DropdownItem(
value: country,
label: country.name,
description: country.region,
),
)
.toList(),
search: const DropdownSearchConfig<Country>(),
onChanged: (country) => setState(() => selectedCountry = country),
)
Use filter when matching depends on fields that are not displayed:
search: DropdownSearchConfig<Country>(
filter: (item, query) {
final normalized = query.toLowerCase();
return item.label.toLowerCase().contains(normalized) ||
item.value.isoCode.toLowerCase().contains(normalized);
},
),
Asynchronous search #
Supplying onSearch changes the search source from local items to asynchronous
results. Requests are debounced and late responses from older queries are
ignored automatically.
DropdownFormField<Person>(
value: selectedPerson,
items: const [],
search: DropdownSearchConfig<Person>(
minimumQueryLength: 2,
debounceDuration: const Duration(milliseconds: 350),
onSearch: (query) async {
final people = await directory.search(query);
return people.map(
(person) => DropdownItem(
value: person,
label: person.name,
description: person.email,
),
);
},
errorText: 'Could not load people',
retryText: 'Try again',
),
onChanged: (person) => setState(() => selectedPerson = person),
)
The search decoration, controller, focus node, loading widget, error widget, query callbacks, minimum query length, and result retention are configurable.
Multiple selection #
MultiDropdownFormField<String>(
values: selectedSkills,
maxSelections: 4,
decoration: const InputDecoration(labelText: 'Skills'),
items: const [
DropdownItem(value: 'dart', label: 'Dart'),
DropdownItem(value: 'flutter', label: 'Flutter'),
DropdownItem(value: 'design', label: 'Product design'),
],
search: const DropdownSearchConfig<String>(),
onChanged: (values) => setState(() => selectedSkills = values),
)
Multiple-selection menus remain open by default. Set closeOnSelection when a
different interaction is preferred.
Styling #
DropdownFieldStyle controls the menu, options, dimensions, overlay behavior,
and animations. Per-item styles and builders can override global defaults.
DropdownFormField<String>(
style: const DropdownFieldStyle(
menuMaxHeight: 320,
indicatorAnimationDuration: Duration(milliseconds: 180),
selectedItemStyle: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Color(0xFFE8F2FF)),
),
),
itemBuilder: (context, item, state) => Text(item.label),
selectionIndicatorBuilder: (context, item, state) =>
state.selected ? const Icon(Icons.check_rounded) : null,
items: const [
DropdownItem(value: 'one', label: 'One'),
DropdownItem(value: 'two', label: 'Two'),
],
onChanged: (value) {},
)
See the included example for local search, asynchronous search, validation, and multiple selection in one application.