FSelectMenuTile<T> constructor

FSelectMenuTile<T>({
  1. required FSelectGroupController<T> groupController,
  2. required List<FSelectTile<T>> menu,
  3. required Widget title,
  4. FPopoverController? popoverController,
  5. FSelectMenuTileStyle? style,
  6. FTileDivider divider = FTileDivider.full,
  7. Alignment menuAnchor = Alignment.topRight,
  8. Alignment tileAnchor = Alignment.bottomRight,
  9. Offset shift(
    1. Size,
    2. FPortalTarget,
    3. FPortalFollower
    ) = FPortalFollowerShift.flip,
  10. bool hideOnTapOutside = true,
  11. bool ignoreDirectionalPadding = true,
  12. bool autoHide = false,
  13. Widget? label,
  14. Widget? description,
  15. Widget errorBuilder(
    1. BuildContext,
    2. String
    ) = _errorBuilder,
  16. String? semanticLabel,
  17. bool autofocus = false,
  18. FocusNode? focusNode,
  19. ValueChanged<bool>? onFocusChange,
  20. Widget? prefixIcon,
  21. Widget? subtitle,
  22. Widget? details,
  23. Widget? suffixIcon,
  24. FormFieldSetter<Set<T>>? onSaved,
  25. FormFieldValidator<Set<T>>? validator,
  26. Set<T>? initialValue,
  27. String? forceErrorText,
  28. bool enabled = true,
  29. AutovalidateMode? autovalidateMode,
  30. String? restorationId,
  31. Key? key,
})

Creates a FSelectMenuTile.

Implementation

FSelectMenuTile({
  required this.groupController,
  required this.menu,
  required this.title,
  this.popoverController,
  this.style,
  this.divider = FTileDivider.full,
  this.menuAnchor = Alignment.topRight,
  this.tileAnchor = Alignment.bottomRight,
  this.shift = FPortalFollowerShift.flip,
  this.hideOnTapOutside = true,
  this.ignoreDirectionalPadding = true,
  this.autoHide = false,
  this.label,
  this.description,
  this.errorBuilder = _errorBuilder,
  this.semanticLabel,
  this.autofocus = false,
  this.focusNode,
  this.onFocusChange,
  this.prefixIcon,
  this.subtitle,
  this.details,
  this.suffixIcon,
  super.onSaved,
  super.validator,
  super.initialValue,
  super.forceErrorText,
  super.enabled = true,
  super.autovalidateMode,
  super.restorationId,
  super.key,
}) : super(
        builder: (field) {
          final state = field as _State<T>;
          final groupData = FTileGroupData.maybeOf(state.context);
          final tileData = FTileData.maybeOf(state.context);

          final global = state.context.theme.selectMenuTileStyle;
          final labelStyle = style?.labelStyle ?? global.labelStyle;
          final menuStyle = style?.menuStyle ?? global.menuStyle;
          final tileStyle = style?.tileStyle ?? tileData?.style ?? groupData?.style.tileStyle ?? global.tileStyle;

          final (labelState, error) = switch (state.errorText) {
            _ when !enabled => (FLabelState.disabled, null),
            final text? => (FLabelState.error, errorBuilder(state.context, text)),
            null => (FLabelState.enabled, null),
          };

          Widget tile = FPopover(
            // A GlobalObjectKey is used to workaround 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 state. 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,
            followerAnchor: menuAnchor,
            targetAnchor: tileAnchor,
            shift: shift,
            hideOnTapOutside: hideOnTapOutside,
            ignoreDirectionalPadding: ignoreDirectionalPadding,
            autofocus: autofocus,
            focusNode: focusNode,
            onFocusChange: onFocusChange,
            followerBuilder: (context, _, __) => ConstrainedBox(
              constraints: BoxConstraints(maxWidth: menuStyle.maxWidth),
              child: FSelectTileGroup<T>(
                controller: state._controller,
                style: menuStyle.tileGroupStyle,
                semanticLabel: semanticLabel,
                divider: divider,
                children: menu,
              ),
            ),
            target: FTile(
              style: tileStyle,
              prefixIcon: prefixIcon,
              enabled: enabled,
              title: title,
              subtitle: subtitle,
              details: details,
              suffixIcon: suffixIcon ?? FIcon(FAssets.icons.chevronsUpDown),
              onPress: state._controller._popover.toggle,
            ),
          );

          if (groupData == null && tileData == null && (label != null || description != null || error != null)) {
            tile = FLabel(
              axis: Axis.vertical,
              style: labelStyle,
              state: labelState,
              label: label,
              description: description,
              error: error,
              child: tile,
            );
          }

          return tile;
        },
      );