diff --git a/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart b/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart index f2d55a0..7f4786d 100644 --- a/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart +++ b/packages/chicken/lib/presentation/widget/sdui/form/sdui_form_widget.dart @@ -255,7 +255,7 @@ class SDUIFormWidget extends StatelessWidget { iLog('Stack trace: $stackTrace'); return Container( padding: EdgeInsets.all(16), - color: Colors.orange.withOpacity(0.1), + color: Colors.orange.withAlpha(10), child: Text('Card Error: $e'), ); } @@ -526,7 +526,7 @@ class SDUIFormWidget extends StatelessWidget { iLog('Error building stepper: $e'); iLog('Stack trace: $stackTrace'); return Container( - padding: EdgeInsets.all(16), + padding: EdgeInsets.symmetric(horizontal: 16), color: Colors.orange.withAlpha(10), child: Text('Stepper Error: $e'), ); diff --git a/packages/chicken/lib/presentation/widget/sdui/form/ts_with_stepper copy.json b/packages/chicken/lib/presentation/widget/sdui/form/ts_with_stepper copy.json new file mode 100644 index 0000000..a76a847 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/form/ts_with_stepper copy.json @@ -0,0 +1,640 @@ +{ + "info": { + "type": "column", + "visible": true, + "visible_condition": null, + "spacing": 30.0, + "children": [ + { + "type": "stepper", + "visible": true, + "visible_condition": null, + "data": { + "key": "activeStepperIndex", + "total_steps": 2, + "active_step": 0 + } + }, + { + "type": "card_label_item", + "visible": true, + "visible_condition": "activeStepperIndex == 0", + "data": { + "title": "اطلاعات پایه واحد", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "visible_condition": null, + "spacing": 10.0, + "children": [ + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "unit_name", + "label": "نام واحد مرغداری", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "breeding_unique_id", + "label": "کد یکتا / شناسه واحد", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "health_license", + "label": "پروانه بهداشتی", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "tenant_status", + "label": "وضعیت مستاجر", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": "tenant_status == 'دارد'", + "data": { + "key": "tenant_name", + "label": "نام مستاجر", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": "tenant_status == 'دارد'", + "data": { + "key": "tenant_national_id", + "label": "کد ملی مستاجر", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": "tenant_status == 'دارد'", + "data": { + "key": "tenant_phone_number", + "label": "شماره تماس مستاجر", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "owner_national_code", + "label": "کد ملی بهره‌بردار", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "owner_phone_number", + "label": "شماره تماس بهره‌بردار", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "total_capacity", + "label": "ظرفیت اسمی سالن‌ها", + "keyboard_type": "number", + "comma_separator": true + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "visible_condition": "activeStepperIndex == 0", + "data": { + "title": "اطلاعات جوجه ریزی", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "visible_condition": null, + "spacing": 10.0, + "children": [ + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_date", + "label": "تاریخ جوجه ریزی", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "visit_date", + "label": "تاریخ بازدید", + "keyboard_type": "text", + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_count", + "label": "تعداد جوجه‌ریزی اولیه", + "keyboard_type": "number", + "readonly": true, + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_breed", + "label": "نوع نژاد", + "keyboard_type": "text" + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_average_weight", + "label": "میانگین وزن جوجه", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "image_picker", + "visible": true, + "visible_condition": null, + "data": { + "key": "pultry_images", + "label": "تعداد موجود فعلی" + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "sanitary_condition_of_the_hall", + "label": "وضعیت بهداشتی سالن", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "ventilation_status", + "label": "وضعیت تهویه", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "bedding_status", + "label": "وضعیت بستر", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "خشک", + "value": "خشک" + }, + { + "index": 1, + "label": "نیمه‌مرطوب", + "value": "نیمه‌مرطوب" + }, + { + "index": 2, + "label": "مرطوب", + "value": "مرطوب" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_temperature", + "label": "دمای سالن", + "keyboard_type": "number" + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "visible_condition": "activeStepperIndex == 1", + "data": { + "title": "وضعیت عمومی سالن", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "visible_condition": null, + "spacing": 10.0, + "children": [ + { + "type": "image_picker", + "visible": true, + "visible_condition": null, + "data": { + "key": "pultry_images", + "label": "تعداد موجود فعلی" + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "sanitary_condition_of_the_hall", + "label": "وضعیت بهداشتی سالن", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "ventilation_status", + "label": "وضعیت تهویه", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "عالی", + "value": "عالی" + }, + { + "index": 1, + "label": "خوب", + "value": "خوب" + }, + { + "index": 2, + "label": "متوسط", + "value": "متوسط" + }, + { + "index": 3, + "label": "ضعیف", + "value": "ضعیف" + } + ] + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "bedding_status", + "label": "وضعیت بستر", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "خشک", + "value": "خشک" + }, + { + "index": 1, + "label": "نیمه‌مرطوب", + "value": "نیمه‌مرطوب" + }, + { + "index": 2, + "label": "مرطوب", + "value": "مرطوب" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "hatching_temperature", + "label": "دمای سالن", + "keyboard_type": "number" + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": null, + "data": { + "key": "water_quality", + "label": "آب مصرفی", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "چاه", + "value": "چاه" + }, + { + "index": 1, + "label": "شهری", + "value": "شهری" + }, + { + "index": 2, + "label": "تصفیه‌شده", + "value": "تصفیه‌شده" + }, + { + "index": 3, + "label": "حمل تانکر", + "value": "حمل تانکر" + } + ] + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "water_hardness", + "label": "درصد سختی آب (PPM)", + "keyboard_type": "number", + "decimal": true + } + } + ] + } + }, + { + "type": "card_label_item", + "visible": true, + "visible_condition": "activeStepperIndex == 1", + "data": { + "title": "تلفات", + "padding_horizontal": 12.0, + "padding_vertical": 11.0 + }, + "child": { + "type": "column", + "visible": true, + "visible_condition": null, + "spacing": 10.0, + "children": [ + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "normal_losses", + "label": "تعداد تلفات عادی دوره", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": null, + "data": { + "key": "abnormal_losses", + "label": "تلفات غیرعادی", + "keyboard_type": "number", + "comma_separator": true + } + }, + { + "type": "dropdown", + "visible": true, + "visible_condition": null, + "data": { + "key": "cause_of_unusual_casualties", + "label": "علت تلفات غیرعادی", + "placeholder": "علت تلفات غیرعادی", + "items": [ + "بیماری", + "قطعی برق", + "استرس گرمایی", + "مشکلات دان", + "کیفیت جوجه", + "سایر (شرح…)" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": "cause_of_unusual_casualties == 'سایر (شرح…)'", + "data": { + "key": "other_cause_of_unusual_casualties", + "label": "علت تلفات غیرعادی", + "keyboard_type": "text" + } + }, + { + "type": "dropdown", + "visible": true, + "visible_condition": null, + "data": { + "key": "type_of_disease", + "label": "نوع بیماری تشخیص داده‌شده / مشکوک", + "placeholder": "نوع بیماری تشخیص داده‌شده / مشکوک", + "items": [ + "آنفلوانزا", + "نیوکاسل", + "IB", + "عفونت‌های باکتریایی", + "مشکلات گوارشی", + "سایر (شرح)" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "text_form_field", + "visible": true, + "visible_condition": "type_of_disease == 'سایر (شرح)'", + "data": { + "key": "other_type_of_disease", + "label": "نوع بیماری", + "keyboard_type": "text" + } + }, + { + "type": "dropdown", + "visible": true, + "visible_condition": null, + "data": { + "key": "sampling_done", + "label": "نمونه‌برداری انجام‌شده", + "placeholder": "نمونه‌برداری انجام‌شده", + "items": [ + "انجام شد", + "انجام نشد" + ], + "selectedValue": null, + "enabled": true + } + }, + { + "type": "chip_selection", + "visible": true, + "visible_condition": "sampling_done == 'انجام شد'", + "data": { + "key": "sample_type", + "label": "نوع نمونه", + "selectedIndex": -1, + "options": [ + { + "index": 0, + "label": "لاشه", + "value": "لاشه" + }, + { + "index": 1, + "label": "آب", + "value": "آب" + }, + { + "index": 2, + "label": "دانه", + "value": "دانه" + } + ] + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget.dart b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget.dart index 032cc14..a199af2 100644 --- a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget.dart +++ b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget.dart @@ -95,7 +95,7 @@ sealed class SDUIWidgetModel with _$SDUIWidgetModel { String? visibleCondition, }) = _SizedBox; - // تبدیل مستقیم JSON به مدل بدون هیچ واسطه‌ای + factory SDUIWidgetModel.fromJson(Map json) => _$SDUIWidgetModelFromJson(json); diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart index a807d77..3918ee0 100644 --- a/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/stepper/stepper_sdui.dart @@ -2,40 +2,91 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/stepper/model/stepper_sdui_model.dart'; import 'package:rasadyar_core/core.dart'; -class StepperSDUI extends StatelessWidget { +class StepperSDUI extends StatefulWidget { final StepperSDUIModel model; final RxMap? state; const StepperSDUI({super.key, required this.model, this.state}); + @override + State createState() => _StepperSDUIState(); +} + +class _StepperSDUIState extends State { + late final ScrollController _scrollController; + List? itemKeys; + int? _lastActiveStep; + late int totalSteps; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + totalSteps = widget.model.totalSteps ?? 1; + itemKeys = List.generate(totalSteps, (index) => GlobalKey()); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - final totalSteps = model.totalSteps ?? 5; + + + // Update keys if totalSteps changed + if (itemKeys == null || itemKeys!.length != totalSteps) { + itemKeys = List.generate(totalSteps, (index) => GlobalKey()); + } + + if ((totalSteps * 24.w + ((totalSteps - 1) * (40.w))) < Get.width) { + return Obx(() { + final activeStep = + widget.state?[widget.model.key] as int? ?? + widget.model.activeStep ?? + 0; + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: _buildSteps(totalSteps, activeStep), + ); + }); + } return Obx(() { - final activeStep = state?[model.key] as int? ?? model.activeStep ?? 0; + final activeStep = + widget.state?[widget.model.key] as int? ?? + widget.model.activeStep ?? + 0; - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Directionality( - textDirection: TextDirection.ltr, - child: SizedBox( - height: 30.h, - width: Get.width, - child: Row(children: _buildSteps(totalSteps, activeStep)), - ), + // Scroll to active step if it changed + if (_lastActiveStep != activeStep) { + _lastActiveStep = activeStep; + WidgetsBinding.instance.addPostFrameCallback((_) { + scrollToActive(activeStep); + }); + } + + return SizedBox( + height: 30.h, + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + child: Row(children: _buildScrolledSteps(totalSteps, activeStep)), ), ); }); } - List _buildSteps(int totalSteps, int activeStep) { + List _buildScrolledSteps(int totalSteps, int activeStep) { final List widgets = []; for (int i = 0; i < totalSteps; i++) { // Add step circle widgets.add( Container( + key: itemKeys?[i], alignment: Alignment.center, decoration: BoxDecoration( color: activeStep >= i @@ -73,4 +124,84 @@ class StepperSDUI extends StatelessWidget { return widgets; } + + List _buildSteps(int totalSteps, int activeStep) { + final List widgets = []; + double space = 0; + + if (totalSteps < 2) { + space = 0; + } else if (totalSteps == 2) { + space = Get.width - (2 * (24.w)) - (51.w); + } else { + space = ((Get.width - (51.w)) - (totalSteps * (24.w))) / (totalSteps - 1); + } + + for (int i = 0; i < totalSteps; i++) { + // Add step circle + widgets.add( + Container( + key: itemKeys?[i], + alignment: Alignment.center, + decoration: BoxDecoration( + color: activeStep >= i + ? AppColor.greenNormalHover + : AppColor.whiteNormalActive, + shape: BoxShape.circle, + ), + width: 24.w, + height: 24.h, + child: Text( + '${i + 1}', + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith( + color: activeStep >= i ? Colors.white : AppColor.iconColor, + ), + ), + ), + ); + + // Add divider between steps (except after last step) + if (i < totalSteps - 1) { + widgets.add( + SizedBox( + width: space, + child: Divider( + color: activeStep >= i + 1 + ? AppColor.greenNormalHover + : AppColor.whiteNormalActive, + thickness: 8, + ), + ), + ); + } + } + + return widgets; + } + + void scrollToActive(int index) { + if (itemKeys == null || index < 0 || index >= itemKeys!.length) return; + + final context = itemKeys![index].currentContext; + if (context != null) { + Scrollable.ensureVisible( + context, + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + alignment: 0.5, // دکمه بیاد وسط صفحه + ); + } else if (_scrollController.hasClients) { + // Fallback: calculate position manually + final double itemWidth = 24.w + 40.w; // step width + divider width + final double targetOffset = + (index * itemWidth) - (Get.width / 2) + (24.w / 2); + final double maxScroll = _scrollController.position.maxScrollExtent; + _scrollController.animateTo( + targetOffset.clamp(0.0, maxScroll), + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + ); + } + } }