Menu
A dropdown menu component for displaying a list of actions or options
A dropdown menu component for displaying actions or options in an overlay.
When to use this
- Action menus: Show a list of available actions for a selected item
- Context menus: Provide contextual options based on user selection
- Navigation: Create dropdown navigation menus
- Settings: Display configuration options in a compact dropdown
Basic implementation
Basic implementation
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class MenuExample extends StatefulWidget {
const MenuExample({super.key});
@override
State<MenuExample> createState() => _MenuExampleState();
}
class _MenuExampleState extends State<MenuExample> {
final controller = MenuController();
@override
Widget build(BuildContext context) {
return RemixMenu<String>(
trigger: const RemixMenuTrigger(label: 'Open Menu'),
items: [
RemixMenuItem(
value: 'History',
leadingIcon: Icons.history,
label: 'History',
),
RemixMenuItem(
value: 'Settings',
leadingIcon: Icons.settings,
label: 'Settings',
),
const RemixMenuDivider(),
RemixMenuItem(
value: 'Logout',
leadingIcon: Icons.logout,
label: 'Logout',
style: MenuItemStyler().onHovered(
MenuItemStyler()
.color(Colors.redAccent.withValues(alpha: 0.05))
.label(TextStyler().color(Colors.redAccent))
.leadingIcon(IconStyler().color(Colors.redAccent)),
),
),
],
positioning: const OverlayPositionConfig(
sideOffset: 8,
side: OverlaySide.bottom,
alignment: OverlayAlignment.center,
),
style: menuStyle,
onSelected: (value) {
debugPrint('Selected: $value');
},
controller: controller,
);
}
MenuStyler get menuStyle {
return MenuStyler()
.trigger(
MenuTriggerStyler()
.padding(EdgeInsetsMix.symmetric(horizontal: 14))
.decoration(
BoxDecorationMix()
.color(Colors.white)
.borderRadius(BorderRadiusMix.all(const Radius.circular(12)))
.border(BorderMix.all(BorderSideMix(color: Colors.blueGrey.shade100)))
.boxShadow([
BoxShadowMix(
color: Colors.blueGrey.withValues(alpha: 0.1),
blurRadius: 3,
offset: const Offset(0, 3),
),
]),
)
.constraints(BoxConstraintsMix(minHeight: 40))
.label(
TextStyler()
.color(Colors.blueGrey.shade700)
.fontWeight(FontWeight.w400),
),
)
.overlay(
FlexBoxStyler(
padding: EdgeInsetsMix.all(12),
decoration: BoxDecorationMix(
color: Colors.white,
borderRadius: BorderRadiusMix.all(const Radius.circular(12)),
border: BorderMix.all(BorderSideMix(color: Colors.blueGrey.shade100)),
boxShadow: [
BoxShadowMix(
color: Colors.blueGrey.withValues(alpha: 0.1),
blurRadius: 3,
offset: const Offset(0, 3),
),
],
),
),
)
.item(menuItemStyle);
}
MenuItemStyler get menuItemStyle {
return MenuItemStyler()
.padding(.all(6))
.leadingIcon(IconStyler().size(20).color(Colors.blueGrey.shade800))
.spacing(8)
.borderRadius(.all(const Radius.circular(8)))
.label(TextStyler().color(Colors.blueGrey.shade800))
.onHovered(MenuItemStyler().color(Colors.blueGrey.shade50));
}
}An item without a label is icon-only and must set semanticLabel, the name
screen readers announce.
Custom trigger content
RemixMenuTrigger is a configuration object, not a widget. The same object is
the trigger for FortalMenu; there is no separate Fortal trigger type. It has
three constructors:
- The unnamed constructor is the standard label/icon surface.
RemixMenuTrigger.builderis custom visual content, such as an avatar or profile row. The builder receives the current menu state and the already styled default trigger asdefaultTrigger. It may wrap that child or replace it.NakedMenuowns tapping, focus, keyboard behavior, and expanded state.RemixMenuTrigger.controlis a real button, such as aFortalButton. The button owns its look, label, enabled state, and activation.
Builder output must be non-interactive visual content. Returning a
FortalButton, FortalIconButton, or any other nested interactive control
from RemixMenuTrigger.builder creates duplicate semantics and a second tap
target. Use RemixMenuTrigger.control when the trigger is a button.
Builder-backed avatar trigger
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
import 'ui/ui.dart';
class AccountMenuExample extends StatelessWidget {
const AccountMenuExample({super.key});
@override
Widget build(BuildContext context) {
return FortalMenu<String>(
trigger: RemixMenuTrigger.builder(
label: 'Account menu',
// The builder replaces the styled trigger, so it draws its own ring.
builder: (context, state, defaultTrigger) => RemixCard(
style: CardStyler()
.borderRadius(.circular(999))
.onFocusVisible(
CardStyler().containerEffects(
RemixBoxEffectsMix(
outline: BorderSideMix(color: Colors.indigo, width: 2),
outlineOffset: 2,
),
),
),
child: const FortalAvatar(label: 'LF'),
),
),
items: const [
RemixMenuItem(value: 'profile', label: 'View profile'),
RemixMenuItem(value: 'preferences', label: 'Preferences'),
RemixMenuDivider(),
RemixMenuItem(value: 'signout', label: 'Sign out'),
],
onSelected: (value) => debugPrint(value),
);
}
}Use a button as the trigger
RemixMenuTrigger.control takes a builder that receives a
RemixMenuTriggerHandle. Pass the handle's focusNode and toggle to your
button as its focusNode and onPressed.
A button as the trigger
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
import 'ui/ui.dart';
class ActionsMenuExample extends StatelessWidget {
const ActionsMenuExample({super.key});
@override
Widget build(BuildContext context) {
return FortalMenu<String>(
trigger: RemixMenuTrigger.control(
builder: (context, menu) => FortalButton(
label: 'Actions',
trailingIcon: Icons.keyboard_arrow_down,
focusNode: menu.focusNode,
onPressed: menu.toggle,
),
),
items: const [
RemixMenuItem(value: 'edit', label: 'Edit'),
RemixMenuItem(value: 'duplicate', label: 'Duplicate'),
RemixMenuDivider(),
RemixMenuItem(value: 'delete', label: 'Delete'),
],
onSelected: (value) => debugPrint(value),
);
}
}The handle exposes:
isOpen: whether the menu is open.controller: theMenuControllerthat opens and closes the menu. It is thecontrolleryou passed to the menu, or one the menu creates.focusNode: the node that receives focus when the menu closes; on open, focus moves into the menu panel. It is thetriggerFocusNodeyou passed to the menu, or a node the menu creates and disposes.toggle(): opens a closed menu and closes an open one.
The menu keeps the overlay, Escape, outside taps, focus return to the button,
and the expanded state, which it merges onto the button's own semantics node so
a screen reader announces one expandable button. The button keeps its look,
accessible name, enabled state, and Enter and Space activation. Pass
onPressed: null to disable it; a disabled button does not open the menu.
The builder must return a single control. Its expanded state stays on the button, while a loading button's live announcement remains a separate node. A split button needs separate menu controls.
MenuStyler.trigger and RemixMenu.semanticLabel do not apply to a control
trigger, because the button owns its look and name. Setting semanticLabel on a
menu with a control trigger fails an assertion.
Checkboxes, radio groups, and submenus
Checkboxes and radio groups are controlled. Keep their current value in your
widget state and update that state from onChanged. Submenus can contain any
menu item data, including another submenu.
Controlled compound items
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
import 'ui/ui.dart';
class CompoundMenuExample extends StatefulWidget {
const CompoundMenuExample({super.key});
@override
State<CompoundMenuExample> createState() => _CompoundMenuExampleState();
}
class _CompoundMenuExampleState extends State<CompoundMenuExample> {
bool showStatus = true;
String density = 'comfortable';
@override
Widget build(BuildContext context) {
return FortalMenu<String>.soft(
trigger: const RemixMenuTrigger(label: 'View options'),
onSelected: (value) => debugPrint('Root selected: $value'),
items: [
RemixMenuCheckboxItem(
value: 'show-status',
label: 'Show status',
checked: showStatus,
closeOnActivate: false,
onChanged: (next) => setState(() => showStatus = next),
),
RemixMenuRadioGroup(
value: density,
onChanged: (next) => setState(() => density = next),
items: const [
RemixMenuRadioItem(
value: 'compact',
label: 'Compact',
closeOnActivate: false,
),
RemixMenuRadioItem(
value: 'comfortable',
label: 'Comfortable',
closeOnActivate: false,
),
],
),
const RemixMenuDivider(),
const RemixMenuSubmenu(
label: 'Share',
items: [
RemixMenuItem(value: 'copy-link', label: 'Copy link'),
RemixMenuSubmenu(
label: 'More',
items: [
RemixMenuItem(value: 'email', label: 'Email'),
],
),
],
),
],
);
}
}RemixMenuCheckboxItem.checked and RemixMenuRadioGroup.value are never
changed internally. On activation, RemixMenu.onSelected runs first, followed
by the checkbox or radio-group onChanged. Selecting the already-selected
radio item re-emits its value. A checkbox or radio item is disabled when neither
its local callback nor the root onSelected callback is supplied.
closeOnActivate defaults to true. Set it to false for settings that should
remain visible while they are changed. Submenus open after a 100 ms hover delay
by default. The directional forward-arrow opens a focused submenu, the reverse
arrow closes it, and Escape closes it and restores focus to its trigger. These
directions are mirrored automatically in right-to-left layouts.
| Key | Behavior |
|---|---|
| Down / Up arrow | Move focus between enabled items |
| Enter / Space | Activate the focused item |
| Right arrow in LTR / Left arrow in RTL | Open the focused submenu and focus its first item |
| Left arrow in LTR / Right arrow in RTL | Close the current submenu and focus its trigger |
| Escape | Close the current menu level and restore trigger focus |
The root items list, each radio group's items, and every submenu's items
are copied to immutable snapshots when their panel is built. Rebuild the menu
after changing a source list; do not rely on mutating a visible panel in place.
For dynamic menus, including lists that are filtered, have items inserted, or
are reordered, supply a stable caller-owned key: on each ordinary item,
checkbox item, radio group, radio item, submenu, and divider. Build keys from
application-owned item identity and keep them stable across rebuilds. Item keys
are not derived automatically from labels or values.
Compound-item styling
Compound rows use the same MenuItemStyler as ordinary rows, including
hovered, focused, pressed, disabled, and selected-state variants. An open
submenu trigger contributes WidgetState.selected.
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
final sharedItemStyle = MenuItemStyler()
.padding(.horizontal(12))
.spacing(8)
.onSelected(.color(Colors.indigo.withValues(alpha: 0.08)));
final choiceItemStyle = MenuItemStyler().indicator(
IconStyler().size(9).color(Colors.indigo),
);
final appMenuStyle = MenuStyler()
.item(sharedItemStyle)
.checkboxItem(choiceItemStyle)
.radioItem(choiceItemStyle)
.submenuItem(
MenuItemStyler().trailingIcon(IconStyler().color(Colors.indigo)),
);item is the shared foundation for every actionable row. The semantic
checkboxItem, radioItem, and submenuItem styles merge after it, and an
individual item's style merges last. RemixMenuRadioGroup is behavioral and
does not add another styling layer. Use the normal MenuItemStyler flex,
padding, and spacing methods for layout.
The renderer reserves one leading choice slot for every row in a panel when
that panel directly contains a checkbox or radio group. Nested submenu panels
decide this independently. A custom trailingIcon replaces the default
directional submenu chevron.
Shortcuts and counts
trailingText adds a short string after an item's label, such as a keyboard
shortcut or a count. Items, checkbox items, radio items, and submenus accept it.
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
final appMenu = RemixMenu<String>(
trigger: const RemixMenuTrigger(label: 'More'),
style: MenuStyler()
.overlay(FlexBoxStyler().minWidth(200))
.item(
MenuItemStyler()
.spacing(8)
.label(TextStyler().fontSize(13).color(Colors.black87))
.trailingText(TextStyler().fontSize(12).color(Colors.black45)),
),
onSelected: (value) => debugPrint(value),
items: const [
RemixMenuItem(value: 'refresh', label: 'Refresh', trailingText: '⌘R'),
RemixMenuItem(value: 'settings', label: 'Settings…', trailingText: '⌘,'),
RemixMenuItem(
value: 'shortcuts',
label: 'Keyboard shortcuts',
trailingText: '?',
),
],
);The text appears before any trailing icon or submenu chevron. A minimum panel
width, as above, leaves room between the longest label and its text. It
inherits the label's text style, so style only what differs, such as a muted
color. Screen readers announce it after the label unless the item sets
semanticLabel. trailingText only displays a shortcut; bind the key with
Shortcuts or CallbackShortcuts.
Long menus
The panel scrolls when its items are taller than the room beside the trigger.
It is laid out no taller than the room above or below the trigger, after the
positioning sideOffset and collisionPadding, and opens on the side with
more room, so a long menu scrolls there instead of covering the trigger. Every
submenu panel scrolls the same way, within the room the overlay has. A
maxHeight on the overlay style caps the panel lower still. A menu that fits
keeps the size of its items, and the overlay's padding, decoration, and effects
stay in place while the items scroll. If neither side has
positioning.minimumAvailableHeight of room (48 logical pixels by default),
the panel may overlap the trigger to keep options usable, while respecting
collisionPadding. Increase this threshold for larger rows or panel padding;
it is a placement policy, not a guarantee for arbitrary content or a minimum
panel size. Set it to zero to retain strict side-capping, even at zero headroom.
A short
panel that fits still stays beside its trigger. With avoidCollisions: false,
only the requested side's space is used and the fallback does not apply.
import 'package:remix/remix.dart';
void main() {
MenuStyler().overlay(FlexBoxStyler().maxHeight(320));
}Match the trigger width
A menu panel is as wide as its widest row. remixAnchorWidth is the width of
the trigger that opened the menu, so the panel can follow it, like CSS
min-width: var(--trigger-width):
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class TriggerWidthMenuExample extends StatelessWidget {
const TriggerWidthMenuExample({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 240,
child: RemixMenu<String>(
trigger: const RemixMenuTrigger(label: 'Actions'),
style: MenuStyler().overlay(
FlexBoxStyler().minWidth(remixAnchorWidth()),
),
items: const [
RemixMenuItem(value: 'copy', label: 'Copy'),
RemixMenuItem(value: 'paste', label: 'Paste'),
],
onSelected: (value) => debugPrint(value),
),
);
}
}- Pass it to
minWidthfor a panel at least as wide as the trigger,widthfor exactly as wide, ormaxWidthfor no wider. The panel follows the trigger if it resizes while open. - It is opt-in. Without it the panel keeps the width its styles give it.
- The token resolves where the style is built.
RemixMenuandRemixSelectbuild their menu styles inside the overlay, so it works in the menuoverlaystyle. A style resolved outside the overlay, such as the trigger's or aRemixPopover's, reads0. - Submenu panels reuse the style resolved for the root panel, so they take the root trigger's width.
- On a single box, a
minWidthlarger than itsmaxWidthis invalid in Flutter and throws in debug. With the token that only happens once the trigger is wider than the maximum, so keep a ceiling on a different box than the token. - A panel wider than its trigger starts at the trigger's start edge by default, and follows the text direction.
Focus rings
MenuTriggerStyler.containerEffects and MenuItemStyler.containerEffects
paint an outline around the trigger or a row without taking layout space, so
focus does not resize either. MenuStyler.containerEffects is separate: it
paints the popup panel.
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
final menuRing = RemixBoxEffectsMix(
outline: BorderSideMix(color: Colors.indigo, width: 2),
outlineOffset: 2,
);
final ringedMenuStyle = MenuStyler()
.trigger(
MenuTriggerStyler().onFocusVisible(
MenuTriggerStyler().containerEffects(menuRing),
),
)
.item(
MenuItemStyler().onFocusVisible(
MenuItemStyler().containerEffects(menuRing),
),
);A popup that clips (clipBehavior on overlay) crops an outside row ring at
its edge. Pad the popup by at least the ring's offset plus width, or use a
negative outlineOffset to draw the row ring inside the row. Once the items
are long enough to scroll, the list clips at its own edges, inside the popup's
padding, so only an inside ring stays whole.
A RemixMenuTrigger.builder that returns a different widget instead of its
child replaces the styled trigger and its effects, so the returned content has
to draw its own focus ring, as the avatar example above does. A builder runs
under the menu's widget states, so onFocusVisible on its styles works.
Fortal widgets
The application-owned Fortal preset provides a themed widget for this component:
Fortal variants
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
import 'ui/ui.dart';
class FortalMenuExample extends StatelessWidget {
const FortalMenuExample({super.key});
@override
Widget build(BuildContext context) {
final choiceItemStyle = MenuItemStyler().indicator(
IconStyler().color(Colors.indigo),
);
final customStyle = MenuStyler()
.checkboxItem(choiceItemStyle)
.radioItem(choiceItemStyle);
final items = <RemixMenuItemData<String>>[
const RemixMenuItem(
value: 'edit',
label: 'Edit',
leadingIcon: Icons.edit,
),
const RemixMenuCheckboxItem(
value: 'show-status',
label: 'Show status',
checked: true,
),
const RemixMenuRadioGroup(
value: 'comfortable',
items: [
RemixMenuRadioItem(value: 'compact', label: 'Compact'),
RemixMenuRadioItem(value: 'comfortable', label: 'Comfortable'),
],
),
];
return Row(
spacing: 16,
children: [
RemixMenu<String>(
style: fortalMenuStyle(
variant: FortalMenuVariant.solid,
).merge(customStyle),
trigger: const RemixMenuTrigger(label: 'Solid', icon: Icons.menu),
items: items,
onSelected: (value) => debugPrint(value),
),
RemixMenu<String>(
style: fortalMenuStyle(
variant: FortalMenuVariant.soft,
).merge(customStyle),
trigger: const RemixMenuTrigger(label: 'Soft', icon: Icons.menu),
items: items,
onSelected: (value) => debugPrint(value),
),
],
);
}
}The generated FortalMenu, FortalMenu.solid, and FortalMenu.soft
constructors apply only the selected recipe. For reusable menu-wide
customization, pass fortalMenuStyle(...).merge(customStyle) to
RemixMenu.style; the custom fields merge after the recipe.
Fortal uses the pinned check and submenu-chevron paths, colors, sizes, and states. Their placement uses Remix's standard leading flex slot and item spacing, so small indicator and trailing-gap differences are the documented visual tolerance.
See the fortalMenuStyle source code for all available options.
Constructor
Constructor
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
RemixMenu<T> remixMenuConstructor<T>({
Key? key,
required RemixMenuTrigger trigger,
required List<RemixMenuItemData<T>> items,
MenuController? controller,
ValueChanged<T>? onSelected,
VoidCallback? onOpen,
VoidCallback? onClose,
VoidCallback? onCanceled,
RawMenuAnchorOpenRequestedCallback? onOpenRequested,
RawMenuAnchorCloseRequestedCallback? onCloseRequested,
bool consumeOutsideTaps = true,
bool useRootOverlay = false,
bool closeOnClickOutside = true,
FocusNode? triggerFocusNode,
OverlayPositionConfig positioning = const OverlayPositionConfig(),
String? semanticLabel,
bool excludeSemantics = false,
MenuStyler style = const MenuStyler.create(),
MenuSpec? styleSpec,
}) => throw UnimplementedError();Properties
Widget Properties
key → Key?
Optional. Controls how one widget replaces another widget in the tree.
trigger → RemixMenuTrigger
Required. Configuration for the menu's button, not a widget. Use
RemixMenuTrigger(label:, icon:) for the standard surface,
RemixMenuTrigger.builder(label:, icon:, builder:) for custom visual
content, or RemixMenuTrigger.control(builder:) for a real button. label is
the visible default label and the accessible fallback for a builder-backed
trigger; it is null for a control trigger. The same object works with
FortalMenu.
items → List<RemixMenuItemData<T>>
Required. The list of ordinary items, checkbox items, radio groups, submenus, and dividers.
controller → MenuController?
Optional. Optional controller for programmatic control of the menu state. If not provided, an internal controller will be created automatically.
onSelected → ValueChanged<T>?
Optional. Called when an item is selected.
onOpen → VoidCallback?
Optional. Called when the menu opens.
onClose → VoidCallback?
Optional. Called when the menu closes.
onCanceled → VoidCallback?
Optional. Called when the menu closes without a selection.
onOpenRequested → RawMenuAnchorOpenRequestedCallback?
Optional. Open/close interceptors (for example, to drive animations).
onCloseRequested → RawMenuAnchorCloseRequestedCallback?
Optional.
consumeOutsideTaps → bool
Optional. Whether outside taps on the trigger are consumed.
useRootOverlay → bool
Optional. Whether to target the root overlay instead of the nearest ancestor.
closeOnClickOutside → bool
Optional. Whether taps outside the overlay close the menu.
triggerFocusNode → FocusNode?
Optional. Optional focus node for the trigger. A RemixMenuTrigger.control receives it as menu.focusNode; without one the menu creates and disposes a node for the control.
positioning → OverlayPositionConfig
Optional. Overlay positioning configuration.
semanticLabel → String?
Optional. The accessible label for the menu trigger. Must be null with RemixMenuTrigger.control, whose button owns its label.
excludeSemantics → bool
Optional. Whether to hide the menu trigger from the semantics tree. Defaults to false. This applies to the trigger only — the overlay is mounted separately, so an open menu keeps announcing its items.
style → MenuStyler
Optional. The style configuration for the menu.
styleSpec → MenuSpec?
Optional. A raw resolved style spec that bypasses fluent style resolution.
For semantic item fields, null inherits the raw item spec and a supplied
value completely replaces it; per-item fluent styles remain bypassed.
Menu Style Methods
trigger(MenuTriggerStyler value)
Configures the trigger button style.
overlay(FlexBoxStyler value)
Configures the menu overlay container style. Its width, minWidth, and
maxWidth accept remixAnchorWidth() to follow the trigger width.
containerEffects(RemixBoxEffectsMix value)
Configures the outline and paint layers drawn around the popup panel without affecting layout.
item(MenuItemStyler value)
Configures the shared default for every actionable menu row.
checkboxItem(MenuItemStyler value)
Configures the menu-wide checkbox-row override after item.
radioItem(MenuItemStyler value)
Configures the menu-wide radio-row override after item.
submenuItem(MenuItemStyler value)
Configures the menu-wide submenu-trigger override after item.
divider(DividerStyler value)
Configures divider style.
animate(AnimationConfig value)
Configures implicit animation for style transitions.
variants(List<VariantStyle<MenuSpec>> value)
Sets style variants.
wrap(WidgetModifierConfig value)
Applies widget modifiers such as clipping, opacity, or scaling.
modifier(WidgetModifierConfig value)
Sets the widget modifier.
call<T>({..., String? semanticLabel, bool excludeSemantics = false})
Creates a RemixMenu widget with this style applied.
Menu Trigger Style Methods
container(FlexBoxStyler value)
Configures the trigger container style.
containerEffects(RemixBoxEffectsMix value)
Configures the outline and paint layers drawn around the trigger without affecting layout, such as a focus ring.
label(TextStyler value)
Configures the trigger label style.
icon(IconStyler value)
Configures the trigger icon style.
animate(AnimationConfig value)
Configures implicit animation for trigger style transitions.
variants(List<VariantStyle<MenuTriggerSpec>> value)
Sets trigger style variants.
wrap(WidgetModifierConfig value)
Applies widget modifiers such as clipping, opacity, or scaling.
modifier(WidgetModifierConfig value)
Sets the trigger widget modifier.
labelStyle(TextStyleMix value)
Sets label/text style using TextStyleMix directly
labelColor(Color value)
Sets label/text color
labelFontSize(double value)
Sets label/text font size
labelFontWeight(FontWeight value)
Sets label/text font weight
labelFontStyle(FontStyle value)
Sets label/text font style (italic/normal)
labelLetterSpacing(double value)
Sets label/text letter spacing
labelDecoration(TextDecoration value)
Sets label/text decoration (underline, strikethrough, etc.)
labelFontFamily(String value)
Sets label/text font family
labelHeight(double value)
Sets label/text line height
labelWordSpacing(double value)
Sets label/text word spacing
labelDecorationColor(Color value)
Sets label/text decoration color
iconColor(Color value)
Sets icon color
iconSize(double value)
Sets icon size
iconOpacity(double value)
Sets icon opacity
iconWeight(double value)
Sets icon weight (useful for variable icons like Material Symbols)
iconGrade(double value)
Sets icon grade (useful for Material Icons)
iconFill(double value)
Sets icon fill (useful for Material Icons filled variants)
iconOpticalSize(double value)
Sets icon optical size (useful for Material Icons)
iconBlendMode(BlendMode value)
Sets icon blend mode
iconTextDirection(TextDirection value)
Sets icon text direction
iconShadows(List<ShadowMix> value)
Sets icon shadows
iconShadow(ShadowMix value)
Sets single icon shadow
Menu Item Style Methods
container(FlexBoxStyler value)
Configures the item container style.
containerEffects(RemixBoxEffectsMix value)
Configures the outline and paint layers drawn around the row without affecting layout, such as a focus ring.
label(TextStyler value)
Configures the label text style using a TextStyler.
leadingIcon(IconStyler value)
Configures the leading icon style.
trailingIcon(IconStyler value)
Configures the trailing icon style.
trailingText(TextStyler value)
Configures the trailing text style, which inherits the label's.
indicator(IconStyler value)
Configures the checkbox and radio indicator style.
alignment(Alignment value)
Sets container alignment
padding(EdgeInsetsGeometryMix value)
Sets the container padding.
spacing(double value)
Sets the gap between the row's indicator, icons, label, and trailing text.
color(Color value)
Sets background color.
size(double width, double height)
Sets the component size.
borderRadius(BorderRadiusGeometryMix radius)
Sets the border radius.
constraints(BoxConstraintsMix value)
Sets size constraints on the component.
decoration(DecorationMix value)
Sets the container decoration.
margin(EdgeInsetsGeometryMix value)
Sets the container margin.
foregroundDecoration(DecorationMix value)
Sets a foreground decoration painted on top of the component.
transform(Matrix4 value, AlignmentGeometry alignment = Alignment.center)
Applies a matrix transformation to the component.
animate(AnimationConfig value)
Configures implicit animation for style transitions.
variants(List<VariantStyle<MenuItemSpec>> value)
Sets item style variants.
wrap(WidgetModifierConfig value)
Applies widget modifiers such as clipping, opacity, or scaling.
modifier(WidgetModifierConfig value)
Sets the item widget modifier.