THATMobile/lib/core/components/select/custom_select.dart
2026-02-26 10:39:42 +07:00

260 lines
9.6 KiB
Dart

import 'package:baseproject/assets/images.dart';
import 'package:baseproject/core/components/constants_widget.dart';
import 'package:baseproject/core/components/form/form_builder_field.dart';
import 'package:baseproject/core/components/index.dart';
import 'package:baseproject/core/theme/custom_color.dart';
import 'package:baseproject/core/theme/text_style.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
/// Field for Dropdown button
class CustomSelect<T> extends FormBuilderField<T> {
/// The list of items the user can select.
///
/// If the [onChanged] callback is null or the list of items is null
/// then the dropdown button will be disabled, i.e. its arrow will be
/// displayed in grey and it will not respond to input. A disabled button
/// will display the [disabledHint] widget if it is non-null. If
/// [disabledHint] is also null but [hint] is non-null, [hint] will instead
/// be displayed.
final List<DropdownMenuItem<T>> items;
/// A placeholder widget that is displayed by the dropdown button.
///
/// If [value] is null, this widget is displayed as a placeholder for
/// the dropdown button's value. This widget is also displayed if the button
/// is disabled ([items] or [onChanged] is null) and [disabledHint] is null.
final Widget? hint;
/// A message to show when the dropdown is disabled.
///
/// Displayed if [items] or [onChanged] is null. If [hint] is non-null and
/// [disabledHint] is null, the [hint] widget will be displayed instead.
final Widget? disabledHint;
/// Called when the dropdown button is tapped.
///
/// This is distinct from [onChanged], which is called when the user
/// selects an item from the dropdown.
///
/// The callback will not be invoked if the dropdown button is disabled.
final VoidCallback? onTap;
/// A builder to customize the dropdown buttons corresponding to the
/// [DropdownMenuItem]s in [items].
///
/// When a [DropdownMenuItem] is selected, the widget that will be displayed
/// from the list corresponds to the [DropdownMenuItem] of the same index
/// in [items].
///
/// {@tool dartpad --template=stateful_widget_scaffold}
///
/// This sample shows a `DropdownButton` with a button with [Text] that
/// corresponds to but is unique from [DropdownMenuItem].
///
/// If this callback is null, the [DropdownMenuItem] from [items]
/// that matches [value] will be displayed.
final DropdownButtonBuilder? selectedItemBuilder;
/// The z-coordinate at which to place the menu when open.
///
/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12,
/// 16, and 24. See [kElevationToShadow].
///
/// Defaults to 8, the appropriate elevation for dropdown buttons.
final int elevation;
/// {@end-tool}
///
/// Defaults to the [TextTheme.subtitle1] value of the current
/// [ThemeData.textTheme] of the current [Theme].
final TextStyle? style;
/// The widget to use for the drop-down button's icon.
///
/// Defaults to an [Icon] with the [Icons.arrow_drop_down] glyph.
final Widget? icon;
/// The color of any [Icon] descendant of [icon] if this button is disabled,
/// i.e. if [onChanged] is null.
///
/// Defaults to [Colors.grey.shade400] when the theme's
/// [ThemeData.brightness] is [Brightness.light] and to
/// [Colors.white10] when it is [Brightness.dark]
final Color? iconDisabledColor;
/// The color of any [Icon] descendant of [icon] if this button is enabled,
/// i.e. if [onChanged] is defined.
///
/// Defaults to [Colors.grey.shade700] when the theme's
/// [ThemeData.brightness] is [Brightness.light] and to
/// [Colors.white70] when it is [Brightness.dark]
final Color? iconEnabledColor;
/// The size to use for the drop-down button's down arrow icon button.
///
/// Defaults to 24.0.
final double iconSize;
/// Reduce the button's height.
///
/// By default this button's height is the same as its menu items' heights.
/// If isDense is true, the button's height is reduced by about half. This
/// can be useful when the button is embedded in a container that adds
/// its own decorations, like [InputDecorator].
final bool isDense;
/// Set the dropdown's inner contents to horizontally fill its parent.
///
/// By default this button's inner width is the minimum size of its contents.
/// If [isExpanded] is true, the inner width is expanded to fill its
/// surrounding container.
final bool isExpanded;
/// If null, then the menu item heights will vary according to each menu item's
/// intrinsic height.
///
/// The default value is [kMinInteractiveDimension], which is also the minimum
/// height for menu items.
///
/// If this value is null and there isn't enough vertical room for the menu,
/// then the menu's initial scroll offset may not align the selected item with
/// the dropdown button. That's because, in this case, the initial scroll
/// offset is computed as if all of the menu item heights were
/// [kMinInteractiveDimension].
final double itemHeight;
/// The color for the button's [Material] when it has the input focus.
final Color? focusColor;
/// {@macro flutter.widgets.Focus.autofocus}
final bool autofocus;
/// The background color of the dropdown.
///
/// If it is not provided, the theme's [ThemeData.canvasColor] will be used
/// instead.
final Color? dropdownColor;
final bool allowClear;
final Widget clearIcon;
/// Creates field for Dropdown button
CustomSelect({
Key? key,
//From Super
String name = "select",
FormFieldValidator<T>? validator,
T? initialValue,
InputDecoration decoration = const InputDecoration(),
ValueChanged<T?>? onChanged,
ValueTransformer<T?>? valueTransformer,
bool enabled = true,
FormFieldSetter<T>? onSaved,
AutovalidateMode autovalidateMode = AutovalidateMode.disabled,
VoidCallback? onReset,
FocusNode? focusNode,
required this.items,
this.isExpanded = true,
this.isDense = true,
this.elevation = 8,
this.iconSize = 24.0,
this.hint,
this.style,
this.disabledHint,
this.icon,
this.iconDisabledColor,
this.iconEnabledColor,
this.allowClear = false,
this.clearIcon = const Icon(
Icons.close,
size: 18,
color: CustomColor.textGray,
),
this.onTap,
this.autofocus = false,
this.dropdownColor,
this.focusColor,
this.itemHeight = kMinInteractiveDimension,
this.selectedItemBuilder,
}) : /*: assert(allowClear == true || clearIcon != null)*/ super(
key: key,
initialValue: initialValue,
name: name,
validator: validator,
valueTransformer: valueTransformer,
onChanged: onChanged,
autovalidateMode: autovalidateMode,
onSaved: onSaved,
enabled: enabled,
onReset: onReset,
decoration: decoration,
focusNode: focusNode,
builder: (FormFieldState<T?> field) {
final _SelectState<T> state = field as _SelectState<T>;
// DropdownButtonFormField
// TextFormField
void changeValue(T? value) {
state.didChange(value);
}
return InputDecorator(
decoration: state.decoration().copyWith(
floatingLabelBehavior:
hint == null ? decoration.floatingLabelBehavior : FloatingLabelBehavior.always,
filled: true,
fillColor: CustomColor.bgGrayLight,
),
isEmpty: state.value == null,
child: Row(
children: <Widget>[
Expanded(
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
isExpanded: isExpanded,
hint: hint,
items: items,
value: field.value, //field.value,
style: style,
isDense: isDense,
disabledHint: field.value != null
? (items.firstWhereOrNull((val) => val.value == field.value)?.child ??
Text(field.value.toString()))
: disabledHint,
elevation: elevation,
iconSize: iconSize,
// icon: icon ?? svgImage(Images.icArrowDown, color: CustomColor.textGray, height: 8),
iconDisabledColor: iconDisabledColor,
iconEnabledColor: iconEnabledColor,
onChanged: state.enabled ? (T? value) => changeValue(value) : null,
onTap: onTap,
focusNode: state.effectiveFocusNode,
autofocus: autofocus,
dropdownColor: dropdownColor,
focusColor: focusColor,
itemHeight: itemHeight,
selectedItemBuilder: selectedItemBuilder,
),
),
),
if (allowClear && state.enabled && field.value != null) ...[
ConstantWidget.widthSpace10,
InkWell(
onTap: () => changeValue(null),
child: clearIcon,
),
]
],
),
);
},
);
@override
_SelectState<T> createState() => _SelectState<T>();
}
class _SelectState<T> extends FormBuilderFieldState<CustomSelect<T>, T> {}