FSelectMenuTile<T> constructor

FSelectMenuTile<T>({
  1. required Widget title,
  2. required List<FSelectTile<T>> menu,
  3. FSelectMenuTileController<T>? selectController,
  4. FPopoverController? popoverController,
  5. ScrollController? scrollController,
  6. FSelectMenuTileStyle style(
    1. FSelectMenuTileStyle
    )?,
  7. double? cacheExtent,
  8. double maxHeight = double.infinity,
  9. DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  10. ScrollPhysics physics = const ClampingScrollPhysics(),
  11. FItemDivider divider = FItemDivider.full,
  12. AlignmentGeometry menuAnchor = Alignment.topRight,
  13. AlignmentGeometry tileAnchor = Alignment.bottomRight,
  14. FPortalSpacing spacing = const FPortalSpacing(4),
  15. Offset shift(
    1. Size,
    2. FPortalChildBox,
    3. FPortalBox
    ) = FPortalShift.flip,
  16. Offset offset = Offset.zero,
  17. FPopoverHideRegion hideRegion = FPopoverHideRegion.excludeChild,
  18. VoidCallback? onTapHide,
  19. bool autoHide = true,
  20. Widget? label,
  21. Widget? description,
  22. bool autofocus = false,
  23. FocusScopeNode? focusNode,
  24. ValueChanged<bool>? onFocusChange,
  25. TraversalEdgeBehavior? traversalEdgeBehavior,
  26. String? barrierSemanticsLabel,
  27. bool barrierSemanticsDismissible = true,
  28. String? semanticsLabel,
  29. Widget? prefix,
  30. Widget? subtitle,
  31. ValueWidgetBuilder<Set<T>> detailsBuilder = _builder,
  32. Widget? details,
  33. Widget? suffix,
  34. Map<ShortcutActivator, Intent>? shortcuts,
  35. Map<Type, Action<Intent>>? actions,
  36. ValueChanged<Set<T>>? onChange,
  37. ValueChanged<(T, bool)>? onSelect,
  38. Widget errorBuilder(
    1. BuildContext,
    2. String
    ) = FFormFieldProperties.defaultErrorBuilder,
  39. T? initialValue,
  40. FormFieldSetter<Set<T>>? onSaved,
  41. FormFieldValidator<Set<T>>? validator,
  42. String? forceErrorText,
  43. bool enabled = true,
  44. AutovalidateMode? autovalidateMode,
  45. String? restorationId,
  46. Key? key,
})

Creates a FSelectMenuTile that eagerly builds the menu.

Contract

Throws AssertionError if:

  • both selectController and initialValue 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;
       },
     );