Multi Select
A multi select displays a list of drop-down options for the user to pick from.
It is a form-field and can therefore be used in a form.
For single selections, consider using a select.
For touch devices, a select tile group or select menu tile is generally recommended over this.
Preview
CLI
To generate and customize this style:
dart run forui style create multi-select
Usage
FMultiSelect(...)
FMultiSelect<Locale>(
items: {
'United States': Locale('en', 'US'),
'Canada': Locale('en', 'CA'),
'Japan': Locale('ja', 'JP'),
},
controller: FMultiSelectController<Locale>(vsync: this, min: 1, max: 2),
style: FMultiSelectStyle.inherit(...),
label: const Text('Country'),
description: const Text('Select your country of residence'),
hint: Text('Choose a country'),
keepHint: true,
format: (value) => Text(value.toUpperCase()),
sort: (a, b) => a.compareTo(b),
onChange: (value) => print('Selected country: $value'),
onSaved: (value) => print('Saved country: $value'),
autovalidateMode: AutovalidateMode.onUserInteraction,
prefixBuilder: (context, styles) => Icon(FIcons.globe),
suffixBuilder: (context, styles) => Icon(FIcons.arrowDown),
tagBuilder: (context, controller, styles, value, label) => FMultiSelectTag(label: label),
popoverConstraints: const FAutoWidthPortalConstraints(maxHeight: 400),
clearable: true,
contentScrollHandles: true,
min: 1,
max: 2,
initialValue: {Locale('en', 'US')},
);
FMultiSelect.rich(...)
FMultiSelect<String>.rich(
controller: FMultiSelectController<String>(vsync: this, min: 1, max: 2),
style: FMultiSelectStyle(...),
label: const Text('Country'),
description: const Text('Select your country of residence'),
hint: Text('Choose a country'),
keepHint: true,
format: (value) => Text(value.toUpperCase()),
sort: (a, b) => a.compareTo(b),
onChange: (value) => print('Selected country: $value'),
onSaved: (value) => print('Saved country: $value'),
autovalidateMode: AutovalidateMode.onUserInteraction,
prefixBuilder: (context, styles) => Icon(FIcons.globe),
suffixBuilder: (context, styles) => Icon(FIcons.arrowDown),
tagBuilder: (context, controller, styles, value, label) => FMultiSelectTag( label: label),
popoverConstraints: const FAutoWidthPortalConstraints(maxHeight: 400),
clearable: true,
contentDivider: FItemDivider.none,
contentScrollHandles: true,
min: 1,
max: 2,
initialValue: {'ca'},
children: [
FSelectSection.rich(
label: const Text('North American Countries'),
divider: FItemDivider.none,
children: [
FSelectItem( title: const Text('United States'), value: 'us'),
FSelectItem(title: Text('Canada'), value: 'ca'),
],
),
FSelectItem(title: Text('Japan'), value: 'jp'),
],
);
FMultiSelect.search(...)
FMultiSelect<User>.search(
items: {
'Bob Ross': User(firstName: 'Bob', lastName: 'Ross'),
'John Doe': User(firstName: 'John', lastName: 'Doe'),
'Mary Jane': User(firstName: 'Mary', lastName: 'Jane'),
'Peter Parker': User(firstName: 'Peter', lastName: 'Parker'),
},
controller: FMultiSelectController<User>(vsync: this, min: 1, max: 2),
style: FMultiSelectStyle.inherit(...),
label: const Text('User'),
description: const Text('Search and select a user'),
format: (user) => Text('${user.firstName} ${user.lastName}'),
sort: (a, b) => a.compareTo(b),
hint: Text('Search users...'),
popoverConstraints: const FAutoWidthPortalConstraints(maxHeight: 400),
clearable: true,
onChange: (value) => print('Selected country: $value'),
tagBuilder: (context, controller, styles, value, label) => FMultiSelectTag( label: label),
min: 1,
max: 2,
initialValue: {'value'},
contentDivider: FItemDivider.none,
contentScrollHandles: false,
contentPhysics: const BouncingScrollPhysics(),
contentEmptyBuilder: (context, style) => Text('No results'),
filter: (query) async {
// Fetch users based on search query
return fetchUsers(query);
},
contentBuilder: (context, _, users) => [
for (final user in users)
FSelectItem(title: Text('${user.firstName} ${user.lastName}'), value: user),
],
searchFieldProperties: FSelectSearchFieldProperties(
autofocus: true,
hint: 'Type to search...',
),
contentLoadingBuilder: (context, style) => Text('Loading...'),
contentErrorBuilder: (context, error, stackTrace) => Text('Error...'),
);
FMultiSelect.searchBuilder(...)
FMultiSelect<User>.search(
controller: FMultiSelectController<User>(vsync: this, min: 1, max: 2),
style: FMultiSelectStyle.inherit(...),
label: const Text('User'),
description: const Text('Search and select a user'),
format: (user) => Text('${user.firstName} ${user.lastName}'),
sort: (a, b) => a.compareTo(b),
hint: Text('Search users...'),
tagBuilder: (context, controller, styles, value, label) => FMultiSelectTag( label: label),
popoverConstraints: const FAutoWidthPortalConstraints(maxHeight: 400),
clearable: true,
onChange: (value) => print('Selected country: $value'),
min: 1,
max: 2,
initialValue: {'value'},
contentDivider: FItemDivider.none,
contentScrollHandles: false,
contentPhysics: const BouncingScrollPhysics(),
contentEmptyBuilder: (context, style) => Text('No results'),
filter: (query) async {
// Fetch users based on search query
return fetchUsers(query);
},
contentBuilder: (context, _, users) => [
for (final user in users)
FSelectItem(title: Text('${user.firstName} ${user.lastName}'), value: user),
],
searchFieldProperties: FSelectSearchFieldProperties(
autofocus: true,
hint: 'Type to search...',
),
contentLoadingBuilder: (context, style) => Text('Loading...'),
contentErrorBuilder: (context, error, stackTrace) => Text('Error...'),
);
Examples
Detailed
Preview
Sections
Preview
Dividers
Preview
Searchable
Sync
Preview
Async
Preview
Async with Custom Loading
Preview
Async with Custom Error Handling
Preview
Behavior
Clearable
Preview
Custom Formatting
Preview
Min & Max
Preview
With Scroll Handles
Preview
Sorted
Preview
Form
Preview
Last updated on