feat: enhance NewPage UI with dynamic bottom sheet for form input and restructure SDUI JSON schema for improved data handling
This commit is contained in:
@@ -25,30 +25,39 @@ class NewPage extends GetView<NewPageLogic> {
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 24.h),
|
||||
ObxValue((data) {
|
||||
if (data.value == null) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Obx(
|
||||
() => SDUIFormWidget(
|
||||
model: data.value!,
|
||||
controllers: controller.controllers,
|
||||
state: controller.formState,
|
||||
onStateChanged: (key, value) {
|
||||
controller.formState[key] = value;
|
||||
},
|
||||
images: controller.images,
|
||||
onImagesChanged: (key, imageList) {
|
||||
controller.images[key] = imageList;
|
||||
},
|
||||
),
|
||||
);
|
||||
}, controller.sduiModel),
|
||||
Row(children: []),
|
||||
SizedBox(height: 24.h),
|
||||
RElevated(
|
||||
text: 'دکمه نمونه',
|
||||
onPressed: () {
|
||||
controller.onButtonPressed();
|
||||
Get.bottomSheet(
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
|
||||
|
||||
BaseBottomSheet(
|
||||
height: Get.height * 0.8,
|
||||
child: ObxValue((data) {
|
||||
if (data.value == null) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Obx(
|
||||
() => SDUIFormWidget(
|
||||
model: data.value!,
|
||||
controllers: controller.controllers,
|
||||
state: controller.formState,
|
||||
onStateChanged: (key, value) {
|
||||
controller.formState[key] = value;
|
||||
},
|
||||
images: controller.images,
|
||||
onImagesChanged: (key, imageList) {
|
||||
controller.images[key] = imageList;
|
||||
},
|
||||
),
|
||||
);
|
||||
}, controller.sduiModel),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 24.h),
|
||||
|
||||
@@ -1,13 +1,149 @@
|
||||
{
|
||||
"type": "card_label_item",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"title": "اطلاعات مزرعه",
|
||||
"padding_horizontal": 12.0,
|
||||
"padding_vertical": 11.0
|
||||
},
|
||||
"child": {
|
||||
|
||||
"info": {
|
||||
"type": "column",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"spacing": 10.0,
|
||||
"crossAxisAlignment": "start"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "card_label_item",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"title": "اطلاعات پایه واحد",
|
||||
"padding_horizontal": 12.0,
|
||||
"padding_vertical": 11.0
|
||||
},
|
||||
"child": {
|
||||
"type": "column",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"spacing": 10.0
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "unit_name",
|
||||
"label": "نام واحد مرغداری",
|
||||
"keyboard_type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "breeding_unique_id",
|
||||
"label": "(عدد معمولی)کد یکتا / شناسه واحد",
|
||||
"keyboard_type": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "health",
|
||||
"label": "پروانه بهداشتی",
|
||||
"keyboard_type": "number"
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "health_license",
|
||||
"label": "عدد اعشاری ",
|
||||
"keyboard_type": "number",
|
||||
"decimal": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "health_q",
|
||||
"label": "عدد اعشاری با 2 رقم اعشار",
|
||||
"keyboard_type": "number",
|
||||
"decimal": true,
|
||||
"decimal_places": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text_form_field",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "unit_date",
|
||||
"label": "نام تاریییییییییییخ",
|
||||
"keyboard_type": "text",
|
||||
"type": "date_picker"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "chip_selection",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "grain_quality",
|
||||
"label": "کیفیت دانه",
|
||||
"selectedIndex": -1,
|
||||
"options": [
|
||||
{
|
||||
"index": 0,
|
||||
"label": "خوب",
|
||||
"value": "خوب"
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"label": "متوسط",
|
||||
"value": "متوسط"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"label": "ضعیف",
|
||||
"value": "ضعیف"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "dropdown",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "training_status",
|
||||
"label": "آموزشدیده در حوزه بهداشت و امنیت زیستی",
|
||||
"placeholder": "آموزشدیده در حوزه بهداشت و امنیت زیستی",
|
||||
"items": [
|
||||
"بله",
|
||||
"خیر"
|
||||
],
|
||||
"selectedValue": null,
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "image_picker",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "hall_images",
|
||||
"label": "ثبت عکس سالن (حداقل ۳ زاویه)",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "image_picker",
|
||||
"visible": true,
|
||||
"data": {
|
||||
"key": "hall_images_klllll",
|
||||
"label": "ثبت عکس",
|
||||
"required": true,
|
||||
"max_images": 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -213,86 +213,98 @@ class _RTextFieldState extends State<RTextField> {
|
||||
return widget.height;
|
||||
}
|
||||
|
||||
bool get _isMultiLine {
|
||||
return (widget.maxLines != null && widget.maxLines! > 1) ||
|
||||
(widget.minLines != null && widget.minLines! > 1);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: _calculateHeight().h,
|
||||
child: Padding(
|
||||
padding: widget.padding ?? EdgeInsets.zero,
|
||||
child: TextFormField(
|
||||
key: _formFieldKey,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
textAlign: widget.textAlign ?? TextAlign.start,
|
||||
readOnly: widget.readonly,
|
||||
minLines: widget.minLines,
|
||||
maxLines: widget.maxLines,
|
||||
onChanged: (value) {
|
||||
widget.onChanged?.call(value);
|
||||
},
|
||||
validator: (value) {
|
||||
final error = widget.validator?.call(value);
|
||||
final Widget textField = Padding(
|
||||
padding: widget.padding ?? EdgeInsets.zero,
|
||||
child: TextFormField(
|
||||
key: _formFieldKey,
|
||||
textAlignVertical: _isMultiLine
|
||||
? TextAlignVertical.top
|
||||
: TextAlignVertical.center,
|
||||
controller: widget.controller,
|
||||
focusNode: widget.focusNode,
|
||||
textAlign: widget.textAlign ?? TextAlign.start,
|
||||
readOnly: widget.readonly,
|
||||
minLines: widget.minLines,
|
||||
maxLines: widget.maxLines,
|
||||
onChanged: (value) {
|
||||
widget.onChanged?.call(value);
|
||||
},
|
||||
validator: (value) {
|
||||
final error = widget.validator?.call(value);
|
||||
|
||||
if (widget.isFullHeight) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
return error;
|
||||
},
|
||||
inputFormatters: widget.inputFormatters,
|
||||
enabled: widget.enabled,
|
||||
obscureText: obscure,
|
||||
onTap: widget.onTap,
|
||||
onTapOutside: (_) => FocusScope.of(context).unfocus(),
|
||||
onFieldSubmitted: widget.onSubmitted,
|
||||
maxLength: widget.maxLength,
|
||||
textDirection: textDirection,
|
||||
style: widget.style,
|
||||
keyboardType: widget.keyboardType,
|
||||
autovalidateMode:
|
||||
widget.autoValidateMode ?? AutovalidateMode.disabled,
|
||||
cursorColor: widget.cursorColor,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
autocorrect: widget.autocorrect ?? true,
|
||||
enableSuggestions: widget.enableSuggestions ?? true,
|
||||
textInputAction: widget.textInputAction,
|
||||
autofillHints: widget.autofillHints,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: widget.isFullHeight ? widget.height / 3 : 0,
|
||||
),
|
||||
|
||||
errorStyle: widget.errorStyle,
|
||||
errorMaxLines: 1,
|
||||
isDense: widget.isDense,
|
||||
suffix: widget.suffix,
|
||||
suffixIcon: _buildSuffixIcon(),
|
||||
suffixIconConstraints: widget.boxConstraints,
|
||||
prefixIcon: widget.prefixIcon,
|
||||
prefixIconConstraints: widget.boxConstraints,
|
||||
hintText: widget.hintText,
|
||||
labelText: widget.label,
|
||||
alignLabelWithHint: true,
|
||||
labelStyle: AppFonts.yekan14
|
||||
.copyWith(color: AppColor.lightGreyDarkActive)
|
||||
.merge(widget.labelStyle),
|
||||
filled:
|
||||
widget.filled || widget._noBorder || widget._passwordNoBorder,
|
||||
fillColor: widget.filledColor,
|
||||
counter: widget.showCounter ? null : const SizedBox(),
|
||||
hintStyle: widget.hintStyle,
|
||||
enabledBorder: widget._inputBorder,
|
||||
focusedBorder: widget.focusedBorder ?? widget._inputBorder,
|
||||
border: widget._inputBorder,
|
||||
if (widget.isFullHeight) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
return error;
|
||||
},
|
||||
inputFormatters: widget.inputFormatters,
|
||||
enabled: widget.enabled,
|
||||
obscureText: obscure,
|
||||
onTap: widget.onTap,
|
||||
onTapOutside: (_) => FocusScope.of(context).unfocus(),
|
||||
onFieldSubmitted: widget.onSubmitted,
|
||||
maxLength: widget.maxLength,
|
||||
textDirection: textDirection,
|
||||
style: widget.style,
|
||||
keyboardType: widget.keyboardType,
|
||||
autovalidateMode: widget.autoValidateMode ?? AutovalidateMode.disabled,
|
||||
cursorColor: widget.cursorColor,
|
||||
textCapitalization: widget.textCapitalization,
|
||||
autocorrect: widget.autocorrect ?? true,
|
||||
enableSuggestions: widget.enableSuggestions ?? true,
|
||||
textInputAction: widget.textInputAction,
|
||||
autofillHints: widget.autofillHints,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: _isMultiLine
|
||||
? 12
|
||||
: (widget.isFullHeight ? widget.height / 3 : 0),
|
||||
),
|
||||
|
||||
errorStyle: widget.errorStyle,
|
||||
errorMaxLines: 1,
|
||||
isDense: widget.isDense,
|
||||
suffix: widget.suffix,
|
||||
suffixIcon: _buildSuffixIcon(),
|
||||
suffixIconConstraints: widget.boxConstraints,
|
||||
prefixIcon: widget.prefixIcon,
|
||||
prefixIconConstraints: widget.boxConstraints,
|
||||
hintText: widget.hintText,
|
||||
labelText: widget.label,
|
||||
alignLabelWithHint: true,
|
||||
labelStyle: AppFonts.yekan14
|
||||
.copyWith(color: AppColor.lightGreyDarkActive)
|
||||
.merge(widget.labelStyle),
|
||||
filled: widget.filled || widget._noBorder || widget._passwordNoBorder,
|
||||
fillColor: widget.filledColor,
|
||||
counter: widget.showCounter ? null : const SizedBox(),
|
||||
hintStyle: widget.hintStyle,
|
||||
enabledBorder: widget._inputBorder,
|
||||
focusedBorder: widget.focusedBorder ?? widget._inputBorder,
|
||||
border: widget._inputBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// برای فیلدهای تک خطی، ارتفاع ثابت اعمال میکنیم
|
||||
// برای فیلدهای چند خطی، اجازه میدهیم ارتفاع به صورت خودکار تنظیم شود
|
||||
if (_isMultiLine) {
|
||||
return textField;
|
||||
}
|
||||
|
||||
return SizedBox(height: _calculateHeight().h, child: textField);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user