renderProperty method

Iterable<Widget> renderProperty(
  1. BuildContext context,
  2. IMetaFormContext metaForm,
  3. HandledPaths paths,
  4. SunnyFormFieldState state, {
  5. Key? key,
})
override

Renders a widget (or null) for a provided HandledPaths (see acceptProperties)

Implementation

Iterable<Widget> renderProperty(BuildContext context,
    IMetaFormContext metaForm, HandledPaths paths, SunnyFormFieldState state,
    {Key? key}) {
  final prop = paths.property;
  final fieldContext = metaForm.prop(paths.property!);
  assert(metaForm.isEmbed == paths.isEmbed,
      "The form context and paths context should match embed");

  Widget buildWidget() {
    final schemaType = prop!.type!.replaceAll("Schema", "");

    final fullPath = paths.fullPath();

    final resolvedPlaceholder =
        fieldContext.overrides.resolvePlaceholder(fullPath);
    Widget _imageUploadWidget(
        IMSchemaProperty? prop, SunnyFormFieldState<Uri> state) {
      final readyCheck = state.form?.requestReadyCheck(fullPath);

      return ImageUrlControl(
        key: Key("field-${fullPath.toKey()}"),
        initialValue: state.value,
        size: 50,
        readyCheck: readyCheck,
        type: 'picture',
        imageId: () => uuid(),
        allowThirdParty: true,
        onUploaded: (uri) {
          state.updateValue(uri, AttributeSource.control);
        },
        onError: ((err, stack) {
          log.warning("Error uploading: $err", err, stack);
          state.error = ValidationError(
              path: fullPath, message: "Error uploading image");
        }),
      );
    }

    Widget _doubleWidget(
        IMSchemaProperty prop, SunnyFormFieldState<double> state,
        {Icon? icon}) {
      return TextControl<double?>.ofFormField(
        state,
        key: Key("field-$fullPath"),
        prefix: icon,
        validators: [FormBuilderValidators.numeric()],
        placeholder: resolvedPlaceholder,
        isRequired: prop.isRequired,
        converter: InputConverters.doubles,
        autofocus: false,
        minLines: 1,
        maxLines: 1,
        formatter: (_double) => _double.formatNumber(fixed: 2) ?? '',
      );
    }

    Widget _defaultWidget(IMSchemaProperty prop, SunnyFormFieldState state) {
      // Let's try to reason about the field
      final byType =
          textFieldOverridesByUri["${prop.uri}"] ?? const TextConfig();
      final merged = byType.merge(fieldContext.formConfig<TextConfig>());

      return TextControl<dynamic>.ofFormField(
        state,
        key: Key("field-$fullPath"),
        placeholder: resolvedPlaceholder,
        isRequired: prop.isRequired,
        autofocus: false,
        textCapitalization: merged.textCapitalization,
        keyboardType: merged.textInputType,
        autocorrect: merged.autocorrect,
        enabled: merged.isDisabled != true,
        minLines: 1,
        maxLines: 1,
      );
    }

    Widget _checkboxWidget(
        IMSchemaProperty? prop, SunnyFormFieldState<bool> state) {
      return Wrap(
        crossAxisAlignment: WrapCrossAlignment.center,
        spacing: 10,
        children: [
          Text(
            resolvedPlaceholder ?? prop!.description ?? prop!.path ?? "Field",
            style: SunnyTextStyles.appBarInput,
          ),
          PlatformSwitch(
            widgetKey: Key("field-$fullPath"),
            key: Key("$fullPath-switch"),
            value: state.value == true,
            onChanged: (checked) =>
                state.updateValue(checked, AttributeSource.control),
          ),
        ],
      );
    }
//
//        String _resolvedLabel(IMSchemaProperty prop, [JsonPath fullPath]) {
//          final label = this.label ?? fieldContext.overrides.resolveLabel(fullPath ?? fullPath) ?? prop.label;
//          if (prop.isNotRequired) {
//            return label;
//          } else {
//            return "$label *";
//          }
//        }

//        String _resolvedPlaceholder(IMSchemaProperty prop) {
//          return fieldContext.overrides.resolvePlaceholder(fullPath);
//        }

    Widget _bigStringWidget(
        IMSchemaProperty prop, SunnyFormFieldState<String> state,
        {int? minLines, int? maxLines}) {
      final config =
          fieldContext.formConfig<TextAreaConfig>() ?? const TextAreaConfig();
      return TextControl<dynamic>.ofFormField(
        state,
        key: Key("field-$fullPath"),
        placeholder: resolvedPlaceholder,
        isRequired: prop.isRequired,
        autofocus: false,
        textCapitalization: TextCapitalization.sentences,
        textInputAction: TextInputAction.newline,
        keyboardType: TextInputType.multiline,
        autocorrect: config.autocorrect ?? true,
        enabled: config.isDisabled != true,
        minLines:
            config.expands == true ? null : minLines ?? config.minLines ?? 2,
        maxLines:
            config.expands == true ? null : maxLines ?? config.maxLines ?? 3,
      );
    }

    Widget _dateTimeWidget(
        IMSchemaProperty? prop, SunnyFormFieldState<DateTime?> state) {
      return DateTypeaheadControl(
        key: Key("typeahead-$fullPath"),
        placeholder: resolvedPlaceholder ?? "e.g. April 6, 2013",
        focusNode: state.focusNode,
        initialValue: state.value,
        onChange: (dateString, source) {
          state.updateValue(dateString?.resultDate, source);
        },
      );
    }

    Widget _embeddedWidget(
        IEmbeddedProperty prop, SunnyFormFieldState state) {
      return MetaForm.ofMModel(
        context,
        id: "${state.attribute}",
        ref: prop.embedRef,
        model: prop.embedRef!.newInstance(),
      );
    }

    Widget _dateWidget(
        IMSchemaProperty? prop, SunnyFormFieldState<DateTime> state) {
      return DateTypeaheadControl(
        key: Key("field-$fullPath"),
        placeholder: resolvedPlaceholder,
        focusNode: state.focusNode,
        enabled: state.isEnabled,
        onChange: (SmartDateQueryResult? dateResult, source) {
          final localDate = dateResult!.resultDate!.toLocal();
          state.updateValue(localDate, source);
        },
        initialValue: state.value,
      );
    }

    Widget _flexiDateWidget(
        IMSchemaProperty? prop, SunnyFormFieldState<FlexiDate?> state) {
      return DateTypeaheadControl(
        key: Key("field-$fullPath"),
        placeholder: resolvedPlaceholder,
        inputContext: SmartDateOptionsConfig.flexiDate(),
        focusNode: state.focusNode,
        initialValue: state.value,
        onChange: (SmartDateQueryResult? dateResult, source) {
          state.updateValue(dateResult?.flexiDate, source);
        },
      );
    }

    Widget _listWidget(BuildContext context, IListProperty prop,
        SunnyFormFieldState<List> state) {
      final itemProp = prop.allItems!;
      String? optionKey = itemProp.baseCode;
      var config = fieldContext.formConfig<SelectOptionConfig>();
      if (optionKey == ImageContentRef.baseCode) {
        // ignore: unused_local_variable
        final readyCheck = state.form?.requestReadyCheck(fullPath);
        // return ImageUrlControl(
        //   key: Key("field-$fullPath"),
        //   placeholder: textOrNull(resolvedPlaceholder),
        //   // initialValue: state.value?.cast(),
        //   onUploaded: (newValue) {
        //     state.updateValue(newValue, AttributeSource.control);
        //   },
        //   isReadOnly: state.isEnabled == false,
        //   onError: (err, stack) {
        //     log.warning("Error with pictures: $err", err, stack);
        //     return SunnyHud.error(context, "Error with pictures: $err");
        //   },
        // );
        return emptyBox;
      } else {
        // if (optionKey == "mverse.mthing.definitions.mKey") {
        //   return Container(child: Text("Fix this"));
        //
        //   // optionKey = "${ContactRef.baseCode}";
        // }
        KeyedOptionsHandler handler =
            (config?.handler as KeyedOptionsHandler?) ??
                context.optionsService.getKeyedHandler(
                  optionKey,
                  metaContext: fieldContext,
                );

        if (itemProp is IStringSelectProperty) {
          handler = OptionsHandler.ofProperty(itemProp, allowAdHoc: false);
        }

        final typeaheadRenderer = config?.viewHandler ??
            optionViewService
                .getTypeaheadRenderer<dynamic, dynamic>(optionKey);
        return ListSelectControl<dynamic, dynamic>.ofKey(
          key: Key("field-$fullPath"),
          id: "$fullPath",
          handler: handler,
          initialValue: state.value,
          onChange: (list, source) {
            state.updateValue(handler.castList(list), source);
          },
          viewHandler: typeaheadRenderer,
          isRequired: prop.isRequired ?? false,
          selectedType: "${itemProp.uri}",
          placeholder: resolvedPlaceholder,
          focusNode: state.focusNode,
        );
      }
    }

    Widget _stringSelectWidget(BuildContext context,
        IStringSelectProperty prop, SunnyFormFieldState state) {
      final formatOption = (KeyedOption option) {
        return properCase(
            splitSnakeCase("${option.value ?? ""}".toLowerCase()));
      };
      final handler = OptionsHandler.ofEnum(prop.baseCode!);
      // Show typeahead
      return OptionSelectorControl(
        key: Key("field-$fullPath"),
        handler: handler,
        focusNode: state.focusNode,
        isRequired: prop.isRequired,
        viewHandler: defaultOptionViewHandler(
            showExpand: true,
            focusMode: handler.canShowAll
                ? TypeaheadFocusMode.showAll
                : TypeaheadFocusMode.showFiltered,
            renderSuggestionTile: renderSimpleTypeaheadOption(
              title: formatOption,
              isAdhocCreator: false,
            )),
//        selectedType: prop.baseCode,
        onChange: (value, source) => state.updateValue(value?.key, source),
        onError: (error) => state.error = error,
        initialValue: state.value,
        placeholder: resolvedPlaceholder,
      );
    }

    Widget _selectWidget(BuildContext context, IMSchemaProperty prop,
        SunnyFormFieldState state) {
      final config = fieldContext.formConfig<SelectOptionConfig>();
      final handler = (config?.handler as KeyedOptionsHandler?) ??
          optionService.getKeyedHandler(prop.baseCode,
              metaContext: fieldContext);
      final viewHandler = config?.viewHandler ??
          optionViewService.getTypeaheadRenderer(prop.baseCode);

      return OptionSelectorControl(
        key: Key("field-$fullPath"),
        handler: handler,
        resultFilter: config?.resultFilter,
        focusNode: state.focusNode,
        isRequired: prop.isRequired,
        viewHandler: viewHandler,
//        selectedType: prop.baseCode,
        onChange: (value, source) => state.updateValue(value?.key, source),
        onError: (error) => state.error = error,
        initialKeyValue: state.value,
        placeholder: resolvedPlaceholder,
        prefixIcon: config?.prefixIcon,
      );
    }

    Widget _intWidget(IMSchemaProperty prop, SunnyFormFieldState<int> state) {
      return TextControl<int?>.ofFormField(
        state,
        converter: InputConverters.ints,
        validators: [
          FormBuilderValidators.numeric(),
        ],
        placeholder: resolvedPlaceholder,
        isRequired: prop.isRequired,
        keyboardType: TextInputType.numberWithOptions(decimal: false),
        minLines: 1,
        autofocus: false,
        maxLines: 1,
      );
    }

    if (prop.isType(Definitions.liquidContent)) {
      return _bigStringWidget(prop, state.cast(), minLines: 10, maxLines: 20);
    }

    if (prop is IListProperty) {
      return _listWidget(context, prop, state.cast());
    } else if (prop is IStringSelectProperty) {
      return _stringSelectWidget(context, prop, state.cast());
    }
    switch (schemaType) {
      case "boolean":
        return _checkboxWidget(prop, state.cast());
      case "bigString":
        return _bigStringWidget(prop, state.cast());
      case "int":
        return _intWidget(prop, state.cast());
      case "long":
        return _intWidget(prop, state.cast());
      case "double":
        return _doubleWidget(prop, state.cast());
      case "currency":
        return _doubleWidget(prop, state.cast(),
            icon: const Icon(Icons.attach_money));
      case "date":
        return _dateWidget(prop, state.cast());
      case "string":
        if (optionsService.contains(prop.baseCode ?? '') ||
            fieldContext.formConfig<SelectOptionConfig>() != null) {
          return _selectWidget(context, prop, state);
        } else {
          return _defaultWidget(prop, state.cast());
        }
      case "flexiDate":
        return _flexiDateWidget(prop, state.cast());
      case "dateTime":
        return _dateTimeWidget(prop, state.cast());
      case "embedded":
        return _embeddedWidget(prop as IEmbeddedProperty, state);
      default:
        if (prop.isType(Definitions.imageUrl)) {
          return _imageUploadWidget(prop, state.cast());
        } else {
          return _defaultWidget(prop, state.cast());
        }
    }
  }

  return [buildWidget()];
}