FSelectMenuTile<T> constructor
FSelectMenuTile<T> ({
- required Widget title,
- FSelectMenuTileController<
T> ? selectController, - FPopoverController? popoverController,
- ScrollController? scrollController,
- FSelectMenuTileStyle style()?,
- double? cacheExtent,
- double maxHeight = double.infinity,
- DragStartBehavior dragStartBehavior = DragStartBehavior.start,
- ScrollPhysics physics = const ClampingScrollPhysics(),
- FItemDivider divider = FItemDivider.full,
- AlignmentGeometry tileAnchor = Alignment.bottomRight,
- FPortalSpacing spacing = const FPortalSpacing(4),
- Offset shift() = FPortalShift.flip,
- Offset offset = Offset.zero,
- FPopoverHideRegion hideRegion = FPopoverHideRegion.excludeChild,
- VoidCallback? onTapHide,
- bool autoHide = true,
- Widget? label,
- Widget? description,
- bool autofocus = false,
- FocusScopeNode? focusNode,
- ValueChanged<
bool> ? onFocusChange, - TraversalEdgeBehavior? traversalEdgeBehavior,
- String? barrierSemanticsLabel,
- bool barrierSemanticsDismissible = true,
- String? semanticsLabel,
- Widget? prefix,
- Widget? subtitle,
- ValueWidgetBuilder<
Set< detailsBuilder = _builder,T> > - Widget? details,
- Widget? suffix,
- Map<
ShortcutActivator, Intent> ? shortcuts, - Map<
Type, Action< ? actions,Intent> > - ValueChanged<
Set< ? onChange,T> > - ValueChanged<
(T, bool)> ? onSelect, - Widget errorBuilder() = FFormFieldProperties.defaultErrorBuilder,
- T? initialValue,
- FormFieldSetter<
Set< ? onSaved,T> > - FormFieldValidator<
Set< ? validator,T> > - String? forceErrorText,
- bool enabled = true,
- AutovalidateMode? autovalidateMode,
- String? restorationId,
- Key? key,
Creates a FSelectMenuTile that eagerly builds the menu.
Contract
Throws AssertionError if:
- both
selectController
andinitialValue
are provided.
Implementation
FSelectMenuTile({
required this.title,
required List<FSelectTile<T>> menu,
this.selectController,
this.popoverController,
this.scrollController,
this.style,
this.cacheExtent,
this.maxHeight = double.infinity,
this.dragStartBehavior = DragStartBehavior.start,
this.physics = const ClampingScrollPhysics(),
this.divider = FItemDivider.full,
this.menuAnchor = Alignment.topRight,
this.tileAnchor = Alignment.bottomRight,
this.spacing = const FPortalSpacing(4),
this.shift = FPortalShift.flip,
this.offset = Offset.zero,
this.hideRegion = FPopoverHideRegion.excludeChild,
this.onTapHide,
this.autoHide = true,
this.label,
this.description,
this.autofocus = false,
this.focusNode,
this.onFocusChange,
this.traversalEdgeBehavior,
this.barrierSemanticsLabel,
this.barrierSemanticsDismissible = true,
this.semanticsLabel,
this.prefix,
this.subtitle,
this.detailsBuilder = _builder,
this.details,
this.suffix,
this.shortcuts,
this.actions,
this.onChange,
this.onSelect,
Widget Function(BuildContext, String) errorBuilder = FFormFieldProperties.defaultErrorBuilder,
T? initialValue,
super.onSaved,
super.validator,
super.forceErrorText,
super.enabled = true,
super.autovalidateMode,
super.restorationId,
super.key,
}) : assert(
selectController == null || initialValue == null,
'Cannot provide both selectController and initialValue',
),
super(
initialValue: {?initialValue, ...?selectController?.value},
errorBuilder: errorBuilder,
builder: (field) {
final state = field as _State<T>;
final data = FInheritedItemData.maybeOf(state.context);
final inheritedStyle = FTileGroupStyleData.maybeOf(state.context);
final global = state.context.theme.selectMenuTileStyle;
final selectMenuTileStyle = style?.call(global);
final menuStyle = selectMenuTileStyle?.menuStyle ?? global.menuStyle;
final tileStyle = selectMenuTileStyle?.tileStyle ?? inheritedStyle?.tileStyle ?? global.tileStyle;
Widget tile = FPopover(
// A GlobalObjectKey is used to work around Flutter not recognizing how widgets move inside the widget tree.
//
// OverlayPortalControllers are tied to a single _OverlayPortalState, and conditional rebuilds introduced
// by FLabel and its internals can cause a new parent to be inserted above FPopover. This leads to the
// entire widget subtree being rebuilt and losing their states. Consequently, the controller is assigned
// another _OverlayPortalState, causing an assertion to be thrown.
//
// See https://stackoverflow.com/a/59410824/4189771
key: GlobalObjectKey(state._controller._popover),
controller: state._controller._popover,
style: menuStyle,
popoverAnchor: menuAnchor,
childAnchor: tileAnchor,
spacing: spacing,
shift: shift,
offset: offset,
hideRegion: hideRegion,
onTapHide: onTapHide,
autofocus: autofocus,
focusNode: focusNode,
onFocusChange: onFocusChange,
traversalEdgeBehavior: traversalEdgeBehavior,
barrierSemanticsLabel: barrierSemanticsLabel,
barrierSemanticsDismissible: barrierSemanticsDismissible,
popoverBuilder: (_, _) => ConstrainedBox(
constraints: BoxConstraints(maxWidth: menuStyle.maxWidth),
child: FInheritedItemData(
child: FSelectTileGroup<T>(
selectController: state._controller,
scrollController: scrollController,
cacheExtent: cacheExtent,
maxHeight: maxHeight,
dragStartBehavior: dragStartBehavior,
physics: physics,
style: menuStyle.tileGroupStyle,
semanticsLabel: semanticsLabel,
divider: divider,
children: menu,
),
),
),
child: FTile(
style: tileStyle,
prefix: prefix,
enabled: enabled,
title: title,
subtitle: subtitle,
details: ValueListenableBuilder(
valueListenable: state._controller.delegate,
builder: detailsBuilder,
child: details,
),
suffix: suffix ?? const Icon(FIcons.chevronsUpDown),
shortcuts: shortcuts,
actions: actions,
onPress: state._controller._popover.toggle,
),
);
if (data == null && (label != null || description != null || state.errorText != null)) {
final states = {if (!enabled) WidgetState.disabled, if (state.errorText != null) WidgetState.error};
final error = state.errorText == null ? null : errorBuilder(state.context, state.errorText!);
tile = FLabel(
axis: Axis.vertical,
style: selectMenuTileStyle ?? global,
states: states,
label: label,
description: description,
error: error,
child: tile,
);
}
return tile;
},
);