multi_data_picker 0.2.0
multi_data_picker: ^0.2.0 copied to clipboard
A flexible multi-select widget for Flutter with popup picker, controller support, and TextField styling.
๐งฎ MultiDataPicker #
multi_data_picker is a customizable multi-select widget for Flutter, inspired by the UX of Select2 and architecturally based on the any_field pattern. It allows users to select multiple values from dynamic data sources with support for generic types, popup customization (dialog, page, bottom), and external state control via MultiPickerController.
Designed to integrate seamlessly into form layouts, it supports full TextField styling via InputDecoration, making it visually consistent with standard Flutter input fields. Ideal for filter panels, tag selectors, and data-driven workflows.
โจ Features #
- โ
Generic support for any data type
T - ๐งฑ Custom display builder (text or widget)
- ๐ช Popup picker:
dialog,page, orbottom - ๐๏ธ Controller support for external state management
- ๐งฉ TextField-style appearance via
InputDecoration - ๐ Initial value and change detection hooks
- ๐ Layout, spacing, and tile styling options
๐ธ Demo #
| Fix Height | Dialog Picker | Page Picker |
|---|---|---|
| [Fix Height Bottom] | [Dialog Picker] | [Page Picker] |
| Custom | Use Controller | ------------- |
| [Custom] | [Use Controller] |
๐ฆ Installation #
Add this to your pubspec.yaml:
dependencies:
multi_data_picker: ^<latest_version>
Then run:
flutter pub get
๐ Usage #
MultiDataPicker<Map<String, dynamic>>(
initialValue: [data[0], data[1]],
isEqual: (a, b) => a["id"] == b["id"],
decoration: InputDecoration(
labelText: "Input Label",
prefixIcon: Icon(Icons.location_city),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
),
),
minHeight: 100,
maxHeight: 150,
dataDisplayBuilder: DataDisplayBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
),
popupType: DataPickerPopupType.bottom(
loadData: loadData,
listDataBuilder: ListDataBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
),
),
)
๐งฑ Constructor Parameters #
| Parameter | Type | Description |
|---|---|---|
dataDisplayBuilder |
DataDisplayBuilder<T> |
Defines how each selected item is rendered (see details) |
popupType |
DataPickerPopupType<T> |
Controls the popup dialog behavior (see details) |
isEqual |
bool Function(T a, T b)? |
Optional equality function for comparing items. Required when controller not exist |
controller |
MultiPickerController<T>? |
Optional controller to manage selected values (see details) |
initialValue |
List<T>? |
Initial selected values |
decoration |
InputDecoration |
Input field decoration |
onChanged |
Function(List<T>)? |
Callback when selection changes |
displaySpacing |
double |
Horizontal spacing between items |
displayRunSpacing |
double |
Vertical spacing between wrapped items |
minHeight / maxHeight |
double? |
Height constraints |
๐งฉ Display Builder #
The DataDisplayBuilder<T> sealed class defines how each selected item is rendered inside the picker. It supports two modes:
1. DataDisplayBuilder.string #
Renders each item as a Chip with customizable appearance.
| Parameter | Type | Description |
|---|---|---|
labelBuilder |
String Function(T data) |
Required. Builds the label text for each item |
backgroundColor |
Color? |
Background color of the chip |
labelStyle |
TextStyle? |
Style applied to the label text |
shape |
OutlinedBorder? |
Shape of the chip |
deleteIconColor |
Color? |
Color of the delete icon |
deleteIcon |
Widget? |
Custom delete icon widget |
avatar |
Widget? |
Avatar widget displayed on the chip |
avatarBoxConstraints |
BoxConstraints? |
Constraints for the avatar widget |
labelPadding |
EdgeInsetsGeometry? |
Padding around the label text |
side |
BorderSide (default: none) |
Border side configuration |
Example:
DataDisplayBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
backgroundColor: Colors.blue.shade50,
labelStyle: TextStyle(fontWeight: FontWeight.bold),
shape: StadiumBorder(),
deleteIconColor: Colors.red,
deleteIcon: Icon(Icons.close),
avatar: CircleAvatar(child: Text("A")),
avatarBoxConstraints: BoxConstraints.tight(Size(24, 24)),
labelPadding: EdgeInsets.symmetric(horizontal: 8),
side: BorderSide(color: Colors.grey),
)
2. DataDisplayBuilder.custom #
Provides full control over rendering via a custom widget builder.
| Parameter | Type | Description |
|---|---|---|
builder |
Widget Function(BuildContext context, T data, void Function() delete) |
Required. Custom widget builder for rendering each item with delete support |
Example:
DataDisplayBuilder.custom(
builder: (context, data, delete) {
return Row(
children: [
Text(data['name']),
IconButton(
icon: Icon(Icons.delete),
onPressed: delete,
),
],
);
},
)
Use this when you need advanced layout or interactivity beyond the default Chip UI.
๐ช Popup Types #
The popupType parameter defines how the selection UI is presented. It uses a sealed class DataPickerPopupType<T> with three variants:
1. dialog #
Displays a modal dialog with optional title, search input, and action buttons.
DataPickerPopupType.dialog(
loadData: loadData,
listDataBuilder: ListDataBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
),
titleText: "Dialog Title",
)
2. page #
Displays a full-screen modal page.
DataPickerPopupType.page(
loadData: loadData,
listDataBuilder: ListDataBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
),
titleText: 'Choose Options',
withCancelButton: true,
)
3. bottom #
Shows a bottom sheet for quick, contextual selection.
DataPickerPopupType.bottom(
loadData: loadData,
listDataBuilder: ListDataBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
),
titleText: 'Choose Options',
withCancelButton: true,
)
๐ช Popup Type Parameters #
| Parameter | Type | Description | Used In |
|---|---|---|---|
loadData |
ListDataPickerCallback<T> |
Required. Callback to load data with filter and previous selection | All |
listDataBuilder |
ListDataBuilder<T> |
Required. Builder for rendering list items (see details) | All |
title |
Widget? |
Optional widget for the popup title | All |
titleText |
String? |
Optional text for the popup title | All |
cancelText |
String? |
Text for the cancel button | All |
selectText |
String? |
Text for the select button | All |
cancelButtonStyle |
ButtonStyle? |
Style for the cancel button | All |
selectButtonStyle |
ButtonStyle? |
Style for the select button | All |
searchInputDecoration |
InputDecoration? |
Decoration for the search input field | All |
searchAutoFocus |
bool (default varies by type) |
Whether the search input should autofocus | All |
width |
double? |
Width of the popup dialog | dialog only |
height |
double? |
Height of the popup dialog | dialog, bottom |
withCancelButton |
bool (default varies by type) |
Whether to show a cancel button | page, bottom |
Notes:
searchAutoFocusdefaults tofalsefordialogandbottom, andtrueforpage.withCancelButtondefaults tofalsefor bothpageandbottom.widthis only available indialog;heightis available indialogandbottom.
๐งพ List Data Builder #
The ListDataBuilder<T> sealed class defines how each item in the popup selection list is rendered. It supports two modes:
1. ListDataBuilder.string #
Displays each item as a simple text-based ListTile.
ListDataBuilder.string(
labelBuilder: (data) => data['name'] ?? "-",
)
2. ListDataBuilder.custom #
Provides full control over rendering via a custom widget builder.
ListDataBuilder.custom(
builder: (context, data, metadata) {
return Container(
color: metadata.selected ? Colors.green.shade100 : null,
child: Row(
children: [
Text(data['name']),
if (metadata.isEven) Icon(Icons.star),
],
),
);
},
)
๐งพ ListDataBuilder Parameters #
| Parameter | Type | Description | Used In |
|---|---|---|---|
labelBuilder |
String Function(T data) |
Required. Builds the label text for each item | string |
style |
ListDataTileStyle? |
Optional. Customizes the appearance of the list tile | string |
builder |
Widget Function(BuildContext, T data, ListTileMetadata metadata) |
Required. Custom widget builder with metadata for selection and index | custom |
๐งพ ListTileMetadata #
This helper class provides metadata for each item in the list:
| Property | Type | Description |
|---|---|---|
selected |
bool |
Whether the item is currently selected |
isEven |
bool |
Whether the item is at an even index in the list |
Use this metadata inside ListDataBuilder.custom to style or annotate items dynamically.
๐ฎ MultiPickerController #
MultiPickerController<T> is an optional controller that allows external control over the selected values in MultiDataPicker. It enables programmatic selection, deselection, toggling, and clearing of items, and notifies listeners when changes occur.
Example #
final controller = MultiPickerController<Map<String, dynamic>>(
isEquals: (a, b) => a['id'] == b['id'],
initialValue: [data[0]],
);
MultiDataPicker<Map<String, dynamic>>(
controller: controller,
isEqual: controller.isEquals,
// other parameters...
);
๐ฎ Controller API #
| Member | Type / Signature | Description |
|---|---|---|
values |
List<T> (getter) |
Returns the current selection (unmodifiable) |
values |
set(List<T>) |
Replaces the current selection and notifies listeners |
isSelected |
bool Function(T item) |
Checks if an item is currently selected |
select |
void Function(T item) |
Adds an item to the selection if not already selected |
deselect |
void Function(T item) |
Removes an item from the selection if present |
toggle |
void Function(T item) |
Toggles selection state of an item |
clear |
void Function() |
Clears all selected items |
๐ค Contributing #
We welcome contributions! Feel free to open issues, submit pull requests, or suggest improvements.
git clone https://github.com/your-org/multi_data_picker.git
โ Support This Project #
If you find multi_data_picker helpful, consider supporting its development:
๐ License #
MIT License - see the LICENSE file for details