feat: refactor NewPageLogic to utilize SDUIWidgetModel, enhance form handling with dynamic controllers, and implement SDUIFormWidget for rendering UI components

This commit is contained in:
2025-12-28 13:50:48 +03:30
parent 0b49302434
commit 71952bef5a
27 changed files with 3022 additions and 52 deletions

View File

@@ -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<VetFarmRootLogic>();
Rxn<TextFormFieldSDUIModel> textFormFieldSDUIModel =
Rxn<TextFormFieldSDUIModel>();
Rxn<SDUIWidgetModel> sduiModel = Rxn<SDUIWidgetModel>();
Map<String, TextEditingController> controllers = {};
RxMap<String, dynamic> formState = <String, dynamic>{}.obs;
Map<String, RxList<XFile>> 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<void> 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<String, dynamic>? sduiData;
if (result.data!['info'] != null && result.data!['info'] is Map) {
final infoMap = result.data!['info'] as Map<String, dynamic>;
// 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<String, dynamic>;
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');
});
}
}

View File

@@ -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<NewPageLogic> {
}
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<NewPageLogic> {
controller.onButtonPressed();
},
),
SizedBox(height: 24.h),
],
),
);

View File

@@ -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": "ضعیف"
}
]
}
}

View File

@@ -0,0 +1,15 @@
{
"type": "dropdown",
"visible": true,
"data": {
"key": "training_status",
"label": "آموزش‌دیده در حوزه بهداشت و امنیت زیستی",
"placeholder": "آموزش‌دیده در حوزه بهداشت و امنیت زیستی",
"items": [
"بله",
"خیر"
],
"selectedValue": null,
"enabled": true
}
}

View File

@@ -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
}
}
]
}
}
]
}

View File

@@ -0,0 +1,11 @@
{
"type": "image_picker",
"visible": true,
"data": {
"key": "hall_images",
"label": "ثبت عکس سالن (حداقل ۳ زاویه)",
"required": true,
"maxImages": null
}
}

View File

@@ -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<String, TextEditingController>? controllers;
final RxMap<String, dynamic>? state;
final Function(String key, dynamic value)? onStateChanged;
final Map<String, RxList<XFile>>? images;
final Function(String key, RxList<XFile> 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 = <Widget>[];
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 = <Widget>[];
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;
}
}
}

View File

@@ -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<String, dynamic>? data,
Map<String, dynamic>? child,
List<Map<String, dynamic>>? children,
}) = _SDUIWidgetModel;
factory SDUIWidgetModel.fromJson(Map<String, dynamic> json) =>
_$SDUIWidgetModelFromJson(json);
}

View File

@@ -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>(T value) => value;
/// @nodoc
mixin _$SDUIWidgetModel {
String? get type; bool? get visible; Map<String, dynamic>? get data; Map<String, dynamic>? get child; List<Map<String, dynamic>>? 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<SDUIWidgetModel> get copyWith => _$SDUIWidgetModelCopyWithImpl<SDUIWidgetModel>(this as SDUIWidgetModel, _$identity);
/// Serializes this SDUIWidgetModel to a JSON map.
Map<String, dynamic> 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<String, dynamic>? data, Map<String, dynamic>? child, List<Map<String, dynamic>>? 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<String, dynamic>?,child: freezed == child ? _self.child : child // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,children: freezed == children ? _self.children : children // ignore: cast_nullable_to_non_nullable
as List<Map<String, dynamic>>?,
));
}
}
/// 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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(TResult Function( String? type, bool? visible, Map<String, dynamic>? data, Map<String, dynamic>? child, List<Map<String, dynamic>>? 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 extends Object?>(TResult Function( String? type, bool? visible, Map<String, dynamic>? data, Map<String, dynamic>? child, List<Map<String, dynamic>>? 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 extends Object?>(TResult? Function( String? type, bool? visible, Map<String, dynamic>? data, Map<String, dynamic>? child, List<Map<String, dynamic>>? 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<String, dynamic>? data, final Map<String, dynamic>? child, final List<Map<String, dynamic>>? children}): _data = data,_child = child,_children = children;
factory _SDUIWidgetModel.fromJson(Map<String, dynamic> json) => _$SDUIWidgetModelFromJson(json);
@override final String? type;
@override final bool? visible;
final Map<String, dynamic>? _data;
@override Map<String, dynamic>? 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<String, dynamic>? _child;
@override Map<String, dynamic>? 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<Map<String, dynamic>>? _children;
@override List<Map<String, dynamic>>? 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<String, dynamic> 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<String, dynamic>? data, Map<String, dynamic>? child, List<Map<String, dynamic>>? 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<String, dynamic>?,child: freezed == child ? _self._child : child // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,children: freezed == children ? _self._children : children // ignore: cast_nullable_to_non_nullable
as List<Map<String, dynamic>>?,
));
}
}
// dart format on

View File

@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sdui_widget_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SDUIWidgetModel _$SDUIWidgetModelFromJson(Map<String, dynamic> json) =>
_SDUIWidgetModel(
type: json['type'] as String?,
visible: json['visible'] as bool?,
data: json['data'] as Map<String, dynamic>?,
child: json['child'] as Map<String, dynamic>?,
children: (json['children'] as List<dynamic>?)
?.map((e) => e as Map<String, dynamic>)
.toList(),
);
Map<String, dynamic> _$SDUIWidgetModelToJson(_SDUIWidgetModel instance) =>
<String, dynamic>{
'type': instance.type,
'visible': instance.visible,
'data': instance.data,
'child': instance.child,
'children': instance.children,
};

View File

@@ -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';

View File

@@ -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
}
}
]
}
}
]
}
}

View File

@@ -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
}

View File

@@ -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(

View File

@@ -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<String, dynamic>? 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(),
),
],
);
});
}
}

View File

@@ -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<ChipOption>? options,
int? selectedIndex,
}) = _ChipSelectionSDUIModel;
factory ChipSelectionSDUIModel.fromJson(Map<String, dynamic> json) =>
_$ChipSelectionSDUIModelFromJson(json);
}
@freezed
abstract class ChipOption with _$ChipOption {
const factory ChipOption({
int? index,
String? label,
String? value,
}) = _ChipOption;
factory ChipOption.fromJson(Map<String, dynamic> json) =>
_$ChipOptionFromJson(json);
}

View File

@@ -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>(T value) => value;
/// @nodoc
mixin _$ChipSelectionSDUIModel {
String? get key; String? get label; List<ChipOption>? 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<ChipSelectionSDUIModel> get copyWith => _$ChipSelectionSDUIModelCopyWithImpl<ChipSelectionSDUIModel>(this as ChipSelectionSDUIModel, _$identity);
/// Serializes this ChipSelectionSDUIModel to a JSON map.
Map<String, dynamic> 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<ChipOption>? 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<ChipOption>?,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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(TResult Function( String? key, String? label, List<ChipOption>? 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 extends Object?>(TResult Function( String? key, String? label, List<ChipOption>? 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 extends Object?>(TResult? Function( String? key, String? label, List<ChipOption>? 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<ChipOption>? options, this.selectedIndex}): _options = options;
factory _ChipSelectionSDUIModel.fromJson(Map<String, dynamic> json) => _$ChipSelectionSDUIModelFromJson(json);
@override final String? key;
@override final String? label;
final List<ChipOption>? _options;
@override List<ChipOption>? 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<String, dynamic> 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<ChipOption>? 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<ChipOption>?,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<ChipOption> get copyWith => _$ChipOptionCopyWithImpl<ChipOption>(this as ChipOption, _$identity);
/// Serializes this ChipOption to a JSON map.
Map<String, dynamic> 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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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<String, dynamic> 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<String, dynamic> 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

View File

@@ -0,0 +1,40 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'chip_selection_sdui_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_ChipSelectionSDUIModel _$ChipSelectionSDUIModelFromJson(
Map<String, dynamic> json,
) => _ChipSelectionSDUIModel(
key: json['key'] as String?,
label: json['label'] as String?,
options: (json['options'] as List<dynamic>?)
?.map((e) => ChipOption.fromJson(e as Map<String, dynamic>))
.toList(),
selectedIndex: (json['selected_index'] as num?)?.toInt(),
);
Map<String, dynamic> _$ChipSelectionSDUIModelToJson(
_ChipSelectionSDUIModel instance,
) => <String, dynamic>{
'key': instance.key,
'label': instance.label,
'options': instance.options,
'selected_index': instance.selectedIndex,
};
_ChipOption _$ChipOptionFromJson(Map<String, dynamic> json) => _ChipOption(
index: (json['index'] as num?)?.toInt(),
label: json['label'] as String?,
value: json['value'] as String?,
);
Map<String, dynamic> _$ChipOptionToJson(_ChipOption instance) =>
<String, dynamic>{
'index': instance.index,
'label': instance.label,
'value': instance.value,
};

View File

@@ -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<String, dynamic>? 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<String>(
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,
);
});
}
}

View File

@@ -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<String>? items,
String? selectedValue,
bool? enabled,
}) = _DropdownSDUIModel;
factory DropdownSDUIModel.fromJson(Map<String, dynamic> json) =>
_$DropdownSDUIModelFromJson(json);
}

View File

@@ -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>(T value) => value;
/// @nodoc
mixin _$DropdownSDUIModel {
String? get key; String? get label; String? get placeholder; List<String>? 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<DropdownSDUIModel> get copyWith => _$DropdownSDUIModelCopyWithImpl<DropdownSDUIModel>(this as DropdownSDUIModel, _$identity);
/// Serializes this DropdownSDUIModel to a JSON map.
Map<String, dynamic> 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<String>? 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<String>?,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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(TResult Function( String? key, String? label, String? placeholder, List<String>? 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 extends Object?>(TResult Function( String? key, String? label, String? placeholder, List<String>? 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 extends Object?>(TResult? Function( String? key, String? label, String? placeholder, List<String>? 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<String>? items, this.selectedValue, this.enabled}): _items = items;
factory _DropdownSDUIModel.fromJson(Map<String, dynamic> json) => _$DropdownSDUIModelFromJson(json);
@override final String? key;
@override final String? label;
@override final String? placeholder;
final List<String>? _items;
@override List<String>? 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<String, dynamic> 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<String>? 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<String>?,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

View File

@@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'dropdown_sdui_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_DropdownSDUIModel _$DropdownSDUIModelFromJson(Map<String, dynamic> json) =>
_DropdownSDUIModel(
key: json['key'] as String?,
label: json['label'] as String?,
placeholder: json['placeholder'] as String?,
items: (json['items'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
selectedValue: json['selected_value'] as String?,
enabled: json['enabled'] as bool?,
);
Map<String, dynamic> _$DropdownSDUIModelToJson(_DropdownSDUIModel instance) =>
<String, dynamic>{
'key': instance.key,
'label': instance.label,
'placeholder': instance.placeholder,
'items': instance.items,
'selected_value': instance.selectedValue,
'enabled': instance.enabled,
};

View File

@@ -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<String, RxList<XFile>>? images;
final Function(String key, RxList<XFile> images)? onImagesChanged;
const ImagePickerSDUI({
super.key,
required this.model,
this.images,
this.onImagesChanged,
});
@override
State<ImagePickerSDUI> createState() => _ImagePickerSDUIState();
}
class _ImagePickerSDUIState extends State<ImagePickerSDUI> {
late RImagePickerController imagePickerController;
RxList<XFile>? imageList;
Set<String> _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<XFile>();
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),
),
],
),
),
],
],
);
}
}

View File

@@ -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<String, dynamic> json) =>
_$ImagePickerSDUIModelFromJson(json);
}

View File

@@ -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>(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<ImagePickerSDUIModel> get copyWith => _$ImagePickerSDUIModelCopyWithImpl<ImagePickerSDUIModel>(this as ImagePickerSDUIModel, _$identity);
/// Serializes this ImagePickerSDUIModel to a JSON map.
Map<String, dynamic> 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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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<String, dynamic> 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<String, dynamic> 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

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'image_picker_sdui_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_ImagePickerSDUIModel _$ImagePickerSDUIModelFromJson(
Map<String, dynamic> 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<String, dynamic> _$ImagePickerSDUIModelToJson(
_ImagePickerSDUIModel instance,
) => <String, dynamic>{
'key': instance.key,
'label': instance.label,
'required': instance.required,
'max_images': instance.maxImages,
};

View File

@@ -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<TextInputFormatter>? 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,
);
}