FormeAsyncInputChip<T extends Object> constructor

FormeAsyncInputChip<T extends Object>({
  1. Key? key,
  2. String? name,
  3. List<T> initialValue = const [],
  4. FormeAsyncValidator<List<T>>? asyncValidator,
  5. Duration? asyncValidatorDebounce,
  6. AutovalidateMode? autovalidateMode,
  7. FormeFieldDecorator<List<T>>? decorator,
  8. bool enabled = true,
  9. FocusNode? focusNode,
  10. FormeFieldInitialized<List<T>>? onInitialized,
  11. FormeFieldSetter<List<T>>? onSaved,
  12. FormeFieldStatusChanged<List<T>>? onStatusChanged,
  13. int? order,
  14. bool quietlyValidate = false,
  15. bool readOnly = false,
  16. bool requestFocusOnUserInteraction = true,
  17. FormeFieldValidationFilter<List<T>>? validationFilter,
  18. FormeValidator<List<T>>? validator,
  19. AutocompleteOptionToString<T> displayStringForOption = RawAutocomplete.defaultStringForOption,
  20. double optionsMaxHeight = 200,
  21. Duration? debounce,
  22. required FormeAsyncAutocompleteOptionsBuilder<T> optionsBuilder,
  23. FormeSearchCondition? searchCondition,
  24. FormeAsyncInputChipFieldViewBuilder<T>? fieldViewBuilder,
  25. InputDecoration? fieldViewDecoration = const InputDecoration(),
  26. FormeFieldDecorator<List<T>>? fieldViewdecorator,
  27. FormeAsyncInputChipBuilder<T>? inputChipBuilder,
  28. FormeAsyncInputChipOptionsViewBuilder<T>? optionsViewBuilder,
  29. InputDecoration? searchInputDecoration,
  30. double searchInputMinWidth = 50,
  31. double? searchInputStepWidth,
  32. WrapAlignment wrapAlignment = WrapAlignment.start,
  33. Clip wrapClipBehavior = Clip.none,
  34. WrapCrossAlignment wrapCrossAxisAlignment = WrapCrossAlignment.center,
  35. Axis wrapDirection = Axis.horizontal,
  36. WrapAlignment wrapRunAlignment = WrapAlignment.start,
  37. double wrapRunSpacing = 0.0,
  38. double wrapSpacing = 4.0,
  39. TextDirection? wrapTextDirection,
  40. VerticalDirection wrapVerticalDirection = VerticalDirection.down,
  41. EdgeInsets? fieldViewPadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 6),
})

Implementation

FormeAsyncInputChip({
  super.key,
  super.name,
  super.initialValue = const [],
  super.asyncValidator,
  super.asyncValidatorDebounce,
  super.autovalidateMode,
  super.decorator,
  super.enabled = true,
  super.focusNode,
  super.onInitialized,
  super.onSaved,
  super.onStatusChanged,
  super.order,
  super.quietlyValidate = false,
  super.readOnly = false,
  super.requestFocusOnUserInteraction = true,
  super.validationFilter,
  super.validator,
  this.displayStringForOption = RawAutocomplete.defaultStringForOption,
  this.optionsMaxHeight = 200,
  this.debounce,
  required this.optionsBuilder,
  this.searchCondition,
  this.fieldViewBuilder,
  this.fieldViewDecoration = const InputDecoration(),
  this.fieldViewdecorator,
  this.inputChipBuilder,
  this.optionsViewBuilder,
  this.searchInputDecoration,
  this.searchInputMinWidth = 50,
  this.searchInputStepWidth,
  this.wrapAlignment = WrapAlignment.start,
  this.wrapClipBehavior = Clip.none,
  this.wrapCrossAxisAlignment = WrapCrossAlignment.center,
  this.wrapDirection = Axis.horizontal,
  this.wrapRunAlignment = WrapAlignment.start,
  this.wrapRunSpacing = 0.0,
  this.wrapSpacing = 4.0,
  this.wrapTextDirection,
  this.wrapVerticalDirection = VerticalDirection.down,
  this.fieldViewPadding =
      const EdgeInsets.symmetric(vertical: 8, horizontal: 6),
}) : super.allFields(
        builder: (genericState) {
          final FormeAsyncInputChipState<T> state =
              genericState as FormeAsyncInputChipState<T>;
          final bool readOnly = state.readOnly;
          return RawAutocomplete<T>(
            focusNode: state.focusNode,
            textEditingController: state._textEditingController,
            onSelected: (T t) {
              state._add([t]);
            },
            optionsViewBuilder: (BuildContext context,
                AutocompleteOnSelected<T> onSelected, Iterable<T> options) {
              SchedulerBinding.instance.addPostFrameCallback((timestamp) {
                state._optionsViewVisibleStateNotifier.value = true;
              });

              void multiSelect(List<T> options) {
                state._add(options);
              }

              return LayoutBuilder(builder: (context, constraints) {
                final Size? size = state._fieldSizeGetter?.call();
                final double? width = size?.width;
                return optionsViewBuilder?.call(
                        state, context, multiSelect, state._options, width) ??
                    FormeMultiAutocompleteOptions(
                      displayStringForOption: displayStringForOption,
                      onSelected: multiSelect,
                      options: state._options,
                      maxOptionsHeight: optionsMaxHeight,
                      width: width,
                    );
              });
            },
            optionsBuilder: (TextEditingValue value) {
              if (state._stateNotifier.value ==
                  FormeAsyncOperationState.success) {
                return state._options;
              }
              return const Iterable.empty();
            },
            displayStringForOption: displayStringForOption,
            fieldViewBuilder:
                (context, textEditingController, focusNode, onSubmitted) {
              Widget field;
              if (fieldViewBuilder != null) {
                field = fieldViewBuilder(context, state.value, state,
                    state.effectiveController, focusNode, onSubmitted);
              } else {
                field = TextField(
                  enabled: state.enabled,
                  decoration: searchInputDecoration,
                  focusNode: focusNode,
                  controller: state.effectiveController,
                  readOnly: readOnly,
                  onSubmitted: readOnly
                      ? null
                      : (v) {
                          onSubmitted();
                        },
                );

                field = Wrap(
                  direction: wrapDirection,
                  alignment: wrapAlignment,
                  runAlignment: wrapRunAlignment,
                  runSpacing: wrapRunSpacing,
                  spacing: wrapSpacing,
                  crossAxisAlignment: wrapCrossAxisAlignment,
                  textDirection: wrapTextDirection,
                  verticalDirection: wrapVerticalDirection,
                  clipBehavior: wrapClipBehavior,
                  children: state.value.map<Widget>((e) {
                    void onDeleted() {
                      state.delete(e);
                    }

                    if (inputChipBuilder != null) {
                      return inputChipBuilder(
                          e, state.readOnly ? null : onDeleted);
                    }

                    return InputChip(
                      label: Text(displayStringForOption(e)),
                      onDeleted: state.readOnly ? null : onDeleted,
                    );
                  }).toList()
                    ..add(
                      IntrinsicWidth(
                        stepWidth: searchInputStepWidth,
                        child: Container(
                          constraints:
                              BoxConstraints(minWidth: searchInputMinWidth),
                          child: field,
                        ),
                      ),
                    ),
                );
              }

              final FormeFieldDecorator<List<T>>? finalDecorator =
                  fieldViewdecorator ??
                      (fieldViewDecoration == null
                          ? null
                          : FormeInputDecorationDecorator(
                              childBuilder: (context, child) {
                                return Padding(
                                  padding:
                                      fieldViewPadding ?? EdgeInsets.zero,
                                  child: child,
                                );
                              },
                              decorationBuilder: (context) =>
                                  fieldViewDecoration,
                              emptyChecker: (value, state) {
                                return state.value.isEmpty &&
                                    (state as FormeAsyncInputChipState<T>)
                                        .effectiveController
                                        .text
                                        .isEmpty;
                              }));

              field = finalDecorator == null
                  ? field
                  : finalDecorator.build(context, field, state);

              if (fieldViewBuilder == null) {
                field = GestureDetector(
                    onTap: () {
                      final bool needFocus = !state.focusNode.hasFocus;
                      if (needFocus) {
                        state.focusNode.requestFocus();
                      }
                      if (state.focusNode.hasFocus && !kIsWeb) {
                        SystemChannels.textInput
                            .invokeMethod<Object>('TextInput.show');
                      }
                    },
                    child: field);
              }
              return _FieldView(state: state, child: field);
            },
          );
        },
      );