diff --git a/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/logic.dart b/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/logic.dart index dc85dae..db24e0d 100644 --- a/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/logic.dart +++ b/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/logic.dart @@ -1,13 +1,15 @@ -import 'package:rasadyar_chicken/features/vet_farm/data/repositories/vet_farm_repository.dart'; +import 'package:flutter/material.dart'; import 'package:rasadyar_chicken/features/vet_farm/presentation/pages/root/logic.dart'; -import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/model/text_form_field_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/model/sdui_widget_model.dart'; import 'package:rasadyar_core/core.dart'; class NewPageLogic extends GetxController { VetFarmRootLogic rootLogic = Get.find(); - Rxn textFormFieldSDUIModel = - Rxn(); + Rxn sduiModel = Rxn(); + Map controllers = {}; + RxMap formState = {}.obs; + Map> images = {}; @override void onInit() { @@ -24,23 +26,106 @@ class NewPageLogic extends GetxController { @override void onClose() { + // Dispose all controllers + for (var controller in controllers.values) { + controller.dispose(); + } + controllers.clear(); super.onClose(); - // Cleanup logic here } Future getSDUIForm() async { await safeCall( call: () async => await rootLogic.vetFarmRepository.getSDUIForm(), onSuccess: (result) { - textFormFieldSDUIModel.value = TextFormFieldSDUIModel.fromJson( - result.data ?? {}, - ); + if (result.data != null) { + try { + iLog('SDUI JSON received: ${result.data}'); + + // Extract SDUI data from info structure + // JSON structure: { "info": { "type": "column", "visible": true, "data": {...}, "children": [...] } } + Map? sduiData; + if (result.data!['info'] != null && result.data!['info'] is Map) { + final infoMap = result.data!['info'] as Map; + // Check if info has type field (meaning it's the SDUI structure itself) + if (infoMap.containsKey('type')) { + sduiData = infoMap; + iLog( + 'SDUI data extracted from info (info contains type field)', + ); + } else if (infoMap['data'] != null) { + // Fallback: if info.data exists, use it + sduiData = infoMap['data'] as Map; + iLog('SDUI data extracted from info.data'); + } else { + iLog('info exists but has no type or data field'); + } + } else { + // Fallback: try direct data structure + sduiData = result.data; + iLog('Using direct data structure (no info field)'); + } + + if (sduiData != null) { + iLog('SDUI data to parse: $sduiData'); + sduiModel.value = SDUIWidgetModel.fromJson(sduiData); + iLog( + 'SDUI Model parsed successfully. Type: ${sduiModel.value?.type}, Visible: ${sduiModel.value?.visible}, Children count: ${sduiModel.value?.children?.length ?? 0}', + ); + _initializeControllers(sduiModel.value!); + } else { + iLog('SDUI data is null after extraction'); + } + } catch (e, stackTrace) { + eLog('Error parsing SDUI model: $e'); + eLog('Stack trace: $stackTrace'); + eLog('JSON data: ${result.data}'); + } + } else { + iLog('SDUI result.data is null'); + } }, onError: (error, stackTrace) { - print(error); + eLog('Error fetching SDUI form: $error'); + eLog('Stack trace: $stackTrace'); }, ); } - void onButtonPressed() {} + void _initializeControllers(SDUIWidgetModel model) { + // Extract all text form field keys from the model and create controllers + _extractTextFields(model); + } + + void _extractTextFields(SDUIWidgetModel model) { + if (model.type == 'text_form_field' && model.data != null) { + final key = model.data!['key'] as String?; + final value = model.data!['value'] as String?; + if (key != null && !controllers.containsKey(key)) { + controllers[key] = TextEditingController(text: value ?? ''); + } + } + + if (model.child != null) { + _extractTextFields(SDUIWidgetModel.fromJson(model.child!)); + } + + if (model.children != null) { + for (var child in model.children!) { + _extractTextFields(SDUIWidgetModel.fromJson(child)); + } + } + } + + void onButtonPressed() { + // Example: Get all form values + controllers.forEach((key, controller) { + iLog('Field $key: ${controller.text}'); + }); + + // Example: Get all chip selection values + formState.forEach((key, value) { + iLog('State $key: $value'); + }); + } } diff --git a/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/view.dart b/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/view.dart index 4efea5e..4e2a3ca 100644 --- a/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/view.dart +++ b/packages/chicken/lib/features/vet_farm/presentation/pages/new_page/view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart'; import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart'; -import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/form/sdui_form_widget.dart'; import 'package:rasadyar_core/core.dart'; import 'logic.dart'; @@ -19,17 +19,31 @@ class NewPage extends GetView { } Widget contentWidget() { - return Center( + return SingleChildScrollView( + physics: BouncingScrollPhysics(), + padding: EdgeInsets.all(16.w), child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ + SizedBox(height: 24.h), ObxValue((data) { if (data.value == null) { - return const SizedBox.shrink(); + return Center(child: CircularProgressIndicator()); } - return textFormFiledSDUI(model: data.value!); - }, controller.textFormFieldSDUIModel), - + 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), RElevated( text: 'دکمه نمونه', @@ -37,6 +51,7 @@ class NewPage extends GetView { controller.onButtonPressed(); }, ), + SizedBox(height: 24.h), ], ), ); diff --git a/packages/chicken/lib/presentation/widget/sdui/example_chip_selection.json b/packages/chicken/lib/presentation/widget/sdui/example_chip_selection.json new file mode 100644 index 0000000..e9e2f47 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/example_chip_selection.json @@ -0,0 +1,27 @@ +{ + "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": "ضعیف" + } + ] + } +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/example_dropdown.json b/packages/chicken/lib/presentation/widget/sdui/example_dropdown.json new file mode 100644 index 0000000..9acd4e5 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/example_dropdown.json @@ -0,0 +1,15 @@ +{ + "type": "dropdown", + "visible": true, + "data": { + "key": "training_status", + "label": "آموزش‌دیده در حوزه بهداشت و امنیت زیستی", + "placeholder": "آموزش‌دیده در حوزه بهداشت و امنیت زیستی", + "items": [ + "بله", + "خیر" + ], + "selectedValue": null, + "enabled": true + } +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/example_form.json b/packages/chicken/lib/presentation/widget/sdui/example_form.json new file mode 100644 index 0000000..6ed2027 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/example_form.json @@ -0,0 +1,157 @@ +{ + "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": "نام واحد مرغداری", + "keyboardType": "text", + "enabled": true, + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "breeding_unique_id", + "label": "کد یکتا / شناسه واحد", + "keyboardType": "text", + "enabled": true, + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "health_license", + "label": "پروانه بهداشتی", + "keyboardType": "text", + "enabled": true, + "readonly": false + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "total_capacity", + "label": "ظرفیت اسمی سالن‌ها", + "keyboardType": "number", + "enabled": true, + "readonly": false, + "commaSperator": true + } + } + ] + } + }, + { + "type": "sized_box", + "visible": true, + "data": { + "height": 30.0 + } + }, + { + "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": "hatching_date", + "label": "تاریخ جوجه ریزی", + "keyboardType": "text", + "enabled": true, + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "visit_date", + "label": "تاریخ بازدید", + "keyboardType": "text", + "enabled": true, + "readonly": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_count", + "label": "تعداد جوجه‌ریزی اولیه", + "keyboardType": "number", + "enabled": true, + "readonly": true, + "commaSperator": true + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_breed", + "label": "نوع نژاد", + "keyboardType": "text", + "enabled": true, + "readonly": false + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "hatching_average_weight", + "label": "میانگین وزن جوجه", + "keyboardType": "number", + "enabled": true, + "readonly": false, + "commaSperator": true, + "decimal": true + } + } + ] + } + } + ] +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/example_image_picker.json b/packages/chicken/lib/presentation/widget/sdui/example_image_picker.json new file mode 100644 index 0000000..d59b411 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/example_image_picker.json @@ -0,0 +1,11 @@ +{ + "type": "image_picker", + "visible": true, + "data": { + "key": "hall_images", + "label": "ثبت عکس سالن (حداقل ۳ زاویه)", + "required": true, + "maxImages": null + } +} + 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 e69de29..fa1b6e5 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 @@ -0,0 +1,418 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/model/sdui_widget_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/card_label_item/card_label_item_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/card_label_item/model/card_label_item_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/chip_selection/chip_selection_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/dropdown/dropdown_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/image_picker/image_picker_sdui.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/model/text_form_field_sdui_model.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart'; +import 'package:rasadyar_core/core.dart'; + +class SDUIFormWidget extends StatelessWidget { + final SDUIWidgetModel model; + final Map? controllers; + final RxMap? state; + final Function(String key, dynamic value)? onStateChanged; + final Map>? images; + final Function(String key, RxList images)? onImagesChanged; + + const SDUIFormWidget({ + super.key, + required this.model, + this.controllers, + this.state, + this.onStateChanged, + this.images, + this.onImagesChanged, + }); + + @override + Widget build(BuildContext context) { + if (model.visible == false) { + return const SizedBox.shrink(); + } + + try { + return _buildWidget(model); + } catch (e, stackTrace) { + iLog('Error in SDUIFormWidget.build: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.red.withOpacity(0.1), + child: Text('Error: $e'), + ); + } + } + + Widget _buildWidget(SDUIWidgetModel widgetModel) { + final type = widgetModel.type; + + if (type == null || type.isEmpty) { + iLog('SDUIWidgetModel type is null or empty'); + return const SizedBox.shrink(); + } + + switch (type) { + case 'text_form_field': + return _buildTextFormField(widgetModel); + case 'card_label_item': + return _buildCardLabelItem(widgetModel); + case 'chip_selection': + return _buildChipSelection(widgetModel); + case 'dropdown': + return _buildDropdown(widgetModel); + case 'image_picker': + return _buildImagePicker(widgetModel); + case 'column': + return _buildColumn(widgetModel); + case 'row': + return _buildRow(widgetModel); + case 'sized_box': + return _buildSizedBox(widgetModel); + default: + iLog('Unknown SDUI widget type: $type'); + return const SizedBox.shrink(); + } + } + + Widget _buildTextFormField(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final textFieldModel = TextFormFieldSDUIModel.fromJson(widgetModel.data!); + + // Use provided controller or create a new one + final key = textFieldModel.key ?? ''; + TextEditingController controller; + + if (controllers != null && + key.isNotEmpty && + controllers!.containsKey(key)) { + controller = controllers![key]!; + } else { + controller = TextEditingController(text: textFieldModel.value ?? ''); + // Store the controller in the map if controllers map is provided + if (controllers != null && key.isNotEmpty) { + controllers![key] = controller; + } + } + + return textFormFiledSDUI(model: textFieldModel, controller: controller); + } catch (e) { + iLog('Error building text_form_field: $e'); + return const SizedBox.shrink(); + } + } + + Widget _buildCardLabelItem(SDUIWidgetModel widgetModel) { + try { + final cardModel = CardLabelItemSDUI.fromJson({ + 'type': widgetModel.type, + 'visible': widgetModel.visible, + 'data': widgetModel.data, + 'child': widgetModel.child, + }); + + // If there's a child, build it recursively + Widget? childWidget; + if (widgetModel.child != null) { + try { + childWidget = SDUIFormWidget( + model: SDUIWidgetModel.fromJson(widgetModel.child!), + controllers: controllers, + state: state, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + ); + } catch (e) { + iLog('Error building card_label_item child: $e'); + iLog('Child data: ${widgetModel.child}'); + } + } else if (widgetModel.children != null && + widgetModel.children!.isNotEmpty) { + // If there are children, build them as a column + try { + childWidget = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: widgetModel.children! + .map( + (child) => SDUIFormWidget( + model: SDUIWidgetModel.fromJson(child), + controllers: controllers, + state: state, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + ), + ) + .toList(), + ); + } catch (e) { + iLog('Error building card_label_item children: $e'); + } + } + + // Create a modified card model with the child widget + return cardLabelItemSDUI(model: cardModel, child: childWidget); + } catch (e, stackTrace) { + iLog('Error building card_label_item: $e'); + iLog('Stack trace: $stackTrace'); + iLog('WidgetModel data: ${widgetModel.data}'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.orange.withOpacity(0.1), + child: Text('Card Error: $e'), + ); + } + } + + Widget _buildColumn(SDUIWidgetModel widgetModel) { + if (widgetModel.children == null || widgetModel.children!.isEmpty) { + iLog('Column has no children'); + return const SizedBox.shrink(); + } + + try { + final spacing = widgetModel.data?['spacing']; + final spacingValue = spacing is int + ? spacing.toDouble() + : (spacing as double?) ?? 0.0; + + final mainAxisSize = widgetModel.data?['mainAxisSize'] == 'min' + ? MainAxisSize.min + : MainAxisSize.max; + final crossAxisAlignment = _parseCrossAxisAlignment( + widgetModel.data?['crossAxisAlignment'], + ); + + final children = widgetModel.children!.map((child) { + try { + return SDUIFormWidget( + model: SDUIWidgetModel.fromJson(child), + controllers: controllers, + state: state, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + ); + } catch (e) { + iLog('Error building column child: $e'); + iLog('Child data: $child'); + return Container( + padding: EdgeInsets.all(8), + color: Colors.yellow.withOpacity(0.1), + child: Text('Child Error'), + ); + } + }).toList(); + + // Add spacing between children + if (spacingValue > 0 && children.length > 1) { + final spacedChildren = []; + for (int i = 0; i < children.length; i++) { + spacedChildren.add(children[i]); + if (i < children.length - 1) { + spacedChildren.add(SizedBox(height: spacingValue)); + } + } + return Column( + mainAxisSize: mainAxisSize, + crossAxisAlignment: crossAxisAlignment, + children: spacedChildren, + ); + } + + return Column( + mainAxisSize: mainAxisSize, + crossAxisAlignment: crossAxisAlignment, + children: children, + ); + } catch (e, stackTrace) { + iLog('Error building column: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.blue.withOpacity(0.1), + child: Text('Column Error: $e'), + ); + } + } + + Widget _buildRow(SDUIWidgetModel widgetModel) { + if (widgetModel.children == null || widgetModel.children!.isEmpty) { + return const SizedBox.shrink(); + } + + final spacing = widgetModel.data?['spacing'] as double? ?? 0.0; + final mainAxisAlignment = _parseMainAxisAlignment( + widgetModel.data?['mainAxisAlignment'], + ); + + final children = widgetModel.children! + .map( + (child) => SDUIFormWidget( + model: SDUIWidgetModel.fromJson(child), + controllers: controllers, + state: state, + onStateChanged: onStateChanged, + images: images, + onImagesChanged: onImagesChanged, + ), + ) + .toList(); + + // Add spacing between children + if (spacing > 0 && children.length > 1) { + final spacedChildren = []; + for (int i = 0; i < children.length; i++) { + spacedChildren.add(children[i]); + if (i < children.length - 1) { + spacedChildren.add(SizedBox(width: spacing)); + } + } + return Row( + mainAxisAlignment: mainAxisAlignment, + children: spacedChildren, + ); + } + + return Row(mainAxisAlignment: mainAxisAlignment, children: children); + } + + Widget _buildSizedBox(SDUIWidgetModel widgetModel) { + final data = widgetModel.data ?? {}; + final width = data['width'] as double?; + final height = data['height'] as double?; + + return SizedBox(width: width, height: height); + } + + Widget _buildChipSelection(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final chipModel = ChipSelectionSDUIModel.fromJson(widgetModel.data!); + + return ChipSelectionSDUI( + model: chipModel, + state: state, + onChanged: (key, index, value) { + if (onStateChanged != null) { + onStateChanged!(key, index); + } + }, + ); + } catch (e, stackTrace) { + iLog('Error building chip_selection: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.purple.withOpacity(0.1), + child: Text('Chip Selection Error: $e'), + ); + } + } + + Widget _buildDropdown(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final dropdownModel = DropdownSDUIModel.fromJson(widgetModel.data!); + + return DropdownSDUI( + model: dropdownModel, + state: state, + onChanged: (key, value) { + if (onStateChanged != null) { + onStateChanged!(key, value); + } + }, + ); + } catch (e, stackTrace) { + iLog('Error building dropdown: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.blue.withOpacity(0.1), + child: Text('Dropdown Error: $e'), + ); + } + } + + Widget _buildImagePicker(SDUIWidgetModel widgetModel) { + if (widgetModel.data == null) { + return const SizedBox.shrink(); + } + + try { + final imagePickerModel = ImagePickerSDUIModel.fromJson(widgetModel.data!); + + return ImagePickerSDUI( + model: imagePickerModel, + images: images, + onImagesChanged: (key, imageList) { + if (onImagesChanged != null) { + onImagesChanged!(key, imageList); + } + }, + ); + } catch (e, stackTrace) { + iLog('Error building image_picker: $e'); + iLog('Stack trace: $stackTrace'); + return Container( + padding: EdgeInsets.all(16), + color: Colors.green.withOpacity(0.1), + child: Text('Image Picker Error: $e'), + ); + } + } + + CrossAxisAlignment _parseCrossAxisAlignment(dynamic value) { + if (value == null) return CrossAxisAlignment.center; + switch (value.toString()) { + case 'start': + return CrossAxisAlignment.start; + case 'end': + return CrossAxisAlignment.end; + case 'center': + return CrossAxisAlignment.center; + case 'stretch': + return CrossAxisAlignment.stretch; + default: + return CrossAxisAlignment.center; + } + } + + MainAxisAlignment _parseMainAxisAlignment(dynamic value) { + if (value == null) return MainAxisAlignment.start; + switch (value.toString()) { + case 'start': + return MainAxisAlignment.start; + case 'end': + return MainAxisAlignment.end; + case 'center': + return MainAxisAlignment.center; + case 'spaceBetween': + return MainAxisAlignment.spaceBetween; + case 'spaceAround': + return MainAxisAlignment.spaceAround; + case 'spaceEvenly': + return MainAxisAlignment.spaceEvenly; + default: + return MainAxisAlignment.start; + } + } +} diff --git a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart new file mode 100644 index 0000000..0ff4408 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'sdui_widget_model.freezed.dart'; +part 'sdui_widget_model.g.dart'; + +@freezed +abstract class SDUIWidgetModel with _$SDUIWidgetModel { + const factory SDUIWidgetModel({ + String? type, + bool? visible, + Map? data, + Map? child, + List>? children, + }) = _SDUIWidgetModel; + + factory SDUIWidgetModel.fromJson(Map json) => + _$SDUIWidgetModelFromJson(json); +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.freezed.dart new file mode 100644 index 0000000..e4a3905 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.freezed.dart @@ -0,0 +1,313 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'sdui_widget_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$SDUIWidgetModel { + + String? get type; bool? get visible; Map? get data; Map? get child; List>? get children; +/// Create a copy of SDUIWidgetModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SDUIWidgetModelCopyWith get copyWith => _$SDUIWidgetModelCopyWithImpl(this as SDUIWidgetModel, _$identity); + + /// Serializes this SDUIWidgetModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SDUIWidgetModel&&(identical(other.type, type) || other.type == type)&&(identical(other.visible, visible) || other.visible == visible)&&const DeepCollectionEquality().equals(other.data, data)&&const DeepCollectionEquality().equals(other.child, child)&&const DeepCollectionEquality().equals(other.children, children)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,type,visible,const DeepCollectionEquality().hash(data),const DeepCollectionEquality().hash(child),const DeepCollectionEquality().hash(children)); + +@override +String toString() { + return 'SDUIWidgetModel(type: $type, visible: $visible, data: $data, child: $child, children: $children)'; +} + + +} + +/// @nodoc +abstract mixin class $SDUIWidgetModelCopyWith<$Res> { + factory $SDUIWidgetModelCopyWith(SDUIWidgetModel value, $Res Function(SDUIWidgetModel) _then) = _$SDUIWidgetModelCopyWithImpl; +@useResult +$Res call({ + String? type, bool? visible, Map? data, Map? child, List>? children +}); + + + + +} +/// @nodoc +class _$SDUIWidgetModelCopyWithImpl<$Res> + implements $SDUIWidgetModelCopyWith<$Res> { + _$SDUIWidgetModelCopyWithImpl(this._self, this._then); + + final SDUIWidgetModel _self; + final $Res Function(SDUIWidgetModel) _then; + +/// Create a copy of SDUIWidgetModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? type = freezed,Object? visible = freezed,Object? data = freezed,Object? child = freezed,Object? children = freezed,}) { + return _then(_self.copyWith( +type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as String?,visible: freezed == visible ? _self.visible : visible // ignore: cast_nullable_to_non_nullable +as bool?,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable +as Map?,child: freezed == child ? _self.child : child // ignore: cast_nullable_to_non_nullable +as Map?,children: freezed == children ? _self.children : children // ignore: cast_nullable_to_non_nullable +as List>?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SDUIWidgetModel]. +extension SDUIWidgetModelPatterns on SDUIWidgetModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SDUIWidgetModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SDUIWidgetModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SDUIWidgetModel value) $default,){ +final _that = this; +switch (_that) { +case _SDUIWidgetModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SDUIWidgetModel value)? $default,){ +final _that = this; +switch (_that) { +case _SDUIWidgetModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? type, bool? visible, Map? data, Map? child, List>? children)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SDUIWidgetModel() when $default != null: +return $default(_that.type,_that.visible,_that.data,_that.child,_that.children);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? type, bool? visible, Map? data, Map? child, List>? children) $default,) {final _that = this; +switch (_that) { +case _SDUIWidgetModel(): +return $default(_that.type,_that.visible,_that.data,_that.child,_that.children);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? type, bool? visible, Map? data, Map? child, List>? children)? $default,) {final _that = this; +switch (_that) { +case _SDUIWidgetModel() when $default != null: +return $default(_that.type,_that.visible,_that.data,_that.child,_that.children);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SDUIWidgetModel implements SDUIWidgetModel { + const _SDUIWidgetModel({this.type, this.visible, final Map? data, final Map? child, final List>? children}): _data = data,_child = child,_children = children; + factory _SDUIWidgetModel.fromJson(Map json) => _$SDUIWidgetModelFromJson(json); + +@override final String? type; +@override final bool? visible; + final Map? _data; +@override Map? get data { + final value = _data; + if (value == null) return null; + if (_data is EqualUnmodifiableMapView) return _data; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); +} + + final Map? _child; +@override Map? get child { + final value = _child; + if (value == null) return null; + if (_child is EqualUnmodifiableMapView) return _child; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); +} + + final List>? _children; +@override List>? get children { + final value = _children; + if (value == null) return null; + if (_children is EqualUnmodifiableListView) return _children; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); +} + + +/// Create a copy of SDUIWidgetModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SDUIWidgetModelCopyWith<_SDUIWidgetModel> get copyWith => __$SDUIWidgetModelCopyWithImpl<_SDUIWidgetModel>(this, _$identity); + +@override +Map toJson() { + return _$SDUIWidgetModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SDUIWidgetModel&&(identical(other.type, type) || other.type == type)&&(identical(other.visible, visible) || other.visible == visible)&&const DeepCollectionEquality().equals(other._data, _data)&&const DeepCollectionEquality().equals(other._child, _child)&&const DeepCollectionEquality().equals(other._children, _children)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,type,visible,const DeepCollectionEquality().hash(_data),const DeepCollectionEquality().hash(_child),const DeepCollectionEquality().hash(_children)); + +@override +String toString() { + return 'SDUIWidgetModel(type: $type, visible: $visible, data: $data, child: $child, children: $children)'; +} + + +} + +/// @nodoc +abstract mixin class _$SDUIWidgetModelCopyWith<$Res> implements $SDUIWidgetModelCopyWith<$Res> { + factory _$SDUIWidgetModelCopyWith(_SDUIWidgetModel value, $Res Function(_SDUIWidgetModel) _then) = __$SDUIWidgetModelCopyWithImpl; +@override @useResult +$Res call({ + String? type, bool? visible, Map? data, Map? child, List>? children +}); + + + + +} +/// @nodoc +class __$SDUIWidgetModelCopyWithImpl<$Res> + implements _$SDUIWidgetModelCopyWith<$Res> { + __$SDUIWidgetModelCopyWithImpl(this._self, this._then); + + final _SDUIWidgetModel _self; + final $Res Function(_SDUIWidgetModel) _then; + +/// Create a copy of SDUIWidgetModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? type = freezed,Object? visible = freezed,Object? data = freezed,Object? child = freezed,Object? children = freezed,}) { + return _then(_SDUIWidgetModel( +type: freezed == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as String?,visible: freezed == visible ? _self.visible : visible // ignore: cast_nullable_to_non_nullable +as bool?,data: freezed == data ? _self._data : data // ignore: cast_nullable_to_non_nullable +as Map?,child: freezed == child ? _self._child : child // ignore: cast_nullable_to_non_nullable +as Map?,children: freezed == children ? _self._children : children // ignore: cast_nullable_to_non_nullable +as List>?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.g.dart new file mode 100644 index 0000000..d565ab4 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/model/sdui_widget_model.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sdui_widget_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SDUIWidgetModel _$SDUIWidgetModelFromJson(Map json) => + _SDUIWidgetModel( + type: json['type'] as String?, + visible: json['visible'] as bool?, + data: json['data'] as Map?, + child: json['child'] as Map?, + children: (json['children'] as List?) + ?.map((e) => e as Map) + .toList(), + ); + +Map _$SDUIWidgetModelToJson(_SDUIWidgetModel instance) => + { + 'type': instance.type, + 'visible': instance.visible, + 'data': instance.data, + 'child': instance.child, + 'children': instance.children, + }; diff --git a/packages/chicken/lib/presentation/widget/sdui/sdui.dart b/packages/chicken/lib/presentation/widget/sdui/sdui.dart new file mode 100644 index 0000000..fafa24c --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/sdui.dart @@ -0,0 +1,8 @@ +// SDUI Export file +export 'form/sdui_form_widget.dart'; +export 'model/sdui_widget_model.dart'; +export 'widgets/card_label_item/card_label_item_sdui.dart'; +export 'widgets/card_label_item/model/card_label_item_sdui_model.dart'; +export 'widgets/text_form_filed/text_form_filed_sdui.dart'; +export 'widgets/text_form_filed/model/text_form_field_sdui_model.dart'; + diff --git a/packages/chicken/lib/presentation/widget/sdui/test_sdui_form.json b/packages/chicken/lib/presentation/widget/sdui/test_sdui_form.json new file mode 100644 index 0000000..f96e116 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/test_sdui_form.json @@ -0,0 +1,79 @@ +{ + "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_a", + "label": "(سه تایی جداشه)پروانه بهداشتی", + "keyboard_type": "number", + "comma_sperator": true + } + }, + { + "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 + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/sdui/test_simple_sdui_form.json b/packages/chicken/lib/presentation/widget/sdui/test_simple_sdui_form.json new file mode 100644 index 0000000..b75974a --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/test_simple_sdui_form.json @@ -0,0 +1,77 @@ +{ + "id": 1, + "key": "simple-test-form", + "create_date": "2025-01-15T10:00:00.000000", + "modify_date": "2025-01-15T10:00:00.000000", + "trash": false, + "info": { + "data": { + "type": "column", + "visible": true, + "data": { + "spacing": 20.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": "name", + "label": "نام", + "keyboardType": "text", + "enabled": true, + "readonly": false, + "value": "" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "phone", + "label": "شماره تماس", + "keyboardType": "text", + "enabled": true, + "readonly": false, + "value": "" + } + }, + { + "type": "text_form_field", + "visible": true, + "data": { + "key": "age", + "label": "سن", + "keyboardType": "number", + "enabled": true, + "readonly": false, + "commaSperator": true, + "value": "" + } + } + ] + } + } + ] + } + }, + "created_by": null, + "modified_by": null +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/card_label_item/card_label_item_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/card_label_item/card_label_item_sdui.dart index 7c13098..42f781d 100644 --- a/packages/chicken/lib/presentation/widget/sdui/widgets/card_label_item/card_label_item_sdui.dart +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/card_label_item/card_label_item_sdui.dart @@ -3,18 +3,10 @@ import 'package:rasadyar_core/core.dart'; import 'model/card_label_item_sdui_model.dart'; import 'package:flutter/material.dart'; -Widget cardLabelItemSDUI({required CardLabelItemSDUI model}) { +Widget cardLabelItemSDUI({required CardLabelItemSDUI model, Widget? child}) { return farmInfoWidget( title: model.data?.title ?? '', - child: Container(), - padding: - model.data?.paddingHorizontal != null && - model.data?.paddingVertical != null - ? EdgeInsets.symmetric( - horizontal: model.data?.paddingHorizontal ?? 0, - vertical: model.data?.paddingVertical ?? 0, - ) - : null, + child: child ?? Container(), ); } @@ -26,17 +18,19 @@ Widget farmInfoWidget({ return Stack( clipBehavior: Clip.none, children: [ - Positioned.fill( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - border: Border.all(width: 0.50, color: AppColor.mediumGrey), - ), - - padding: - padding ?? EdgeInsets.symmetric(horizontal: 12.w, vertical: 11.h), - child: child, + Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + border: Border.all(width: 0.50, color: AppColor.mediumGrey), + ), + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 11.h), + child: Column( + children: [ + SizedBox(height: 8.h), + child, + ], ), ), Positioned( diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/chip_selection_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/chip_selection_sdui.dart new file mode 100644 index 0000000..373fb44 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/chip_selection_sdui.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.dart'; +import 'package:rasadyar_core/core.dart'; + +class ChipSelectionSDUI extends StatelessWidget { + final ChipSelectionSDUIModel model; + final RxMap? state; + final Function(String key, int index, String? value)? onChanged; + + const ChipSelectionSDUI({ + super.key, + required this.model, + this.state, + this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final options = model.options ?? []; + final spacing = 10.0; + + if (options.isEmpty) { + return const SizedBox.shrink(); + } + + return Obx(() { + final selectedIndex = + state?[model.key] as int? ?? model.selectedIndex ?? -1; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (model.label != null && model.label!.isNotEmpty) ...[ + Text( + model.label!, + style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2), + ), + SizedBox(height: 9.h), + ], + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: spacing, + children: options.asMap().entries.map((entry) { + final option = entry.value; + final optionIndex = option.index ?? entry.key; + return Expanded( + child: containerChips( + selectedIndex: selectedIndex, + index: optionIndex, + label: option.label ?? '', + onTap: (index) { + if (onChanged != null && model.key != null) { + onChanged!(model.key!, index, option.value); + } + }, + ), + ); + }).toList(), + ), + ], + ); + }); + } +} diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.dart new file mode 100644 index 0000000..c2a2f83 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.dart @@ -0,0 +1,30 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chip_selection_sdui_model.freezed.dart'; +part 'chip_selection_sdui_model.g.dart'; + +@freezed +abstract class ChipSelectionSDUIModel with _$ChipSelectionSDUIModel { + const factory ChipSelectionSDUIModel({ + String? key, + String? label, + List? options, + int? selectedIndex, + }) = _ChipSelectionSDUIModel; + + factory ChipSelectionSDUIModel.fromJson(Map json) => + _$ChipSelectionSDUIModelFromJson(json); +} + +@freezed +abstract class ChipOption with _$ChipOption { + const factory ChipOption({ + int? index, + String? label, + String? value, + }) = _ChipOption; + + factory ChipOption.fromJson(Map json) => + _$ChipOptionFromJson(json); +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.freezed.dart new file mode 100644 index 0000000..ada7b19 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.freezed.dart @@ -0,0 +1,563 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'chip_selection_sdui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$ChipSelectionSDUIModel { + + String? get key; String? get label; List? get options; int? get selectedIndex; +/// Create a copy of ChipSelectionSDUIModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ChipSelectionSDUIModelCopyWith get copyWith => _$ChipSelectionSDUIModelCopyWithImpl(this as ChipSelectionSDUIModel, _$identity); + + /// Serializes this ChipSelectionSDUIModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ChipSelectionSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&const DeepCollectionEquality().equals(other.options, options)&&(identical(other.selectedIndex, selectedIndex) || other.selectedIndex == selectedIndex)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,const DeepCollectionEquality().hash(options),selectedIndex); + +@override +String toString() { + return 'ChipSelectionSDUIModel(key: $key, label: $label, options: $options, selectedIndex: $selectedIndex)'; +} + + +} + +/// @nodoc +abstract mixin class $ChipSelectionSDUIModelCopyWith<$Res> { + factory $ChipSelectionSDUIModelCopyWith(ChipSelectionSDUIModel value, $Res Function(ChipSelectionSDUIModel) _then) = _$ChipSelectionSDUIModelCopyWithImpl; +@useResult +$Res call({ + String? key, String? label, List? options, int? selectedIndex +}); + + + + +} +/// @nodoc +class _$ChipSelectionSDUIModelCopyWithImpl<$Res> + implements $ChipSelectionSDUIModelCopyWith<$Res> { + _$ChipSelectionSDUIModelCopyWithImpl(this._self, this._then); + + final ChipSelectionSDUIModel _self; + final $Res Function(ChipSelectionSDUIModel) _then; + +/// Create a copy of ChipSelectionSDUIModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? label = freezed,Object? options = freezed,Object? selectedIndex = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,options: freezed == options ? _self.options : options // ignore: cast_nullable_to_non_nullable +as List?,selectedIndex: freezed == selectedIndex ? _self.selectedIndex : selectedIndex // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ChipSelectionSDUIModel]. +extension ChipSelectionSDUIModelPatterns on ChipSelectionSDUIModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ChipSelectionSDUIModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ChipSelectionSDUIModel value) $default,){ +final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ChipSelectionSDUIModel value)? $default,){ +final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? key, String? label, List? options, int? selectedIndex)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.options,_that.selectedIndex);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? key, String? label, List? options, int? selectedIndex) $default,) {final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel(): +return $default(_that.key,_that.label,_that.options,_that.selectedIndex);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? key, String? label, List? options, int? selectedIndex)? $default,) {final _that = this; +switch (_that) { +case _ChipSelectionSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.options,_that.selectedIndex);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _ChipSelectionSDUIModel implements ChipSelectionSDUIModel { + const _ChipSelectionSDUIModel({this.key, this.label, final List? options, this.selectedIndex}): _options = options; + factory _ChipSelectionSDUIModel.fromJson(Map json) => _$ChipSelectionSDUIModelFromJson(json); + +@override final String? key; +@override final String? label; + final List? _options; +@override List? get options { + final value = _options; + if (value == null) return null; + if (_options is EqualUnmodifiableListView) return _options; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); +} + +@override final int? selectedIndex; + +/// Create a copy of ChipSelectionSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ChipSelectionSDUIModelCopyWith<_ChipSelectionSDUIModel> get copyWith => __$ChipSelectionSDUIModelCopyWithImpl<_ChipSelectionSDUIModel>(this, _$identity); + +@override +Map toJson() { + return _$ChipSelectionSDUIModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChipSelectionSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&const DeepCollectionEquality().equals(other._options, _options)&&(identical(other.selectedIndex, selectedIndex) || other.selectedIndex == selectedIndex)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,const DeepCollectionEquality().hash(_options),selectedIndex); + +@override +String toString() { + return 'ChipSelectionSDUIModel(key: $key, label: $label, options: $options, selectedIndex: $selectedIndex)'; +} + + +} + +/// @nodoc +abstract mixin class _$ChipSelectionSDUIModelCopyWith<$Res> implements $ChipSelectionSDUIModelCopyWith<$Res> { + factory _$ChipSelectionSDUIModelCopyWith(_ChipSelectionSDUIModel value, $Res Function(_ChipSelectionSDUIModel) _then) = __$ChipSelectionSDUIModelCopyWithImpl; +@override @useResult +$Res call({ + String? key, String? label, List? options, int? selectedIndex +}); + + + + +} +/// @nodoc +class __$ChipSelectionSDUIModelCopyWithImpl<$Res> + implements _$ChipSelectionSDUIModelCopyWith<$Res> { + __$ChipSelectionSDUIModelCopyWithImpl(this._self, this._then); + + final _ChipSelectionSDUIModel _self; + final $Res Function(_ChipSelectionSDUIModel) _then; + +/// Create a copy of ChipSelectionSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? label = freezed,Object? options = freezed,Object? selectedIndex = freezed,}) { + return _then(_ChipSelectionSDUIModel( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,options: freezed == options ? _self._options : options // ignore: cast_nullable_to_non_nullable +as List?,selectedIndex: freezed == selectedIndex ? _self.selectedIndex : selectedIndex // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + + +/// @nodoc +mixin _$ChipOption { + + int? get index; String? get label; String? get value; +/// Create a copy of ChipOption +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ChipOptionCopyWith get copyWith => _$ChipOptionCopyWithImpl(this as ChipOption, _$identity); + + /// Serializes this ChipOption to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ChipOption&&(identical(other.index, index) || other.index == index)&&(identical(other.label, label) || other.label == label)&&(identical(other.value, value) || other.value == value)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,index,label,value); + +@override +String toString() { + return 'ChipOption(index: $index, label: $label, value: $value)'; +} + + +} + +/// @nodoc +abstract mixin class $ChipOptionCopyWith<$Res> { + factory $ChipOptionCopyWith(ChipOption value, $Res Function(ChipOption) _then) = _$ChipOptionCopyWithImpl; +@useResult +$Res call({ + int? index, String? label, String? value +}); + + + + +} +/// @nodoc +class _$ChipOptionCopyWithImpl<$Res> + implements $ChipOptionCopyWith<$Res> { + _$ChipOptionCopyWithImpl(this._self, this._then); + + final ChipOption _self; + final $Res Function(ChipOption) _then; + +/// Create a copy of ChipOption +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? index = freezed,Object? label = freezed,Object? value = freezed,}) { + return _then(_self.copyWith( +index: freezed == index ? _self.index : index // ignore: cast_nullable_to_non_nullable +as int?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ChipOption]. +extension ChipOptionPatterns on ChipOption { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ChipOption value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ChipOption() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ChipOption value) $default,){ +final _that = this; +switch (_that) { +case _ChipOption(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ChipOption value)? $default,){ +final _that = this; +switch (_that) { +case _ChipOption() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( int? index, String? label, String? value)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ChipOption() when $default != null: +return $default(_that.index,_that.label,_that.value);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( int? index, String? label, String? value) $default,) {final _that = this; +switch (_that) { +case _ChipOption(): +return $default(_that.index,_that.label,_that.value);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int? index, String? label, String? value)? $default,) {final _that = this; +switch (_that) { +case _ChipOption() when $default != null: +return $default(_that.index,_that.label,_that.value);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _ChipOption implements ChipOption { + const _ChipOption({this.index, this.label, this.value}); + factory _ChipOption.fromJson(Map json) => _$ChipOptionFromJson(json); + +@override final int? index; +@override final String? label; +@override final String? value; + +/// Create a copy of ChipOption +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ChipOptionCopyWith<_ChipOption> get copyWith => __$ChipOptionCopyWithImpl<_ChipOption>(this, _$identity); + +@override +Map toJson() { + return _$ChipOptionToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChipOption&&(identical(other.index, index) || other.index == index)&&(identical(other.label, label) || other.label == label)&&(identical(other.value, value) || other.value == value)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,index,label,value); + +@override +String toString() { + return 'ChipOption(index: $index, label: $label, value: $value)'; +} + + +} + +/// @nodoc +abstract mixin class _$ChipOptionCopyWith<$Res> implements $ChipOptionCopyWith<$Res> { + factory _$ChipOptionCopyWith(_ChipOption value, $Res Function(_ChipOption) _then) = __$ChipOptionCopyWithImpl; +@override @useResult +$Res call({ + int? index, String? label, String? value +}); + + + + +} +/// @nodoc +class __$ChipOptionCopyWithImpl<$Res> + implements _$ChipOptionCopyWith<$Res> { + __$ChipOptionCopyWithImpl(this._self, this._then); + + final _ChipOption _self; + final $Res Function(_ChipOption) _then; + +/// Create a copy of ChipOption +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? index = freezed,Object? label = freezed,Object? value = freezed,}) { + return _then(_ChipOption( +index: freezed == index ? _self.index : index // ignore: cast_nullable_to_non_nullable +as int?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.g.dart new file mode 100644 index 0000000..b012429 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/chip_selection/model/chip_selection_sdui_model.g.dart @@ -0,0 +1,40 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'chip_selection_sdui_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_ChipSelectionSDUIModel _$ChipSelectionSDUIModelFromJson( + Map json, +) => _ChipSelectionSDUIModel( + key: json['key'] as String?, + label: json['label'] as String?, + options: (json['options'] as List?) + ?.map((e) => ChipOption.fromJson(e as Map)) + .toList(), + selectedIndex: (json['selected_index'] as num?)?.toInt(), +); + +Map _$ChipSelectionSDUIModelToJson( + _ChipSelectionSDUIModel instance, +) => { + 'key': instance.key, + 'label': instance.label, + 'options': instance.options, + 'selected_index': instance.selectedIndex, +}; + +_ChipOption _$ChipOptionFromJson(Map json) => _ChipOption( + index: (json['index'] as num?)?.toInt(), + label: json['label'] as String?, + value: json['value'] as String?, +); + +Map _$ChipOptionToJson(_ChipOption instance) => + { + 'index': instance.index, + 'label': instance.label, + 'value': instance.value, + }; diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/dropdown_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/dropdown_sdui.dart new file mode 100644 index 0000000..7d0fbe6 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/dropdown_sdui.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.dart'; +import 'package:rasadyar_core/core.dart'; + +class DropdownSDUI extends StatelessWidget { + final DropdownSDUIModel model; + final RxMap? state; + final Function(String key, String value)? onChanged; + + const DropdownSDUI({ + super.key, + required this.model, + this.state, + this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final items = model.items ?? []; + + if (items.isEmpty) { + return const SizedBox.shrink(); + } + + return Obx(() { + final selectedValue = state?[model.key] as String? ?? model.selectedValue; + + return ResourceOverlayDropdown( + items: Resource.success(items), + selectedItem: selectedValue, + onChanged: (item) { + if (onChanged != null && model.key != null) { + onChanged!(model.key!, item); + } + }, + itemBuilder: (item) => Text(item), + labelBuilder: (selected) => + Text(selected ?? (model.placeholder ?? model.label ?? '')), + isDisabled: model.enabled == false, + ); + }); + } +} diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.dart new file mode 100644 index 0000000..f67ef13 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.dart @@ -0,0 +1,20 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'dropdown_sdui_model.freezed.dart'; +part 'dropdown_sdui_model.g.dart'; + +@freezed +abstract class DropdownSDUIModel with _$DropdownSDUIModel { + const factory DropdownSDUIModel({ + String? key, + String? label, + String? placeholder, + List? items, + String? selectedValue, + bool? enabled, + }) = _DropdownSDUIModel; + + factory DropdownSDUIModel.fromJson(Map json) => + _$DropdownSDUIModelFromJson(json); +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.freezed.dart new file mode 100644 index 0000000..c2252dc --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.freezed.dart @@ -0,0 +1,300 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'dropdown_sdui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$DropdownSDUIModel { + + String? get key; String? get label; String? get placeholder; List? get items; String? get selectedValue; bool? get enabled; +/// Create a copy of DropdownSDUIModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$DropdownSDUIModelCopyWith get copyWith => _$DropdownSDUIModelCopyWithImpl(this as DropdownSDUIModel, _$identity); + + /// Serializes this DropdownSDUIModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is DropdownSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&(identical(other.placeholder, placeholder) || other.placeholder == placeholder)&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.selectedValue, selectedValue) || other.selectedValue == selectedValue)&&(identical(other.enabled, enabled) || other.enabled == enabled)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,placeholder,const DeepCollectionEquality().hash(items),selectedValue,enabled); + +@override +String toString() { + return 'DropdownSDUIModel(key: $key, label: $label, placeholder: $placeholder, items: $items, selectedValue: $selectedValue, enabled: $enabled)'; +} + + +} + +/// @nodoc +abstract mixin class $DropdownSDUIModelCopyWith<$Res> { + factory $DropdownSDUIModelCopyWith(DropdownSDUIModel value, $Res Function(DropdownSDUIModel) _then) = _$DropdownSDUIModelCopyWithImpl; +@useResult +$Res call({ + String? key, String? label, String? placeholder, List? items, String? selectedValue, bool? enabled +}); + + + + +} +/// @nodoc +class _$DropdownSDUIModelCopyWithImpl<$Res> + implements $DropdownSDUIModelCopyWith<$Res> { + _$DropdownSDUIModelCopyWithImpl(this._self, this._then); + + final DropdownSDUIModel _self; + final $Res Function(DropdownSDUIModel) _then; + +/// Create a copy of DropdownSDUIModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? label = freezed,Object? placeholder = freezed,Object? items = freezed,Object? selectedValue = freezed,Object? enabled = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,placeholder: freezed == placeholder ? _self.placeholder : placeholder // ignore: cast_nullable_to_non_nullable +as String?,items: freezed == items ? _self.items : items // ignore: cast_nullable_to_non_nullable +as List?,selectedValue: freezed == selectedValue ? _self.selectedValue : selectedValue // ignore: cast_nullable_to_non_nullable +as String?,enabled: freezed == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable +as bool?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [DropdownSDUIModel]. +extension DropdownSDUIModelPatterns on DropdownSDUIModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _DropdownSDUIModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _DropdownSDUIModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _DropdownSDUIModel value) $default,){ +final _that = this; +switch (_that) { +case _DropdownSDUIModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _DropdownSDUIModel value)? $default,){ +final _that = this; +switch (_that) { +case _DropdownSDUIModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? key, String? label, String? placeholder, List? items, String? selectedValue, bool? enabled)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _DropdownSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.placeholder,_that.items,_that.selectedValue,_that.enabled);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? key, String? label, String? placeholder, List? items, String? selectedValue, bool? enabled) $default,) {final _that = this; +switch (_that) { +case _DropdownSDUIModel(): +return $default(_that.key,_that.label,_that.placeholder,_that.items,_that.selectedValue,_that.enabled);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? key, String? label, String? placeholder, List? items, String? selectedValue, bool? enabled)? $default,) {final _that = this; +switch (_that) { +case _DropdownSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.placeholder,_that.items,_that.selectedValue,_that.enabled);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _DropdownSDUIModel implements DropdownSDUIModel { + const _DropdownSDUIModel({this.key, this.label, this.placeholder, final List? items, this.selectedValue, this.enabled}): _items = items; + factory _DropdownSDUIModel.fromJson(Map json) => _$DropdownSDUIModelFromJson(json); + +@override final String? key; +@override final String? label; +@override final String? placeholder; + final List? _items; +@override List? get items { + final value = _items; + if (value == null) return null; + if (_items is EqualUnmodifiableListView) return _items; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); +} + +@override final String? selectedValue; +@override final bool? enabled; + +/// Create a copy of DropdownSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$DropdownSDUIModelCopyWith<_DropdownSDUIModel> get copyWith => __$DropdownSDUIModelCopyWithImpl<_DropdownSDUIModel>(this, _$identity); + +@override +Map toJson() { + return _$DropdownSDUIModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _DropdownSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&(identical(other.placeholder, placeholder) || other.placeholder == placeholder)&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.selectedValue, selectedValue) || other.selectedValue == selectedValue)&&(identical(other.enabled, enabled) || other.enabled == enabled)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,placeholder,const DeepCollectionEquality().hash(_items),selectedValue,enabled); + +@override +String toString() { + return 'DropdownSDUIModel(key: $key, label: $label, placeholder: $placeholder, items: $items, selectedValue: $selectedValue, enabled: $enabled)'; +} + + +} + +/// @nodoc +abstract mixin class _$DropdownSDUIModelCopyWith<$Res> implements $DropdownSDUIModelCopyWith<$Res> { + factory _$DropdownSDUIModelCopyWith(_DropdownSDUIModel value, $Res Function(_DropdownSDUIModel) _then) = __$DropdownSDUIModelCopyWithImpl; +@override @useResult +$Res call({ + String? key, String? label, String? placeholder, List? items, String? selectedValue, bool? enabled +}); + + + + +} +/// @nodoc +class __$DropdownSDUIModelCopyWithImpl<$Res> + implements _$DropdownSDUIModelCopyWith<$Res> { + __$DropdownSDUIModelCopyWithImpl(this._self, this._then); + + final _DropdownSDUIModel _self; + final $Res Function(_DropdownSDUIModel) _then; + +/// Create a copy of DropdownSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? label = freezed,Object? placeholder = freezed,Object? items = freezed,Object? selectedValue = freezed,Object? enabled = freezed,}) { + return _then(_DropdownSDUIModel( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,placeholder: freezed == placeholder ? _self.placeholder : placeholder // ignore: cast_nullable_to_non_nullable +as String?,items: freezed == items ? _self._items : items // ignore: cast_nullable_to_non_nullable +as List?,selectedValue: freezed == selectedValue ? _self.selectedValue : selectedValue // ignore: cast_nullable_to_non_nullable +as String?,enabled: freezed == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable +as bool?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.g.dart new file mode 100644 index 0000000..8c82789 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/dropdown/model/dropdown_sdui_model.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dropdown_sdui_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_DropdownSDUIModel _$DropdownSDUIModelFromJson(Map json) => + _DropdownSDUIModel( + key: json['key'] as String?, + label: json['label'] as String?, + placeholder: json['placeholder'] as String?, + items: (json['items'] as List?) + ?.map((e) => e as String) + .toList(), + selectedValue: json['selected_value'] as String?, + enabled: json['enabled'] as bool?, + ); + +Map _$DropdownSDUIModelToJson(_DropdownSDUIModel instance) => + { + 'key': instance.key, + 'label': instance.label, + 'placeholder': instance.placeholder, + 'items': instance.items, + 'selected_value': instance.selectedValue, + 'enabled': instance.enabled, + }; diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/image_picker_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/image_picker_sdui.dart new file mode 100644 index 0000000..422dcaa --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/image_picker_sdui.dart @@ -0,0 +1,251 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:rasadyar_chicken/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart'; +import 'package:rasadyar_core/core.dart'; + +class ImagePickerSDUI extends StatefulWidget { + final ImagePickerSDUIModel model; + final Map>? images; + final Function(String key, RxList images)? onImagesChanged; + + const ImagePickerSDUI({ + super.key, + required this.model, + + this.images, + this.onImagesChanged, + }); + + @override + State createState() => _ImagePickerSDUIState(); +} + +class _ImagePickerSDUIState extends State { + late RImagePickerController imagePickerController; + RxList? imageList; + Set _addedImagePaths = {}; // برای track کردن عکس‌های اضافه شده + + @override + void initState() { + super.initState(); + imagePickerController = RImagePickerController(); + + final key = widget.model.key ?? ''; + if (key.isNotEmpty) { + // Get or create the images list for this key + if (widget.images != null && widget.images!.containsKey(key)) { + imageList = widget.images![key]!; + // اضافه کردن path های عکس‌های موجود به set + _addedImagePaths = imageList!.map((img) => img.path).toSet(); + } else { + imageList = RxList(); + if (widget.images != null) { + widget.images![key] = imageList!; + } + _addedImagePaths = {}; + } + + imagePickerController.capturedImages.clear(); + + // Listen to controller changes + imagePickerController.addListener(() { + if (imagePickerController.capturedImages.isNotEmpty && + imageList != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + final maxImages = widget.model.maxImages; + final allCapturedImages = imagePickerController.capturedImages; + + // فقط عکس‌های جدید را پیدا می‌کنیم (عکس‌هایی که قبلاً اضافه نشده‌اند) + final newImages = allCapturedImages + .where((img) => !_addedImagePaths.contains(img.path)) + .toList(); + + if (newImages.isNotEmpty) { + if (maxImages != null) { + // اگر محدودیت وجود دارد، فقط تا maxImages عکس اضافه می‌کنیم + final currentCount = imageList!.length; + final remainingSlots = maxImages - currentCount; + + if (remainingSlots > 0) { + final imagesToAdd = newImages.take(remainingSlots).toList(); + imageList!.addAll(imagesToAdd); + // اضافه کردن path های عکس‌های جدید به set + _addedImagePaths.addAll(imagesToAdd.map((img) => img.path)); + } + } else { + // اگر محدودیت وجود ندارد، همه عکس‌های جدید را اضافه می‌کنیم + imageList!.addAll(newImages); + // اضافه کردن path های عکس‌های جدید به set + _addedImagePaths.addAll(newImages.map((img) => img.path)); + } + } + + if (widget.onImagesChanged != null) { + widget.onImagesChanged!(key, imageList!); + } + }); + } + }); + } + } + + @override + void dispose() { + imagePickerController.disposeCameraController(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final key = widget.model.key ?? ''; + if (key.isEmpty || imageList == null) { + return const SizedBox.shrink(); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 10.h), + Row( + children: [ + Expanded( + child: Obx(() { + final maxImages = widget.model.maxImages; + final currentImageCount = imageList!.length; + final canAddMore = + maxImages == null || currentImageCount < maxImages; + + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + physics: BouncingScrollPhysics(), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + spacing: 8, + children: [ + ...imageList!.map( + (entry) => Stack( + children: [ + Container( + height: 80.h, + width: 80.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all( + width: 1, + color: AppColor.blackLightHover, + ), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Image.file( + File(entry.path), + fit: BoxFit.cover, + ), + ), + ), + // Delete button + Positioned( + top: 4, + left: 4, + child: GestureDetector( + onTap: () { + imageList!.removeWhere( + (element) => element.path == entry.path, + ); + // حذف path از set + _addedImagePaths.remove(entry.path); + if (widget.onImagesChanged != null) { + widget.onImagesChanged!(key, imageList!); + } + }, + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + child: Icon( + Icons.close, + color: Colors.white, + size: 16, + ), + ), + ), + ), + ], + ), + ), + // Add image button - only show if we can add more + if (canAddMore) + GestureDetector( + onTap: () { + imagePickerController.capturedImages.clear(); + + final existingPaths = imageList! + .map((img) => img.path) + .toSet(); + _addedImagePaths = existingPaths; + + Get.to( + () => RImagePicker( + controller: imagePickerController, + maxImages: maxImages != null + ? maxImages - currentImageCount + : null, + ), + fullscreenDialog: true, + transition: Transition.fade, + duration: Duration(milliseconds: 300), + ); + }, + child: Container( + height: 80.h, + width: 80.w, + padding: EdgeInsets.all(22), + decoration: BoxDecoration( + color: Color(0xFFE9E9E9), + border: Border.all( + width: 1, + color: AppColor.blackLightHover, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Assets.vec.galleryAddSvg.svg( + width: 36, + height: 36, + ), + ), + ), + ], + ), + ); + }), + ), + ], + ), + if (widget.model.label != null && widget.model.label!.isNotEmpty) ...[ + SizedBox(height: 9.h), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: widget.model.label!, + style: AppFonts.yekan14.copyWith( + color: AppColor.textColorLight, + ), + ), + if (widget.model.required == true) + TextSpan( + text: ' *', + style: AppFonts.yekan14.copyWith(color: Colors.red), + ), + ], + ), + ), + ], + ], + ); + } +} diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart new file mode 100644 index 0000000..7effffb --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'image_picker_sdui_model.freezed.dart'; +part 'image_picker_sdui_model.g.dart'; + +@freezed +abstract class ImagePickerSDUIModel with _$ImagePickerSDUIModel { + const factory ImagePickerSDUIModel({ + String? key, + String? label, + bool? required, + int? maxImages, + }) = _ImagePickerSDUIModel; + + factory ImagePickerSDUIModel.fromJson(Map json) => + _$ImagePickerSDUIModelFromJson(json); +} + diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.freezed.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.freezed.dart new file mode 100644 index 0000000..2d7fe42 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.freezed.dart @@ -0,0 +1,286 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'image_picker_sdui_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$ImagePickerSDUIModel { + + String? get key; String? get label; bool? get required; int? get maxImages; +/// Create a copy of ImagePickerSDUIModel +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ImagePickerSDUIModelCopyWith get copyWith => _$ImagePickerSDUIModelCopyWithImpl(this as ImagePickerSDUIModel, _$identity); + + /// Serializes this ImagePickerSDUIModel to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ImagePickerSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&(identical(other.required, required) || other.required == required)&&(identical(other.maxImages, maxImages) || other.maxImages == maxImages)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,required,maxImages); + +@override +String toString() { + return 'ImagePickerSDUIModel(key: $key, label: $label, required: $required, maxImages: $maxImages)'; +} + + +} + +/// @nodoc +abstract mixin class $ImagePickerSDUIModelCopyWith<$Res> { + factory $ImagePickerSDUIModelCopyWith(ImagePickerSDUIModel value, $Res Function(ImagePickerSDUIModel) _then) = _$ImagePickerSDUIModelCopyWithImpl; +@useResult +$Res call({ + String? key, String? label, bool? required, int? maxImages +}); + + + + +} +/// @nodoc +class _$ImagePickerSDUIModelCopyWithImpl<$Res> + implements $ImagePickerSDUIModelCopyWith<$Res> { + _$ImagePickerSDUIModelCopyWithImpl(this._self, this._then); + + final ImagePickerSDUIModel _self; + final $Res Function(ImagePickerSDUIModel) _then; + +/// Create a copy of ImagePickerSDUIModel +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? label = freezed,Object? required = freezed,Object? maxImages = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,required: freezed == required ? _self.required : required // ignore: cast_nullable_to_non_nullable +as bool?,maxImages: freezed == maxImages ? _self.maxImages : maxImages // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// Adds pattern-matching-related methods to [ImagePickerSDUIModel]. +extension ImagePickerSDUIModelPatterns on ImagePickerSDUIModel { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _ImagePickerSDUIModel value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _ImagePickerSDUIModel() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _ImagePickerSDUIModel value) $default,){ +final _that = this; +switch (_that) { +case _ImagePickerSDUIModel(): +return $default(_that);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ImagePickerSDUIModel value)? $default,){ +final _that = this; +switch (_that) { +case _ImagePickerSDUIModel() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String? key, String? label, bool? required, int? maxImages)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _ImagePickerSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.required,_that.maxImages);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( String? key, String? label, bool? required, int? maxImages) $default,) {final _that = this; +switch (_that) { +case _ImagePickerSDUIModel(): +return $default(_that.key,_that.label,_that.required,_that.maxImages);case _: + throw StateError('Unexpected subclass'); + +} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? key, String? label, bool? required, int? maxImages)? $default,) {final _that = this; +switch (_that) { +case _ImagePickerSDUIModel() when $default != null: +return $default(_that.key,_that.label,_that.required,_that.maxImages);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _ImagePickerSDUIModel implements ImagePickerSDUIModel { + const _ImagePickerSDUIModel({this.key, this.label, this.required, this.maxImages}); + factory _ImagePickerSDUIModel.fromJson(Map json) => _$ImagePickerSDUIModelFromJson(json); + +@override final String? key; +@override final String? label; +@override final bool? required; +@override final int? maxImages; + +/// Create a copy of ImagePickerSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ImagePickerSDUIModelCopyWith<_ImagePickerSDUIModel> get copyWith => __$ImagePickerSDUIModelCopyWithImpl<_ImagePickerSDUIModel>(this, _$identity); + +@override +Map toJson() { + return _$ImagePickerSDUIModelToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ImagePickerSDUIModel&&(identical(other.key, key) || other.key == key)&&(identical(other.label, label) || other.label == label)&&(identical(other.required, required) || other.required == required)&&(identical(other.maxImages, maxImages) || other.maxImages == maxImages)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,label,required,maxImages); + +@override +String toString() { + return 'ImagePickerSDUIModel(key: $key, label: $label, required: $required, maxImages: $maxImages)'; +} + + +} + +/// @nodoc +abstract mixin class _$ImagePickerSDUIModelCopyWith<$Res> implements $ImagePickerSDUIModelCopyWith<$Res> { + factory _$ImagePickerSDUIModelCopyWith(_ImagePickerSDUIModel value, $Res Function(_ImagePickerSDUIModel) _then) = __$ImagePickerSDUIModelCopyWithImpl; +@override @useResult +$Res call({ + String? key, String? label, bool? required, int? maxImages +}); + + + + +} +/// @nodoc +class __$ImagePickerSDUIModelCopyWithImpl<$Res> + implements _$ImagePickerSDUIModelCopyWith<$Res> { + __$ImagePickerSDUIModelCopyWithImpl(this._self, this._then); + + final _ImagePickerSDUIModel _self; + final $Res Function(_ImagePickerSDUIModel) _then; + +/// Create a copy of ImagePickerSDUIModel +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? label = freezed,Object? required = freezed,Object? maxImages = freezed,}) { + return _then(_ImagePickerSDUIModel( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String?,required: freezed == required ? _self.required : required // ignore: cast_nullable_to_non_nullable +as bool?,maxImages: freezed == maxImages ? _self.maxImages : maxImages // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.g.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.g.dart new file mode 100644 index 0000000..fce7a69 --- /dev/null +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/image_picker/model/image_picker_sdui_model.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'image_picker_sdui_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_ImagePickerSDUIModel _$ImagePickerSDUIModelFromJson( + Map json, +) => _ImagePickerSDUIModel( + key: json['key'] as String?, + label: json['label'] as String?, + required: json['required'] as bool?, + maxImages: (json['max_images'] as num?)?.toInt(), +); + +Map _$ImagePickerSDUIModelToJson( + _ImagePickerSDUIModel instance, +) => { + 'key': instance.key, + 'label': instance.label, + 'required': instance.required, + 'max_images': instance.maxImages, +}; diff --git a/packages/chicken/lib/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart b/packages/chicken/lib/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart index 5c7bc3c..8bf308b 100644 --- a/packages/chicken/lib/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart +++ b/packages/chicken/lib/presentation/widget/sdui/widgets/text_form_filed/text_form_filed_sdui.dart @@ -4,26 +4,81 @@ import 'package:rasadyar_core/core.dart'; import 'model/text_form_field_sdui_model.dart'; -Widget textFormFiledSDUI({required TextFormFieldSDUIModel model}) { +Widget textFormFiledSDUI({ + required TextFormFieldSDUIModel model, + TextEditingController? controller, +}) { List? inputFormatters = []; TextInputType? keyboardType; + VoidCallback? onTap; + bool isReadonly = model.readonly ?? false; - if (model.keyboardType == 'text') { - keyboardType = TextInputType.text; - } else if (model.keyboardType == 'number') { - keyboardType = TextInputType.numberWithOptions( - decimal: model.decimal ?? false, - ); + final textController = controller ?? TextEditingController(text: model.value); - inputFormatters.add(FilteringTextInputFormatter.digitsOnly); - } + if (model.type == 'date_picker') { + // برای date picker، readonly می‌کنیم و onTap اضافه می‌کنیم + isReadonly = true; - if (model.commaSperator ?? false) { - inputFormatters.add(SeparatorInputFormatter()); + onTap = () { + // پارس کردن تاریخ فعلی اگر وجود دارد + Jalali? initialDate; + if (textController.text.isNotEmpty) { + try { + // فرض می‌کنیم تاریخ به فرمت '1404/01/01' یا '1404-01-01' است + final dateStr = textController.text.replaceAll('-', '/'); + final parts = dateStr.split('/'); + if (parts.length == 3) { + final year = int.tryParse(parts[0]); + final month = int.tryParse(parts[1]); + final day = int.tryParse(parts[2]); + if (year != null && month != null && day != null) { + initialDate = Jalali(year, month, day); + } + } + } catch (e) { + iLog('Error parsing date: $e'); + } + } + + // اگر نتوانستیم parse کنیم، از تاریخ امروز استفاده می‌کنیم + initialDate ??= Jalali.now(); + + // نمایش date picker + Get.bottomSheet( + modalDatePicker( + initialDate: initialDate, + onDateSelected: (selectedDate) { + // فرمت کردن تاریخ و قرار دادن در controller + textController.text = selectedDate.formatCompactDate(); + }, + ), + isScrollControlled: true, + ); + }; + } else { + if (model.keyboardType == 'text') { + keyboardType = TextInputType.text; + } else if (model.keyboardType == 'number') { + final isDecimal = model.decimal ?? false; + keyboardType = TextInputType.numberWithOptions(decimal: isDecimal); + + if (!isDecimal) { + inputFormatters.add(FilteringTextInputFormatter.digitsOnly); + } + } + + if ((model.commaSperator ?? false) && + (model.decimal == null || model.decimal == false)) { + inputFormatters.add(SeparatorInputFormatter()); + } } return RTextField( - controller: TextEditingController(text: model.value), + controller: textController, + onChanged: (data) { + iLog(data); + }, + onTap: onTap, label: model.label, filled: true, filledColor: AppColor.bgLight, @@ -32,8 +87,8 @@ Widget textFormFiledSDUI({required TextFormFieldSDUIModel model}) { maxLength: model.maxLength, minLines: model.minLine, maxLines: model.maxLine, - enabled: model.enabled ?? false, + enabled: model.enabled ?? true, inputFormatters: inputFormatters, - readonly: model.readonly ?? false, + readonly: isReadonly, ); }