M3E Dropdown Menu

A comprehensive Flutter package providing an expressive, Material 3 dropdown menu with high-performance spring physics, interactive radius morphing, and extensive customization options. Featuring single and multi-selection, fuzzy search, async loading, and animated chip tags—all designed with premium M3 Expressive principles.
It automatically calculates and draws corners to fit exactly the Material 3 Expressive spec. It provides deep control over styling, custom haptic feedback, and highly tunable spring motions.
🚀 Features
- Interactive Radius Morphing: Snappy, high-fidelity
BorderRadiusmorphing on hover and press for both field and items. - Dual-Speed Animations: Fine-tuned durations (20ms for selection, 40ms for interactions) for an incredibly responsive feel.
- Premium Interaction Model: Subtle opacity-based overlay fades that complement structural motion better than standard ripples.
- Form Integration: Full support for
FormFieldvalidation with customizable error borders and text styles. - Standardized Styling: All style classes support
copyWith,lerp, and equality operators for easy theme integration. - Dynamic Border Radius: Fluid transitions between field states with animated borders.
- Rich Interaction: Spring-driven physics for expanding and collapsing the dropdown menu.
- Chips & Search: Built-in multi-selection chip support and fuzzy search input.
- Highly Customizable: Complete control over haptics, motion presets, overlay colors, and per-component styling.
📦 Installation
dependencies:
m3e_dropdown_menu: ^0.1.0
import 'package:m3e_dropdown_menu/m3e_dropdown_menu.dart';
🧩 Usage
🚀 Want to try out the package?
Single-select with Radius Morphing
A basic dropdown selecting one item, featuring interactive radius morphing for a tactile feel.
M3EDropdownMenu<String>(
items: [
M3EDropdownItem(label: 'Apple', value: 'apple'),
M3EDropdownItem(label: 'Banana', value: 'banana'),
M3EDropdownItem(label: 'Cherry', value: 'cherry'),
],
singleSelect: true,
openMotion: M3EMotion.standardSpatialDefault,
fieldStyle: M3EDropdownFieldStyle(
hintText: 'Choose a fruit',
borderRadius: BorderRadius.circular(12),
selectedBorderRadius: 28,
hoverRadius: 16,
pressedRadius: 8,
),
dropdownStyle: const M3EDropdownStyle(containerRadius: 18),
itemStyle: const M3EDropdownItemStyle(
outerRadius: 18,
innerRadius: 6,
hoverRadius: 8,
pressedRadius: 4,
),
onSelectionChanged: (items) => print(items),
)
Multi-select + Search + Chips + Custom Motion
Provides a search bar, animated chip display, and custom spring physics.
M3EDropdownMenu<String>(
items: fruitItems,
searchEnabled: true,
showChipAnimation: true,
maxSelections: 7,
openMotion: M3EMotion.custom(stiffness: 500, damping: 0.6),
fieldStyle: M3EDropdownFieldStyle(
hintText: 'Pick up to 7 fruits',
border: BorderSide(color: Theme.of(context).colorScheme.outline),
showClearIcon: true,
),
chipStyle: M3EChipStyle(
maxDisplayCount: 3,
borderRadius: BorderRadius.circular(33),
openMotion: M3EMotion.expressiveSpatialFast,
closeMotion: M3EMotion.expressiveSpatialDefault,
),
searchStyle: M3ESearchStyle(
hintText: 'Search fruits…',
filled: true,
borderRadius: BorderRadius.circular(24),
),
onSelectionChanged: (items) => print(items),
)
Form Validation Styling
Easily style validation errors with custom borders and text styles.
M3EDropdownMenu<String>(
items: fruitItems,
validator: (val) => val == null || val.isEmpty ? 'Required field' : null,
fieldStyle: M3EDropdownFieldStyle(
errorBorder: const BorderSide(color: Colors.red, width: 2),
errorStyle: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
)
Async Data Loading
Loads dropdown items asynchronously via a Future.
M3EDropdownMenu<int>.future(
future: () async {
await Future.delayed(const Duration(seconds: 2));
return List.generate(
10,
(i) => M3EDropdownItem(label: 'User ${i + 1}', value: i + 1),
);
},
singleSelect: true,
fieldStyle: const M3EDropdownFieldStyle(
hintText: 'Loading users…',
),
onSelectionChanged: (items) => print(items),
)
🛠 API Reference
M3EDropdownMenu Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<M3EDropdownItem<T>> |
Required | List of dropdown items. |
future |
M3EDropdownFutureRequest<T>? |
null |
Async item provider (use .future() constructor). |
singleSelect |
bool |
false |
Limits to a single choice if true. |
searchEnabled |
bool |
false |
Displays a search bar inside the overlay. |
showChipAnimation |
bool |
true |
Chips slide / pop when selections change. |
maxSelections |
int |
0 (Unlimited) |
Maximum allowed selections. |
onSelectionChanged |
ValueChanged<List<M3EDropdownItem<T>>>? |
null |
Called whenever the selection changes. |
onSearchChanged |
ValueChanged<String>? |
null |
Called when the search text changes. |
controller |
M3EDropdownController<T>? |
null |
Optional programmatic controller. |
enabled |
bool |
true |
Whether the dropdown is enabled. |
containerRadius |
double |
28.0 |
Radius for the dropdown panel and field (when no field radius is set). |
fieldStyle |
M3EDropdownFieldStyle |
const |
Stylize the field placeholder, radii, and interactions. |
dropdownStyle |
M3EDropdownStyle |
const |
Stylize the overlay panel height, colors, and shadow. |
chipStyle |
M3EChipStyle |
const |
Stylize the chips, spacing, and animations. |
searchStyle |
M3ESearchStyle |
const |
Stylize the search field inside the dropdown. |
itemStyle |
M3EDropdownItemStyle |
const |
Stylize individual dropdown items and their morphing. |
openMotion |
M3EMotion |
expressiveSpatialDefault |
Spring physics for expansion/selection transitions. |
closeMotion |
M3EMotion |
expressiveSpatialDefault |
Spring physics for collapse transitions. |
haptic |
M3EHapticFeedback |
none |
Haptic feedback intensity on tap. |
itemBuilder |
M3EDropdownItemBuilder<T>? |
null |
Custom builder for each dropdown item. |
emptyBuilder |
WidgetBuilder? |
null |
Custom builder for the empty state when no items are found. |
selectedItemBuilder |
Widget Function(M3EDropdownItem<T>)? |
null |
Custom builder for selected items in the field. |
M3EDropdownFieldStyle Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
borderRadius |
BorderRadius? |
- | Resting radius of the field. |
selectedBorderRadius |
double? |
- | Radius when the dropdown is expanded. |
hoverRadius |
double? |
- | Radius used when the field is hovered. |
pressedRadius |
double? |
- | Radius used when the field is pressed. |
splashFactory |
InteractiveInkFeatureFactory? |
NoSplash |
Type of splash effect for tap feedback. |
errorBorder |
BorderSide? |
- | Border used when validation fails. |
errorStyle |
TextStyle? |
- | Style for the validation error text. |
mouseCursor |
MouseCursor? |
SystemMouseCursors.click |
Cursor used when hovering. |
showArrow |
bool |
true |
Shows default animated chevron. |
showClearIcon |
bool |
false |
Shows clear-all icon when selections exist. |
clearIcon |
Widget? |
- | Custom widget for the clear-all icon. |
M3EDropdownItemStyle Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
outerRadius |
double? |
12.0 |
Radius for the "cap" corners of the first/last items. |
innerRadius |
double |
6.0 |
Base radius for internal item corners. |
hoverRadius |
double |
8.0 |
Radius used when an item is hovered. |
pressedRadius |
double |
4.0 |
Radius used when an item is pressed. |
selectedBorderRadius |
double? |
- | Radius used when an item is selected. |
mouseCursor |
MouseCursor? |
- | Cursor used when hovering over an item. |
backgroundColor / textColor |
Color? |
- | Colors for the items. |
selectedBackgroundColor / selectedTextColor |
Color? |
- | Colors for items when selected. |
🎯 Check the Example App for more details.
🐞 Found a bug? or ✨ You have a Feature Request?
Feel free to open a Issue or Contribute to the project.
Hope You Love It!
Credits
- Motor Pub Package for Expressive Animations
- Claude and Gemini for helping me with the code and documentation.
Radhe Radhe 🙏
Libraries
- A Material 3 Expressive dropdown menu package.