[SYNC] Doing the initial Values thing.

This commit is contained in:
2025-05-28 11:52:01 +03:00
parent a4741c3137
commit 898073bc5e
7 changed files with 274 additions and 73 deletions

View File

@@ -15,20 +15,43 @@ import 'enums/enums.exports.dart';
import 'models/form_group_instance.model.dart'; import 'models/form_group_instance.model.dart';
import 'models/form_group_structure.model.dart'; import 'models/form_group_structure.model.dart';
import 'models/form_group_value.model.dart'; import 'models/form_group_value.model.dart';
import 'models/initial_form_group_values.model.dart';
import 'models/initial_values.model.dart';
//s1 Exports //s1 Exports
part './helpers/controller_states.helper.dart';
/// A specialized form controller to handle form states, /// A specialized form controller to handle form states,
class AstromicFormController extends FormController { class AstromicFormController extends FormController {
AstromicFormController({ AstromicFormController({
Map<String, (String initialText, bool initialObscurity)>? initialValues, AstromicFormInitialValues? initialValues,
Map<String, (dynamic, bool)>? initialHostedValues,
this.errorStream, this.errorStream,
}) : super(initialValues) { }) : super(null) {
// Add states and messages based on initial controllers. if (initialValues != null) {
_addInitialControllers(initialValues); // Take in the initials and proceed to fill:
_addInitialHostedValues(initialHostedValues); //1. Field Values
if (initialValues.fieldValues != null && initialValues.fieldValues!.isNotEmpty) {
for (MapEntry<String, String?> fieldValueEntry in initialValues.fieldValues!.entries) {
String k = fieldValueEntry.key;
String v = fieldValueEntry.value ?? '';
bool isObs = initialValues.fieldObscurityValues != null && initialValues.fieldObscurityValues!.containsKey(k) && initialValues.fieldObscurityValues![k]!;
//
controller(k, initialText: v, isObscure: isObs);
}
}
//2. Hosted Values
if (initialValues.hostedValues != null && initialValues.hostedValues!.isNotEmpty) {
for (MapEntry<String, (dynamic, bool)> hostedValueEntry in initialValues.hostedValues!.entries) {
String k = hostedValueEntry.key;
dynamic v = hostedValueEntry.value.$1;
dynamic isReq = hostedValueEntry.value.$2;
//
setValue(k, v, isRequired: isReq);
}
}
//3. Form Group Values
if (initialValues.groupValues != null && initialValues.groupValues!.isNotEmpty) {
_initializeFormGroups(initialValues.groupValues!);
}
}
} }
//SECTION - Overrides //SECTION - Overrides
@@ -74,12 +97,26 @@ class AstromicFormController extends FormController {
final Stream<List<(String internalCode, String? message)>>? errorStream; final Stream<List<(String internalCode, String? message)>>? errorStream;
//S1 - Methods //S1 - Methods
/// Get the field state and message of a specific field using it's ID.
(AstromicFieldState, String? message)? getState(String fieldId) => (fieldStates.containsKey(fieldId)) ? (fieldStates[fieldId]!, fieldMessages[fieldId]) : null;
/// Set the field state and message of a specific field using it's ID.
void setState(String fieldId, AstromicFieldState state, {String? message}) {
if (!fieldStates.containsKey(fieldId)) {
throw Exception('The state of the field ID $fieldId does not exist.');
}
fieldStates[fieldId] = state;
fieldMessages[fieldId] = message;
_stateStreamController.add((fieldId, state));
}
/// Reset the state of a specific field using it's ID.
void resetState(String fieldId) => setState(fieldId, AstromicFieldState.idle);
//!SECTION //!SECTION
//SECTION - Hosted Values //SECTION - Hosted Values
//S1 - State Variables //S1 - State Variables
final Map<String, (dynamic, bool)> _hostedValues = <String, (dynamic, bool)>{}; final Map<String, (dynamic value, bool isRequired)> _hostedValues = <String, (dynamic, bool)>{};
//S1 - Streams //S1 - Streams
static final StreamController<(String, bool)> _hostedValueValidationStreamController = StreamController<(String id, bool)>.broadcast(); static final StreamController<(String, bool)> _hostedValueValidationStreamController = StreamController<(String id, bool)>.broadcast();
@@ -140,6 +177,7 @@ class AstromicFormController extends FormController {
//S1 - State Variables //S1 - State Variables
final List<FormGroupStructure> _formGroups = <FormGroupStructure>[]; final List<FormGroupStructure> _formGroups = <FormGroupStructure>[];
//S1 - Methods //S1 - Methods
/// Does the controller has a field group with this ID. /// Does the controller has a field group with this ID.
bool hasFieldGroup(String formGroupID) => getGroupStructure(formGroupID) != null; bool hasFieldGroup(String formGroupID) => getGroupStructure(formGroupID) != null;
@@ -245,21 +283,12 @@ class AstromicFormController extends FormController {
for (final String fieldID in structure.fields) { for (final String fieldID in structure.fields) {
final String fullID = standeredGroupFormat(baseID, index.toString(), fieldID); final String fullID = standeredGroupFormat(baseID, index.toString(), fieldID);
String? preFilled; controller(fullID);
if (structure.preFields?.containsKey(fieldID) ?? false) {
preFilled = structure.preFields![fieldID];
}
controller(fullID, initialText: preFilled);
} }
for (final String valueID in structure.values ?? <String>[]) { for (final String valueID in structure.values ?? <String>[]) {
final String fullID = standeredGroupFormat(baseID, index.toString(), valueID); final String fullID = standeredGroupFormat(baseID, index.toString(), valueID);
(String, dynamic)? preFilled; controller(fullID);
if (structure.preValues?.containsKey(valueID) ?? false) {
preFilled = structure.preValues![valueID];
}
controller(fullID, initialText: preFilled?.$1);
setValue(valueID, preFilled?.$2);
} }
} }
@@ -533,26 +562,71 @@ class AstromicFormController extends FormController {
//SECTION - Helper Methods //SECTION - Helper Methods
String standeredGroupFormat(String groupID, String groupIndex, String? secondaryID) => '$groupID-#$groupIndex${secondaryID == null ? "" : "-$secondaryID"}'; String standeredGroupFormat(String groupID, String groupIndex, String? secondaryID) => '$groupID-#$groupIndex${secondaryID == null ? "" : "-$secondaryID"}';
void _addInitialControllers(Map<String, (String, bool)>? initialValues) { // void _addInitialControllers(Map<String, (String, bool)>? initialValues) {
if (initialValues != null) { // if (initialValues != null) {
// Add in the initial field states... // // Add in the initial field states...
fieldStates.addEntries(initialValues.entries.map((MapEntry<String, (String, bool)> e) => MapEntry<String, AstromicFieldState>( // fieldStates.addEntries(initialValues.entries.map((MapEntry<String, (String, bool)> e) => MapEntry<String, AstromicFieldState>(
e.key, // controller ID // e.key, // controller ID
AstromicFieldState.idle, // Initial state of any new controller is Idle // AstromicFieldState.idle, // Initial state of any new controller is Idle
))); // )));
// Add in the initial field messages... // // Add in the initial field messages...
fieldMessages.addEntries(initialValues.entries.toList().map((MapEntry<String, (String, bool)> e) => MapEntry<String, String?>( // fieldMessages.addEntries(initialValues.entries.toList().map((MapEntry<String, (String, bool)> e) => MapEntry<String, String?>(
e.key, // Controller ID // e.key, // Controller ID
null, // The initial message it has which is Null // null, // The initial message it has which is Null
))); // )));
} // }
} // }
void _addInitialHostedValues(Map<String, (dynamic, bool)>? initialValues) { // void _addInitialHostedValues(Map<String, (dynamic, bool)>? initialValues) {
if (initialValues != null) { // if (initialValues != null) {
for (MapEntry<String, (dynamic, bool)> vEntry in initialValues.entries) { // for (MapEntry<String, (dynamic, bool)> vEntry in initialValues.entries) {
setValue(vEntry.key, vEntry.value.$1, isRequired: vEntry.value.$2); // setValue(vEntry.key, vEntry.value.$1, isRequired: vEntry.value.$2);
// }
// }
// }
_initializeFormGroups(Map<String, InitialFormGroupValue> initVals, {Map<String, int>? parents}) {
for (MapEntry<String, InitialFormGroupValue> groupValueEntry in initVals.entries) {
String groupID = groupValueEntry.key;
InitialFormGroupValue groupValue = groupValueEntry.value;
for (int i = 0; i < groupValue.instancesCount; i++) {
int currentGroupIndex = i;
// Check for the subgroups
if (groupValueEntry.value.subGroups != null && groupValueEntry.value.subGroups!.isNotEmpty) {
// There are subgroups.
_initializeFormGroups(groupValueEntry.value.subGroups!, parents: <String, int>{...?parents, groupID: currentGroupIndex});
}
String? prefix = parents?.entries.map((MapEntry<String, int> parentEntry) => standeredGroupFormat(parentEntry.key, parentEntry.value.toString(), null)).join('-');
// Fields
if (groupValue.fieldValues != null) {
for (MapEntry<String, List<String?>> fve in groupValue.fieldValues!.entries) {
assert(fve.value.length == groupValue.instancesCount, 'Your supplied list of `${fve.key}` is not `${groupValue.instancesCount}` as stated..');
if (groupValue.fieldObscurityValues != null) {
assert(groupValue.fieldObscurityValues!.length == groupValue.instancesCount, 'Your supplied obscurity list of `${fve.key}` is not `${groupValue.instancesCount}` as stated..');
}
String fieldKey = fve.key;
String fID = standeredGroupFormat((prefix != null ? '$prefix-' : '') + groupID, i.toString(), fieldKey);
String? fieldValue = fve.value[i] ?? '';
bool obscValue = groupValue.fieldObscurityValues?[fieldKey]?[i] ?? false;
controller(fID, initialText: fieldValue, isObscure: obscValue);
}
}
// Values
if (groupValue.hostedValues != null) {
for (MapEntry<String, List<(dynamic, bool)>> hve in groupValue.hostedValues!.entries) {
assert(hve.value.length == groupValue.instancesCount, 'Your supplied list of `${hve.key}` is not `${groupValue.instancesCount}` as stated..');
String fieldKey = hve.key;
String fID = standeredGroupFormat((prefix != null ? '$prefix-' : '') + groupID, i.toString(), fieldKey);
dynamic fieldValue = hve.value[i].$1;
bool isReq = hve.value[i].$2;
setValue(fID, fieldValue, isRequired: isReq);
}
}
} }
} }
} }

View File

@@ -1,16 +0,0 @@
part of '../controller.dart';
/// Get the field state and message of a specific field using it's ID.
(AstromicFieldState, String? message)? getState(String fieldId) => (fieldStates.containsKey(fieldId)) ? (fieldStates[fieldId]!, fieldMessages[fieldId]) : null;
/// Set the field state and message of a specific field using it's ID.
void setState(String fieldId, AstromicFieldState state, {String? message}) {
if (!fieldStates.containsKey(fieldId)) {
throw Exception('The state of the field ID $fieldId does not exist.');
}
fieldStates[fieldId] = state;
fieldMessages[fieldId] = message;
_stateStreamController.add((fieldId, state));
}
/// Reset the state of a specific field using it's ID.
void resetState(String fieldId) => setState(fieldId, AstromicFieldState.idle);

View File

@@ -5,16 +5,12 @@ import 'package:flutter/foundation.dart';
class FormGroupStructure { class FormGroupStructure {
final String id; final String id;
final List<String> fields; final List<String> fields;
final Map<String, String>? preFields;
final List<String>? values; final List<String>? values;
final Map<String, (String, dynamic)>? preValues;
final List<(int initialCount, FormGroupStructure structure)>? subGroups; final List<(int initialCount, FormGroupStructure structure)>? subGroups;
FormGroupStructure({ FormGroupStructure({
required this.id, required this.id,
required this.fields, required this.fields,
this.values, this.values,
this.preFields,
this.preValues,
this.subGroups, this.subGroups,
}); });
@@ -30,8 +26,6 @@ class FormGroupStructure {
id: id ?? this.id, id: id ?? this.id,
fields: fields ?? this.fields, fields: fields ?? this.fields,
values: values ?? this.values, values: values ?? this.values,
preFields: preFields ?? this.preFields,
preValues: preValues ?? this.preValues,
subGroups: subGroups ?? this.subGroups, subGroups: subGroups ?? this.subGroups,
); );
} }
@@ -41,8 +35,6 @@ class FormGroupStructure {
'id': id, 'id': id,
'fields': fields, 'fields': fields,
'values': values, 'values': values,
'preFields': preFields,
'preValues': preValues,
'subGroups': subGroups?.map(((int initialCount, FormGroupStructure structure) x) => <String, dynamic>{'structure': x.$2.toMap(), 'initialCount': x.$1}).toList(), 'subGroups': subGroups?.map(((int initialCount, FormGroupStructure structure) x) => <String, dynamic>{'structure': x.$2.toMap(), 'initialCount': x.$1}).toList(),
}; };
} }
@@ -52,8 +44,6 @@ class FormGroupStructure {
id: map['id'] as String, id: map['id'] as String,
fields: List<String>.from(map['fields'] as List<String>), fields: List<String>.from(map['fields'] as List<String>),
values: map['values'] != null ? List<String>.from(map['values'] as List<String>) : null, values: map['values'] != null ? List<String>.from(map['values'] as List<String>) : null,
preFields: map['preFields'] != null ? map['preFields'] as Map<String,String> : null,
preValues: map['preValues'] != null ? map['preValues'] as Map<String,(String,dynamic)>: null,
subGroups: map['subGroups'] != null subGroups: map['subGroups'] != null
? (map['subGroups'] as List<Map<String, dynamic>>).map((Map<String, dynamic> map) => (int.tryParse(map['initialCount']) ?? 0, FormGroupStructure.fromMap(map['structure']))).toList() ? (map['subGroups'] as List<Map<String, dynamic>>).map((Map<String, dynamic> map) => (int.tryParse(map['initialCount']) ?? 0, FormGroupStructure.fromMap(map['structure']))).toList()
: null, : null,
@@ -66,23 +56,18 @@ class FormGroupStructure {
@override @override
String toString() { String toString() {
return 'FormGroupStructure(id: $id, fields: $fields, values: $values, preFields: $preFields, preValues: $preValues, subGroups: $subGroups)'; return 'FormGroupStructure(id: $id, fields: $fields, values: $values, subGroups: $subGroups)';
} }
@override @override
bool operator ==(covariant FormGroupStructure other) { bool operator ==(covariant FormGroupStructure other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other.id == id && return other.id == id && listEquals(other.fields, fields) && listEquals(other.values, values) && listEquals(other.subGroups, subGroups);
listEquals(other.fields, fields) &&
listEquals(other.values, values) &&
mapEquals(other.preFields, preFields) &&
mapEquals(other.preValues, preValues) &&
listEquals(other.subGroups, subGroups);
} }
@override @override
int get hashCode { int get hashCode {
return id.hashCode ^ fields.hashCode ^ values.hashCode ^ preFields.hashCode ^ preValues.hashCode ^ subGroups.hashCode; return id.hashCode ^ fields.hashCode ^ values.hashCode ^ subGroups.hashCode;
} }
} }

View File

@@ -0,0 +1,80 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'package:flutter/foundation.dart';
class InitialFormGroupValue {
final int instancesCount;
final Map<String, List<String?>>? fieldValues;
final Map<String, List<bool>>? fieldObscurityValues;
final Map<String, List<(dynamic, bool)>>? hostedValues;
final Map<String, InitialFormGroupValue>? subGroups;
InitialFormGroupValue({
required this.instancesCount,
this.fieldValues,
this.fieldObscurityValues,
this.hostedValues,
this.subGroups,
});
InitialFormGroupValue copyWith({
int? instancesCount,
Map<String, List<String?>>? fieldValues,
Map<String, List<bool>>? fieldObscurityValues,
Map<String, List<(dynamic, bool)>>? hostedValues,
Map<String, InitialFormGroupValue>? subGroups,
}) {
return InitialFormGroupValue(
instancesCount: instancesCount ?? this.instancesCount,
fieldValues: fieldValues ?? this.fieldValues,
fieldObscurityValues: fieldObscurityValues ?? this.fieldObscurityValues,
hostedValues: hostedValues ?? this.hostedValues,
subGroups: subGroups ?? this.subGroups,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'instancesCount': instancesCount,
'fieldValues': fieldValues,
'fieldObscurityValues': fieldObscurityValues,
'hostedValues': hostedValues,
'subGroups': subGroups,
};
}
factory InitialFormGroupValue.fromMap(Map<String, dynamic> map) {
return InitialFormGroupValue(
instancesCount: map['instancesCount'] as int,
fieldValues: map['fieldValues'] != null ? Map<String, List<String?>>.from(map['fieldValues'] as Map<String, List<String?>>) : null,
fieldObscurityValues: map['fieldObscurityValues'] != null ? Map<String, List<bool>>.from(map['fieldObscurityValues'] as Map<String, List<bool>>) : null,
hostedValues: map['hostedValues'] != null ? Map<String, List<(dynamic, bool)>>.from(map['hostedValues'] as Map<String, List<(dynamic, bool)>>) : null,
subGroups: map['subGroups'] != null ? Map<String, InitialFormGroupValue>.from(map['subGroups'] as Map<String, InitialFormGroupValue>) : null,
);
}
String toJson() => json.encode(toMap());
factory InitialFormGroupValue.fromJson(String source) => InitialFormGroupValue.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() {
return 'InitialFormGroupValue(instancesCount: $instancesCount, fieldValues: $fieldValues, fieldObscurityValues: $fieldObscurityValues, hostedValues: $hostedValues, subGroups: $subGroups)';
}
@override
bool operator ==(covariant InitialFormGroupValue other) {
if (identical(this, other)) return true;
return other.instancesCount == instancesCount &&
mapEquals(other.fieldValues, fieldValues) &&
mapEquals(other.fieldObscurityValues, fieldObscurityValues) &&
mapEquals(other.hostedValues, hostedValues) &&
mapEquals(other.subGroups, subGroups);
}
@override
int get hashCode {
return instancesCount.hashCode ^ fieldValues.hashCode ^ fieldObscurityValues.hashCode ^ hostedValues.hashCode ^ subGroups.hashCode;
}
}

View File

@@ -0,0 +1,78 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'initial_form_group_values.model.dart';
class AstromicFormInitialValues {
// Field Values
final Map<String, String?>? fieldValues;
final Map<String, bool>? fieldObscurityValues;
// Hosted Values
final Map<String, (dynamic, bool)>? hostedValues;
// Form Groups
final Map<String, InitialFormGroupValue>? groupValues;
AstromicFormInitialValues({
this.fieldValues,
this.fieldObscurityValues,
this.hostedValues,
this.groupValues,
});
AstromicFormInitialValues copyWith({
Map<String, String?>? fieldValues,
Map<String, bool>? fieldObscurityValues,
Map<String, (dynamic, bool)>? hostedValues,
Map<String, InitialFormGroupValue>? groupValues,
}) {
return AstromicFormInitialValues(
fieldValues: fieldValues ?? this.fieldValues,
fieldObscurityValues: fieldObscurityValues ?? this.fieldObscurityValues,
hostedValues: hostedValues ?? this.hostedValues,
groupValues: groupValues ?? this.groupValues,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'fieldValues': fieldValues,
'fieldObscurityValues': fieldObscurityValues,
'hostedValues': hostedValues,
'groupValues': groupValues,
};
}
factory AstromicFormInitialValues.fromMap(Map<String, dynamic> map) {
return AstromicFormInitialValues(
fieldValues: map['fieldValues'] != null ? Map<String, String?>.from(map['fieldValues'] as Map<String, String?>) : null,
fieldObscurityValues: map['fieldObscurityValues'] != null ? Map<String, bool>.from(map['fieldObscurityValues'] as Map<String, bool>) : null,
hostedValues: map['hostedValues'] != null ? Map<String, (dynamic, bool)>.from(map['hostedValues'] as Map<String, dynamic>) : null,
groupValues: map['groupValues'] != null ? Map<String, InitialFormGroupValue>.from(map['groupValues'] as Map<String, InitialFormGroupValue>) : null,
);
}
String toJson() => json.encode(toMap());
factory AstromicFormInitialValues.fromJson(String source) => AstromicFormInitialValues.fromMap(json.decode(source) as Map<String, dynamic>);
@override
String toString() {
return 'AstromicFormInitialValues(fieldValues: $fieldValues, fieldObscurityValues: $fieldObscurityValues, hostedValues: $hostedValues, groupValues: $groupValues)';
}
@override
bool operator ==(covariant AstromicFormInitialValues other) {
if (identical(this, other)) return true;
return mapEquals(other.fieldValues, fieldValues) &&
mapEquals(other.fieldObscurityValues, fieldObscurityValues) &&
mapEquals(other.hostedValues, hostedValues) &&
mapEquals(other.groupValues, groupValues);
}
@override
int get hashCode {
return fieldValues.hashCode ^ fieldObscurityValues.hashCode ^ hostedValues.hashCode ^ groupValues.hashCode;
}
}