From 97313b18573378cee18a5156c8a0917f15ed5798 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:07:29 +0300 Subject: [PATCH 1/9] [SYNC] --- lib/src/sheet/src/models/sheet_store.model.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index a375d2e..5a21bdb 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -17,11 +17,16 @@ class SheetStore extends ChangeNotifier { } /// Updates [item] in the store. - void update(String itemID, dynamic value) { + void update(String itemID, dynamic value, {bool addIfAbsent = false}) { if (!_items.containsKey(itemID)) { - throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?'); + if (addIfAbsent) { + add(itemID, value); + } else { + throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?'); + } + } else { + _items[itemID] = value; } - _items[itemID] = value; // This call tells the widgets that are listening to this store to rebuild. notifyListeners(); } From 1dae29f031464449bd9aad9cb64b3fea74c7f670 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:22:57 +0300 Subject: [PATCH 2/9] [SYNC] --- lib/src/sheet/sheet_helper.astromic.dart | 25 +++++++++++-------- .../sheet/src/models/sheet_store.model.dart | 5 ++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/src/sheet/sheet_helper.astromic.dart b/lib/src/sheet/sheet_helper.astromic.dart index 9a274b3..0ef5977 100644 --- a/lib/src/sheet/sheet_helper.astromic.dart +++ b/lib/src/sheet/sheet_helper.astromic.dart @@ -124,16 +124,19 @@ class AstromicSheetHelper { radius: sheetStyle.radius, // child: ChangeNotifierProvider( - create: (BuildContext c) => SheetStore(), - child: BaseSheetWidget( - sheetType: SheetType.form, - // - sheetConfiguration: sheetConfigs, - sheetStyle: sheetStyle, - // - headSectionFormBuilder: headSectionBuilder == null ? null : (AstromicFormController controller) => headSectionBuilder.call(controller, store), - contentSectionFormBuilder: (AstromicFormController controller) => contentSectionBuilder(controller, store), - footerSectionFormBuilder: footerSectionBuilder == null ? null : (AstromicFormController controller) => footerSectionBuilder.call(controller, store), + create: (BuildContext c) => store, + child: ListenableBuilder( + listenable: store, + builder: (_, __) => BaseSheetWidget( + sheetType: SheetType.form, + // + sheetConfiguration: sheetConfigs, + sheetStyle: sheetStyle, + // + headSectionFormBuilder: headSectionBuilder == null ? null : (AstromicFormController controller) => headSectionBuilder.call(controller, store), + contentSectionFormBuilder: (AstromicFormController controller) => contentSectionBuilder(controller, store), + footerSectionFormBuilder: footerSectionBuilder == null ? null : (AstromicFormController controller) => footerSectionBuilder.call(controller, store), + ), ), ), ); @@ -208,7 +211,7 @@ class AstromicSheetHelper { sheetConfiguration: sheetConfigs, sheetStyle: sheetStyle, headSectionScrollerBuilder: headSectionBuilder == null ? null : (ScrollController controller) => headSectionBuilder.call(controller, store), - contentSectionScrollBuilder: (ScrollController controller, ScrollPhysics physics) => contentSectionBuilder(controller,physics, store), + contentSectionScrollBuilder: (ScrollController controller, ScrollPhysics physics) => contentSectionBuilder(controller, physics, store), footerSectionScrollerBuilder: footerSectionBuilder == null ? null : (ScrollController controller) => footerSectionBuilder.call(controller, store), scrollController: scrollController, scrollPhysics: scrollPhysics, diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index 5a21bdb..e671b76 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -1,3 +1,6 @@ +// ignore_for_file: close_sinks + +import 'dart:async'; import 'dart:collection'; import 'package:flutter/widgets.dart'; @@ -9,6 +12,8 @@ class SheetStore extends ChangeNotifier { /// An unmodifiable view of the items in the store. UnmodifiableMapView get items => UnmodifiableMapView(_items); + static final StreamController> _stateStreamController = StreamController>.broadcast(); + final Stream> stateStream = _stateStreamController.stream; /// Adds [item] to store. void add(String itemID, dynamic value) { _items.addEntries(>[MapEntry(itemID, value)]); From cb87475c43c4d27bd2914f2405025d4c982ab458 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:28:07 +0300 Subject: [PATCH 3/9] [SYNC] --- lib/src/sheet/sheet_helper.astromic.dart | 25 ++++++++----------- .../sheet/src/models/sheet_store.model.dart | 6 +++++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/src/sheet/sheet_helper.astromic.dart b/lib/src/sheet/sheet_helper.astromic.dart index 0ef5977..d5a1a28 100644 --- a/lib/src/sheet/sheet_helper.astromic.dart +++ b/lib/src/sheet/sheet_helper.astromic.dart @@ -125,18 +125,15 @@ class AstromicSheetHelper { // child: ChangeNotifierProvider( create: (BuildContext c) => store, - child: ListenableBuilder( - listenable: store, - builder: (_, __) => BaseSheetWidget( - sheetType: SheetType.form, - // - sheetConfiguration: sheetConfigs, - sheetStyle: sheetStyle, - // - headSectionFormBuilder: headSectionBuilder == null ? null : (AstromicFormController controller) => headSectionBuilder.call(controller, store), - contentSectionFormBuilder: (AstromicFormController controller) => contentSectionBuilder(controller, store), - footerSectionFormBuilder: footerSectionBuilder == null ? null : (AstromicFormController controller) => footerSectionBuilder.call(controller, store), - ), + child: BaseSheetWidget( + sheetType: SheetType.form, + // + sheetConfiguration: sheetConfigs, + sheetStyle: sheetStyle, + // + headSectionFormBuilder: headSectionBuilder == null ? null : (AstromicFormController controller) => headSectionBuilder.call(controller, store), + contentSectionFormBuilder: (AstromicFormController controller) => contentSectionBuilder(controller, store), + footerSectionFormBuilder: footerSectionBuilder == null ? null : (AstromicFormController controller) => footerSectionBuilder.call(controller, store), ), ), ); @@ -205,13 +202,13 @@ class AstromicSheetHelper { // s2 -- Child builder: (BuildContext context, ScrollController scrollController, ScrollPhysics scrollPhysics, int stop) { return ChangeNotifierProvider( - create: (BuildContext c) => SheetStore(), + create: (BuildContext c) => store, child: BaseSheetWidget( sheetType: SheetType.scroller, sheetConfiguration: sheetConfigs, sheetStyle: sheetStyle, headSectionScrollerBuilder: headSectionBuilder == null ? null : (ScrollController controller) => headSectionBuilder.call(controller, store), - contentSectionScrollBuilder: (ScrollController controller, ScrollPhysics physics) => contentSectionBuilder(controller, physics, store), + contentSectionScrollBuilder: (ScrollController controller, ScrollPhysics physics) => contentSectionBuilder(controller,physics, store), footerSectionScrollerBuilder: footerSectionBuilder == null ? null : (ScrollController controller) => footerSectionBuilder.call(controller, store), scrollController: scrollController, scrollPhysics: scrollPhysics, diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index e671b76..96fc162 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -14,6 +14,7 @@ class SheetStore extends ChangeNotifier { static final StreamController> _stateStreamController = StreamController>.broadcast(); final Stream> stateStream = _stateStreamController.stream; + /// Adds [item] to store. void add(String itemID, dynamic value) { _items.addEntries(>[MapEntry(itemID, value)]); @@ -45,4 +46,9 @@ class SheetStore extends ChangeNotifier { // This call tells the widgets that are listening to this store to rebuild. notifyListeners(); } + + /// Return a stream builder for real-time value updates. + Widget builder(Widget child) { + return ListenableBuilder(listenable: this, builder: (_,__) => child); + } } From d9cf149d26477cda65a9b3d795f58378dfa14c3b Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:29:42 +0300 Subject: [PATCH 4/9] [SYNC] --- lib/src/sheet/src/models/sheet_store.model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index 96fc162..95e7a62 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -48,7 +48,7 @@ class SheetStore extends ChangeNotifier { } /// Return a stream builder for real-time value updates. - Widget builder(Widget child) { + Widget builder({required Widget child}) { return ListenableBuilder(listenable: this, builder: (_,__) => child); } } From f9627998e7d95704b767fbae5d1188251cdc5507 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:32:30 +0300 Subject: [PATCH 5/9] [SYNC] --- .../sheet/src/models/sheet_store.model.dart | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index 95e7a62..4516f53 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -15,6 +15,21 @@ class SheetStore extends ChangeNotifier { static final StreamController> _stateStreamController = StreamController>.broadcast(); final Stream> stateStream = _stateStreamController.stream; + /// Adds [item] to store. + dynamic get(String itemID) { + if (!_items.containsKey(itemID)) { + throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?'); + } + return _items[itemID]; + } + + dynamic tryGet(String itemID) { + if (!_items.containsKey(itemID)) { + return null; + } + return _items[itemID]; + } + /// Adds [item] to store. void add(String itemID, dynamic value) { _items.addEntries(>[MapEntry(itemID, value)]); @@ -39,16 +54,15 @@ class SheetStore extends ChangeNotifier { /// Removes [item] from the store. void remove(String itemID, dynamic value) { - if (!_items.containsKey(itemID)) { - throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?'); + if (_items.containsKey(itemID)) { + _items.remove(itemID); } - _items.remove(itemID); // This call tells the widgets that are listening to this store to rebuild. notifyListeners(); } /// Return a stream builder for real-time value updates. Widget builder({required Widget child}) { - return ListenableBuilder(listenable: this, builder: (_,__) => child); + return ListenableBuilder(listenable: this, builder: (_, __) => child); } } From 04481e9a345bb1a9c635d8057b424afe0ffd4b41 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 27 Apr 2025 14:36:03 +0300 Subject: [PATCH 6/9] [SYNC] --- lib/src/sheet/src/models/sheet_store.model.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/src/sheet/src/models/sheet_store.model.dart b/lib/src/sheet/src/models/sheet_store.model.dart index 4516f53..84cc2e7 100644 --- a/lib/src/sheet/src/models/sheet_store.model.dart +++ b/lib/src/sheet/src/models/sheet_store.model.dart @@ -60,9 +60,4 @@ class SheetStore extends ChangeNotifier { // This call tells the widgets that are listening to this store to rebuild. notifyListeners(); } - - /// Return a stream builder for real-time value updates. - Widget builder({required Widget child}) { - return ListenableBuilder(listenable: this, builder: (_, __) => child); - } } From 087414c03a29bb3c1232f642456e26dee6749623 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 11 May 2025 12:06:35 -0400 Subject: [PATCH 7/9] [SYNC] --- lib/src/presenting/src/widgets/future_presenter.widget.dart | 4 ++++ .../presenting/src/widgets/presenter_controller.widget.dart | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/src/presenting/src/widgets/future_presenter.widget.dart b/lib/src/presenting/src/widgets/future_presenter.widget.dart index e0ddec8..2efda6a 100644 --- a/lib/src/presenting/src/widgets/future_presenter.widget.dart +++ b/lib/src/presenting/src/widgets/future_presenter.widget.dart @@ -19,6 +19,7 @@ class AstromicFuturePresenter extends StatefulWidget { //SECTION - Widget Arguments final AstromicPresenterController controller; final String id; + final Map? neededArguments; // final Map? r)> stateBuilder; final AstromicPresenterConfiguration? configuration; @@ -30,6 +31,8 @@ class AstromicFuturePresenter extends StatefulWidget { required this.id, required this.stateBuilder, this.configuration, + this.neededArguments, + }); @override @@ -128,6 +131,7 @@ class _AstromicFuturePresenterState extends State> //S1 -- Method to reinitialize or update `_future` with a new instance void _initializeFuture() { + widget.controller.setProvidedArguments(widget.neededArguments ?? {}); _future = widget.controller.getFuture(widget.id)?.then((dynamic result) => result as T?); } //!SECTION diff --git a/lib/src/presenting/src/widgets/presenter_controller.widget.dart b/lib/src/presenting/src/widgets/presenter_controller.widget.dart index 11330ea..5acd497 100644 --- a/lib/src/presenting/src/widgets/presenter_controller.widget.dart +++ b/lib/src/presenting/src/widgets/presenter_controller.widget.dart @@ -3,14 +3,14 @@ import 'dart:async'; /// A contrller used to control Futures/Streams to present them effeciantly. class AstromicPresenterController { - late final Map _futures; + late final Map args)? fetch, int c)> _futures; late final Map args)?> _streams; late final Map> _futureRefreshers; AstromicPresenterController({ - Map futures = const {}, + Map args)? fetch, int startCycle)> futures = const {}, Map args)?> streams = const {}, }) : _futures = futures.map((k, v) => MapEntry(k, (v.$1, v.$2))), _futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController.broadcast())), @@ -35,7 +35,7 @@ class AstromicPresenterController { Future? getFuture(String id) { assert(_futures.containsKey(id), 'did you add a future with this id?'); - return _futures[id]!.$1!() as Future?; + return _futures[id]!.$1!(getProvidedArguments()) as Future?; } /// Get the stream using it's ID. From b56cd35042868c8e5187f87a6ecc23ab9ff12378 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sun, 11 May 2025 12:24:25 -0400 Subject: [PATCH 8/9] [SYNC] --- .../src/widgets/future_presenter.widget.dart | 12 ++++++------ .../src/widgets/presenter_controller.widget.dart | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/presenting/src/widgets/future_presenter.widget.dart b/lib/src/presenting/src/widgets/future_presenter.widget.dart index 2efda6a..baebd52 100644 --- a/lib/src/presenting/src/widgets/future_presenter.widget.dart +++ b/lib/src/presenting/src/widgets/future_presenter.widget.dart @@ -63,8 +63,8 @@ class _AstromicFuturePresenterState extends State> //s1 --State // //s1 --Controllers & Listeners - widget.controller.getRefreshStream(widget.id).listen((_) { - _refreshFuture(); // Force future recreation on refresh + widget.controller.getRefreshStream(widget.id).listen((uA) { + _refreshFuture(updatedArgs: uA); // Force future recreation on refresh }); //s1 --Controllers & Listeners // @@ -121,17 +121,17 @@ class _AstromicFuturePresenterState extends State> } //S1 -- Method to reinitialize or update `_future` with a new instance - void _refreshFuture() { + void _refreshFuture({Map? updatedArgs}) { // Increment the refresh key to ensure a unique future instance _refreshKey++; setState(() { - _initializeFuture(); + _initializeFuture(updatedArgs: updatedArgs); }); } //S1 -- Method to reinitialize or update `_future` with a new instance - void _initializeFuture() { - widget.controller.setProvidedArguments(widget.neededArguments ?? {}); + void _initializeFuture({Map? updatedArgs}) { + widget.controller.setProvidedArguments(updatedArgs ?? widget.neededArguments ?? {}); _future = widget.controller.getFuture(widget.id)?.then((dynamic result) => result as T?); } //!SECTION diff --git a/lib/src/presenting/src/widgets/presenter_controller.widget.dart b/lib/src/presenting/src/widgets/presenter_controller.widget.dart index 5acd497..d78c74f 100644 --- a/lib/src/presenting/src/widgets/presenter_controller.widget.dart +++ b/lib/src/presenting/src/widgets/presenter_controller.widget.dart @@ -5,7 +5,7 @@ import 'dart:async'; class AstromicPresenterController { late final Map args)? fetch, int c)> _futures; late final Map args)?> _streams; - late final Map> _futureRefreshers; + late final Map?>> _futureRefreshers; @@ -13,7 +13,7 @@ class AstromicPresenterController { Map args)? fetch, int startCycle)> futures = const {}, Map args)?> streams = const {}, }) : _futures = futures.map((k, v) => MapEntry(k, (v.$1, v.$2))), - _futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController.broadcast())), + _futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController?>.broadcast())), _streams = streams; Map providedArguments = {}; @@ -46,7 +46,7 @@ class AstromicPresenterController { } /// Get the refresh stream of a future using it's ID. - Stream getRefreshStream(String id) { + Stream?> getRefreshStream(String id) { assert(_futures.containsKey(id), 'did you add a future with this id?'); return _futureRefreshers[id]!.stream; @@ -60,10 +60,10 @@ class AstromicPresenterController { } /// Refresh a future using it's ID. - void refreshFuture(String id) { + void refreshFuture(String id, {Map? updatedArgs}) { assert(_futures.containsKey(id), 'did you add a future with this id?'); - _futureRefreshers[id]!.add(null); + _futureRefreshers[id]!.add(updatedArgs); } /// Dispose of a future using it's ID. From 67dd688db74f40c56c061361e7f056459c7eaa00 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Fri, 16 May 2025 11:02:15 -0400 Subject: [PATCH 9/9] [SYNC] --- lib/src/form/src/controller.dart | 25 ++++++++++++++++--- .../models/form_group_structure.model.dart | 23 ++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/src/form/src/controller.dart b/lib/src/form/src/controller.dart index 65d4893..9e44af9 100644 --- a/lib/src/form/src/controller.dart +++ b/lib/src/form/src/controller.dart @@ -21,10 +21,12 @@ import 'models/form_group_value.model.dart'; class AstromicFormController extends FormController { AstromicFormController({ Map? initialValues, + Map? initialHostedValues, this.errorStream, }) : super(initialValues) { - // Add states and messages based on initial controller. + // Add states and messages based on initial controllers. _addInitialControllers(initialValues); + _addInitialHostedValues(initialHostedValues); } //SECTION - Overrides @@ -254,12 +256,21 @@ class AstromicFormController extends FormController { for (final String fieldID in structure.fields) { final String fullID = standeredGroupFormat(baseID, index.toString(), fieldID); - controller(fullID); + String? preFilled; + if (structure.preFields?.containsKey(fieldID) ?? false) { + preFilled = structure.preFields![fieldID]; + } + controller(fullID, initialText: preFilled); } for (final String valueID in structure.values ?? []) { final String fullID = standeredGroupFormat(baseID, index.toString(), valueID); - controller(fullID); + (String, dynamic)? preFilled; + if (structure.preValues?.containsKey(valueID) ?? false) { + preFilled = structure.preValues![valueID]; + } + controller(fullID, initialText: preFilled?.$1); + setValue(valueID, preFilled?.$2); } } @@ -548,5 +559,13 @@ class AstromicFormController extends FormController { ))); } } + + void _addInitialHostedValues(Map? initialValues) { + if (initialValues != null) { + for (MapEntry vEntry in initialValues.entries) { + setValue(vEntry.key, vEntry.value.$1, isRequired: vEntry.value.$2); + } + } + } //!SECTION } diff --git a/lib/src/form/src/models/form_group_structure.model.dart b/lib/src/form/src/models/form_group_structure.model.dart index 2b825c4..22b6604 100644 --- a/lib/src/form/src/models/form_group_structure.model.dart +++ b/lib/src/form/src/models/form_group_structure.model.dart @@ -5,25 +5,33 @@ import 'package:flutter/foundation.dart'; class FormGroupStructure { final String id; final List fields; + final Map? preFields; final List? values; + final Map? preValues; final List<(int initialCount, FormGroupStructure structure)>? subGroups; FormGroupStructure({ required this.id, required this.fields, this.values, + this.preFields, + this.preValues, this.subGroups, }); FormGroupStructure copyWith({ String? id, List? fields, + Map? preFields, List? values, + Map? preValues, List<(int initialCount, FormGroupStructure structure)>? subGroups, }) { return FormGroupStructure( id: id ?? this.id, fields: fields ?? this.fields, values: values ?? this.values, + preFields: preFields ?? this.preFields, + preValues: preValues ?? this.preValues, subGroups: subGroups ?? this.subGroups, ); } @@ -33,6 +41,8 @@ class FormGroupStructure { 'id': id, 'fields': fields, 'values': values, + 'preFields': preFields, + 'preValues': preValues, 'subGroups': subGroups?.map(((int initialCount, FormGroupStructure structure) x) => {'structure': x.$2.toMap(), 'initialCount': x.$1}).toList(), }; } @@ -42,6 +52,8 @@ class FormGroupStructure { id: map['id'] as String, fields: List.from(map['fields'] as List), values: map['values'] != null ? List.from(map['values'] as List) : null, + preFields: map['preFields'] != null ? map['preFields'] as Map : null, + preValues: map['preValues'] != null ? map['preValues'] as Map: null, subGroups: map['subGroups'] != null ? (map['subGroups'] as List>).map((Map map) => (int.tryParse(map['initialCount']) ?? 0, FormGroupStructure.fromMap(map['structure']))).toList() : null, @@ -54,18 +66,23 @@ class FormGroupStructure { @override String toString() { - return 'FormGroupStructure(id: $id, fields: $fields, values: $values, subGroups: $subGroups)'; + return 'FormGroupStructure(id: $id, fields: $fields, values: $values, preFields: $preFields, preValues: $preValues, subGroups: $subGroups)'; } @override bool operator ==(covariant FormGroupStructure other) { if (identical(this, other)) return true; - return other.id == id && listEquals(other.fields, fields) && listEquals(other.values, values) && listEquals(other.subGroups, subGroups); + return other.id == id && + listEquals(other.fields, fields) && + listEquals(other.values, values) && + mapEquals(other.preFields, preFields) && + mapEquals(other.preValues, preValues) && + listEquals(other.subGroups, subGroups); } @override int get hashCode { - return id.hashCode ^ fields.hashCode ^ values.hashCode ^ subGroups.hashCode; + return id.hashCode ^ fields.hashCode ^ values.hashCode ^ preFields.hashCode ^ preValues.hashCode ^ subGroups.hashCode; } }