renderProperty method
Iterable<Widget>
renderProperty(
- BuildContext context,
- IMetaFormContext metaForm,
- HandledPaths paths,
- SunnyFormFieldState state, {
- 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()];
}