260 lines
9.6 KiB
Dart
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> {}
|