FSelectMenuTile<T>.builder constructor
FSelectMenuTile<T>.builder ({
- required Widget title,
- int? count,
- 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 lazily builds the menu.
The menuBuilder
is called for each tile that should be built. The current level's FInheritedItemData is not
visible to menuBuilder
.
- It may return null to signify the end of the group.
- It may be called more than once for the same index.
- It will be called only for indices <=
count
ifcount
is given.
The count
is the number of tiles to build. If null, menuBuilder
will be called until it returns null.
Contract
Throws AssertionError if both selectController
and initialValue
are provided.
Warning
May result in an infinite loop or run out of memory if:
- Placed in a parent widget that does not constrain its size, i.e., Column.
count
is null andmenuBuilder
always provides a zero-size widget, i.e., SizedBox(). If possible, provide tiles with non-zero size, return null from the builder, or setcount
to non-null.
Implementation
FSelectMenuTile.builder({
required this.title,
required FSelectTile<T>? Function(BuildContext, int) menuBuilder,
int? count,
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,
constraints: FPortalConstraints(maxWidth: menuStyle.maxWidth),
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: (_, _) => FSelectTileGroup<T>.builder(
selectController: state._controller,
scrollController: scrollController,
cacheExtent: cacheExtent,
maxHeight: maxHeight,
dragStartBehavior: dragStartBehavior,
physics: physics,
style: menuStyle.tileGroupStyle,
semanticsLabel: semanticsLabel,
divider: divider,
tileBuilder: menuBuilder,
count: count,
),
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: global,
states: states,
label: label,
description: description,
error: error,
child: tile,
);
}
return tile;
},
);