diff --git a/lib/src/form/form_helper.astromic.dart b/lib/src/form/form_helper.astromic.dart index add3c00..fd465ed 100644 --- a/lib/src/form/form_helper.astromic.dart +++ b/lib/src/form/form_helper.astromic.dart @@ -2,5 +2,6 @@ export 'package:form_controller/form_controller.dart'; export 'src/controller.dart'; export 'src/form_field.dart'; export 'src/form_value_wrapper.dart'; +export 'src/form_group_wrapper.dart'; export 'src/enums/enums.exports.dart'; export 'src/models/models.exports.dart'; diff --git a/lib/src/form/src/controller.dart b/lib/src/form/src/controller.dart index cde3c37..dd6e4a6 100644 --- a/lib/src/form/src/controller.dart +++ b/lib/src/form/src/controller.dart @@ -105,6 +105,35 @@ class AstromicFormController extends FormController { } */ + bool hasGroup(String formGroupID, {bool isSubGroup = false}) { + FormGroupStructure? groupStructure; + if (isSubGroup) { + FormGroupStructure? parentGroup = _formGroups.where((FormGroupStructure f) => (f.subGroups?.map((FormGroupStructure ss) => ss.id).contains(formGroupID) ?? false)).nonNulls.toList().firstOrNull; + if (parentGroup != null && parentGroup.subGroups != null && parentGroup.subGroups!.map((FormGroupStructure i) => i.id).contains(formGroupID)) { + groupStructure = parentGroup.subGroups!.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } + } else { + groupStructure = _formGroups.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } + // + return groupStructure != null; + } + + FormGroupStructure? getGroupStructure(String formGroupID, {bool isSubGroup = false}) { + // Get the group structure with the same ID + FormGroupStructure? groupStructure; + if (isSubGroup) { + FormGroupStructure? parentGroup = _formGroups.where((FormGroupStructure f) => (f.subGroups?.map((FormGroupStructure ss) => ss.id).contains(formGroupID) ?? false)).nonNulls.toList().firstOrNull; + if (parentGroup != null && parentGroup.subGroups != null && parentGroup.subGroups!.map((FormGroupStructure i) => i.id).contains(formGroupID)) { + groupStructure = parentGroup.subGroups!.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } + } else { + groupStructure = _formGroups.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } + // + return groupStructure; + } + FormGroupValue? getFormGroupValue(String formGroupID, {bool isSubGroup = false}) { // Get the group structure with the same ID FormGroupStructure? groupStructure; @@ -148,8 +177,11 @@ class AstromicFormController extends FormController { for (int i = 0; i < firstSetOfFieldsWithID.length; i++) { instances.add( FormGroupInstance( + composedID: '$formGroupID-#$i-', fields: Map.fromEntries(fieldsIDs.map((String id) => MapEntry('$formGroupID-#$i-$id', value('$formGroupID-#$i-$id'))).toList()), - values: valuesIDs.isNotEmpty? Map.fromEntries(valuesIDs.map((String a) => MapEntry('$formGroupID-#$i-$a', getValue('$formGroupID-#$i-$a'))).toList()) : {}, + values: valuesIDs.isNotEmpty + ? Map.fromEntries(valuesIDs.map((String a) => MapEntry('$formGroupID-#$i-$a', getValue('$formGroupID-#$i-$a'))).toList()) + : {}, subGroups: subValues, ), ); @@ -159,28 +191,112 @@ class AstromicFormController extends FormController { return groupValue; } - // void addToFormGroup(String formGroupID) { - // // Get the group structure with the same ID - // FormGroupStructure? groupWithSameID = _formGroups.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; - // if (groupWithSameID == null) { - // throw Exception('The group ID $formGroupID doesn\'t have any elements. Did you initialize the group?'); - // } + String addInstanceToFormGroup(String formGroupID, {bool isSubGroup = false}) { + // Get the group structure with the same ID + FormGroupStructure? groupStructure; + if (isSubGroup) { + FormGroupStructure? parentGroup = _formGroups.where((FormGroupStructure f) => (f.subGroups?.map((FormGroupStructure ss) => ss.id).contains(formGroupID) ?? false)).nonNulls.toList().firstOrNull; + if (parentGroup != null && parentGroup.subGroups != null && parentGroup.subGroups!.map((FormGroupStructure i) => i.id).contains(formGroupID)) { + groupStructure = parentGroup.subGroups!.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } else { + throw Exception('The group ID $formGroupID doesn\'t have any elements. Did you initialize the group?'); + } + } else { + groupStructure = _formGroups.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + if (groupStructure == null) { + throw Exception('The group ID $formGroupID doesn\'t have any elements. Did you initialize the group?'); + } + } - // // Get the current fields with this ID - // Map firstSetOfFieldsWithID = Map.fromEntries( - // controllers.entries.where((MapEntry c) => RegExp(formGroupID + r'-#[\d+]-' + groupWithSameID.fields.entries.first.key).hasMatch(c.key)).nonNulls.toList(), - // ); + // Get the current fields with this ID + Map firstSetOfFieldsWithID = Map.fromEntries( + controllers.entries.where((MapEntry c) => RegExp(formGroupID + r'-#[\d+]-' + groupStructure!.fields.first).hasMatch(c.key)).nonNulls.toList(), + ); - // for (MapEntry fieldEntry in groupWithSameID.fields.entries) { - // String fieldID = '$formGroupID-${firstSetOfFieldsWithID.length}-${fieldEntry.key}'; - // controller(fieldID); - // if (fieldEntry.value) { - // // Is a value field - // } else { - // // Is a text field - // } - // } - // } + // get the fields IDs + List fieldsIDs = groupStructure!.fields.nonNulls.toList(); + print('fieldIDs: $fieldsIDs'); + // get the values IDs + List valuesIDs = groupStructure.values?.nonNulls.toList() ?? []; + print('valueIDs: $valuesIDs'); + + // Add the controllers and values. + + for (String fieldID in groupStructure.fields) { + String finalFieldID = '${groupStructure.id}-#${firstSetOfFieldsWithID.length}-$fieldID'; + controller(finalFieldID); + } + if (groupStructure.values != null && groupStructure.values!.isNotEmpty) { + for (String valueID in groupStructure.values!) { + String finalFieldID = '${groupStructure.id}-#${firstSetOfFieldsWithID.length}-$valueID'; + controller(finalFieldID); + } + } + return '${groupStructure.id}-#${firstSetOfFieldsWithID.length}-'; + } + + String removeInstanceFromFormGroup(String formGroupID, int indexToRemove, {bool isSubGroup = false}) { + // Get the group structure with the same ID + FormGroupStructure? groupStructure; + if (isSubGroup) { + FormGroupStructure? parentGroup = _formGroups.where((FormGroupStructure f) => (f.subGroups?.map((FormGroupStructure ss) => ss.id).contains(formGroupID) ?? false)).nonNulls.toList().firstOrNull; + if (parentGroup != null && parentGroup.subGroups != null && parentGroup.subGroups!.map((FormGroupStructure i) => i.id).contains(formGroupID)) { + groupStructure = parentGroup.subGroups!.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + } else { + throw Exception('The group ID $formGroupID doesn\'t have any elements. Did you initialize the group?'); + } + } else { + groupStructure = _formGroups.where((FormGroupStructure f) => f.id == formGroupID).nonNulls.toList().firstOrNull; + if (groupStructure == null) { + throw Exception('The group ID $formGroupID doesn\'t have any elements. Did you initialize the group?'); + } + } + + // Get the current fields with this ID + Map firstSetOfFieldsWithID = Map.fromEntries( + controllers.entries.where((MapEntry c) => RegExp(formGroupID + r'-#[\d+]-' + groupStructure!.fields.first).hasMatch(c.key)).nonNulls.toList(), + ); + + if (i >= firstSetOfFieldsWithID.length) { + throw Exception('The index to remove is larger than the whole instances count.'); + } else { + // get the fields IDs + List fieldsIDs = groupStructure!.fields.nonNulls.toList(); + print('fieldIDs: $fieldsIDs'); + // get the values IDs + List valuesIDs = groupStructure.values?.nonNulls.toList() ?? []; + print('valueIDs: $valuesIDs'); + + if (i == (firstSetOfFieldsWithID.length - 1)) { + // Remove the last item + for (String fieldID in groupStructure.fields) { + removeController('${groupStructure.id}-#$i-$fieldID'); + } + if (groupStructure.values != null && groupStructure.values!.isNotEmpty) { + for (String valueID in groupStructure.values!) { + removeController('${groupStructure.id}-#$i-$valueID'); + } + } + } else { + // Switch and remove + int nextIndex = i + 1; + for (String fieldID in groupStructure.fields) { + set('${groupStructure.id}-#$i-$fieldID', value('${groupStructure.id}-#$nextIndex-$fieldID')); + removeController('${groupStructure.id}-#$nextIndex-$fieldID'); + } + if (groupStructure.values != null && groupStructure.values!.isNotEmpty) { + for (String valueID in groupStructure.values!) { + set('${groupStructure.id}-#$i-$valueID', value('${groupStructure.id}-#$nextIndex-$valueID')); + removeController('${groupStructure.id}-#$nextIndex-$valueID'); + } + } + } + } + + // Remove the controllers and values. + + return '${groupStructure.id}-#${firstSetOfFieldsWithID.length}-'; + } // void removeFromFormGroup(String formGroupID, int index) { // // Get all the groups with the same ID diff --git a/lib/src/form/src/form_group_wrapper.dart b/lib/src/form/src/form_group_wrapper.dart new file mode 100644 index 0000000..ac82c18 --- /dev/null +++ b/lib/src/form/src/form_group_wrapper.dart @@ -0,0 +1,136 @@ +//s1 Imports +//s2 Packages +//s3 Core Packages +import 'package:flutter/material.dart'; + +import '../../../astromic_helpers.dart'; +//s3 Internal Packages +// import 'package:astromic_elements/astromic_elements.dart'; +//s3 3rd-party Packages +//s2 Utility +//s3 Configs +// import '../../../../../core/configs/routing/routing.config.dart'; +//s3 Misc +//s2 Domain +//s3 Entities +//s3 Usecases +//s2 Presentation +//s3 Design +// import '../../../../design-system/design_system.dart'; +//s3 Presenters +//s3 Widgets +//s1 Exports + +class FormGroupWrapper extends StatefulWidget { + //SECTION - Widget Arguments + final AstromicFormController formController; + final String groupID; + final Widget Function(List children, String Function() addItem, void Function(int) removeItem) groupBuilder; + final Widget Function(int index, String composedID, VoidCallback removeItem) itemBuilder; + final int startLength; + //!SECTION + // + const FormGroupWrapper({ + super.key, + required this.formController, + required this.groupID, + required this.groupBuilder, + required this.itemBuilder, + this.startLength = 0, + }); + + @override + State createState() => _FormGroupWrapperState(); +} + +class _FormGroupWrapperState extends State { + // + //SECTION - State Variables + //s1 --State + late List instances; + //s1 --State + // + //s1 --Controllers + //s1 --Controllers + // + //s1 --Constants + //s1 --Constants + //!SECTION + + @override + void initState() { + super.initState(); + // + //SECTION - State Variables initializations & Listeners + //s1 --State + //s1 --State + // + //s1 --Controllers & Listeners + instances = widget.formController.getFormGroupValue(widget.groupID)!.instances; + //s1 --Controllers & Listeners + // + //s1 --Late & Async Initializers + //s1 --Late & Async Initializers + //!SECTION + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // + //SECTION - State Variables initializations & Listeners + //s1 --State + //s1 --State + // + //s1 --Controllers & Listeners + //s1 --Controllers & Listeners + // + //!SECTION + } + + //SECTION - Dumb Widgets + //!SECTION + + //SECTION - Stateless functions + //!SECTION + + //SECTION - Action Callbacks + //!SECTION + + @override + Widget build(BuildContext context) { + //SECTION - Build Setup + //s1 --Values + //double w = MediaQuery.of(context).size.width; + //double h = MediaQuery.of(context).size.height; + //s1 --Values + // + //s1 --Contexted Widgets + //s1 --Contexted Widgets + //!SECTION + + //SECTION - Build Return + return widget.groupBuilder( + // Children + List.generate(instances.length, (int i) => widget.itemBuilder(i, instances[i].composedID, () => _removeItem(i))), + // Add Callback + () { + return widget.formController.addInstanceToFormGroup(widget.groupID); + }, + // Remove Callback + (int i) => _removeItem(i), + ); + //!SECTION + } + + void _removeItem(int i) { + widget.formController.removeInstanceFromFormGroup(widget.groupID, i); + } + + @override + void dispose() { + //SECTION - Disposable variables + //!SECTION + super.dispose(); + } +} diff --git a/lib/src/form/src/models/form_group_instance.model.dart b/lib/src/form/src/models/form_group_instance.model.dart index 8ddc554..3510550 100644 --- a/lib/src/form/src/models/form_group_instance.model.dart +++ b/lib/src/form/src/models/form_group_instance.model.dart @@ -5,21 +5,25 @@ import 'package:flutter/foundation.dart'; import 'form_group_value.model.dart'; class FormGroupInstance { + String composedID; Map fields; Map values; List? subGroups; FormGroupInstance({ + required this.composedID, required this.fields, required this.values, this.subGroups, }); FormGroupInstance copyWith({ + String? composedID, Map? fields, Map? values, List? subGroups, }) { return FormGroupInstance( + composedID: composedID ?? this.composedID, fields: fields ?? this.fields, values: values ?? this.values, subGroups: subGroups ?? this.subGroups, @@ -28,6 +32,7 @@ class FormGroupInstance { Map toMap() { return { + 'composedID': composedID, 'fields': fields, 'values': values, 'subGroups': subGroups?.map((FormGroupValue x) => x.toMap()).toList(), @@ -36,6 +41,7 @@ class FormGroupInstance { factory FormGroupInstance.fromMap(Map map) { return FormGroupInstance( + composedID: map['composedID'], fields: Map.from(map['fields'] as Map), values: Map.from(map['values'] as Map), subGroups: map['subGroups'] != null @@ -53,16 +59,15 @@ class FormGroupInstance { factory FormGroupInstance.fromJson(String source) => FormGroupInstance.fromMap(json.decode(source) as Map); @override - String toString() => 'FormGroupInstance(fields: $fields, values: $values, subGroups: $subGroups)'; + String toString() => 'FormGroupInstance(composedID: $composedID, fields: $fields, values: $values, subGroups: $subGroups)'; @override bool operator ==(covariant FormGroupInstance other) { if (identical(this, other)) return true; - return mapEquals(other.fields, fields) && mapEquals(other.values, values) && listEquals(other.subGroups, subGroups); + return mapEquals(other.fields, fields) && mapEquals(other.values, values) && listEquals(other.subGroups, subGroups) && composedID == other.composedID; } @override - int get hashCode => fields.hashCode ^ values.hashCode ^ subGroups.hashCode; - + int get hashCode => composedID.hashCode ^ fields.hashCode ^ values.hashCode ^ subGroups.hashCode; }