This commit is contained in:
2025-05-19 13:07:56 +03:00
6 changed files with 92 additions and 27 deletions

View File

@@ -21,10 +21,12 @@ import 'models/form_group_value.model.dart';
class AstromicFormController extends FormController { class AstromicFormController extends FormController {
AstromicFormController({ AstromicFormController({
Map<String, (String initialText, bool initialObscurity)>? initialValues, Map<String, (String initialText, bool initialObscurity)>? initialValues,
Map<String, (dynamic, bool)>? initialHostedValues,
this.errorStream, this.errorStream,
}) : super(initialValues) { }) : super(initialValues) {
// Add states and messages based on initial controller. // Add states and messages based on initial controllers.
_addInitialControllers(initialValues); _addInitialControllers(initialValues);
_addInitialHostedValues(initialHostedValues);
} }
//SECTION - Overrides //SECTION - Overrides
@@ -254,12 +256,21 @@ 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);
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 ?? <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);
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<String, (dynamic, bool)>? initialValues) {
if (initialValues != null) {
for (MapEntry<String, (dynamic, bool)> vEntry in initialValues.entries) {
setValue(vEntry.key, vEntry.value.$1, isRequired: vEntry.value.$2);
}
}
}
//!SECTION //!SECTION
} }

View File

@@ -5,25 +5,33 @@ 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,
}); });
FormGroupStructure copyWith({ FormGroupStructure copyWith({
String? id, String? id,
List<String>? fields, List<String>? fields,
Map<String, String>? preFields,
List<String>? values, List<String>? values,
Map<String, (String, dynamic)>? preValues,
List<(int initialCount, FormGroupStructure structure)>? subGroups, List<(int initialCount, FormGroupStructure structure)>? subGroups,
}) { }) {
return FormGroupStructure( return 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,
); );
} }
@@ -33,6 +41,8 @@ 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(),
}; };
} }
@@ -42,6 +52,8 @@ 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,
@@ -54,18 +66,23 @@ class FormGroupStructure {
@override @override
String toString() { 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 @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 && 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 @override
int get hashCode { 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;
} }
} }

View File

@@ -21,6 +21,7 @@ class AstromicFuturePresenter<T> extends StatefulWidget {
//SECTION - Widget Arguments //SECTION - Widget Arguments
final AstromicPresenterController controller; final AstromicPresenterController controller;
final String id; final String id;
final Map<String,dynamic>? neededArguments;
// //
final Map<AstromicPresenterState, Widget Function(PresenterReturnModel<T?>? r)> stateBuilder; final Map<AstromicPresenterState, Widget Function(PresenterReturnModel<T?>? r)> stateBuilder;
final AstromicPresenterConfiguration? configuration; final AstromicPresenterConfiguration? configuration;
@@ -32,6 +33,8 @@ class AstromicFuturePresenter<T> extends StatefulWidget {
required this.id, required this.id,
required this.stateBuilder, required this.stateBuilder,
this.configuration, this.configuration,
this.neededArguments,
}); });
@override @override
@@ -62,8 +65,8 @@ class _AstromicFuturePresenterState<T> extends State<AstromicFuturePresenter<T>>
//s1 --State //s1 --State
// //
//s1 --Controllers & Listeners //s1 --Controllers & Listeners
widget.controller.getRefreshStream(widget.id).listen((_) { widget.controller.getRefreshStream(widget.id).listen((uA) {
_refreshFuture(); // Force future recreation on refresh _refreshFuture(updatedArgs: uA); // Force future recreation on refresh
}); });
//s1 --Controllers & Listeners //s1 --Controllers & Listeners
// //
@@ -120,16 +123,17 @@ class _AstromicFuturePresenterState<T> extends State<AstromicFuturePresenter<T>>
} }
//S1 -- Method to reinitialize or update `_future` with a new instance //S1 -- Method to reinitialize or update `_future` with a new instance
void _refreshFuture() { void _refreshFuture({Map<String,dynamic>? updatedArgs}) {
// Increment the refresh key to ensure a unique future instance // Increment the refresh key to ensure a unique future instance
_refreshKey++; _refreshKey++;
setState(() { setState(() {
_initializeFuture(); _initializeFuture(updatedArgs: updatedArgs);
}); });
} }
//S1 -- Method to reinitialize or update `_future` with a new instance //S1 -- Method to reinitialize or update `_future` with a new instance
void _initializeFuture() { void _initializeFuture({Map<String,dynamic>? updatedArgs}) {
widget.controller.setProvidedArguments(updatedArgs ?? widget.neededArguments ?? <String,dynamic>{});
_future = widget.controller.getFuture(widget.id)?.then((dynamic result) => result as T?); _future = widget.controller.getFuture(widget.id)?.then((dynamic result) => result as T?);
} }
//!SECTION //!SECTION

View File

@@ -3,17 +3,17 @@ import 'dart:async';
/// A contrller used to control Futures/Streams to present them effeciantly. /// A contrller used to control Futures/Streams to present them effeciantly.
class AstromicPresenterController { class AstromicPresenterController {
late final Map<String, (Future Function()? fetch, int c)> _futures; late final Map<String, (Future Function(Map<String, dynamic> args)? fetch, int c)> _futures;
late final Map<String, Stream Function(Map<String, dynamic> args)?> _streams; late final Map<String, Stream Function(Map<String, dynamic> args)?> _streams;
late final Map<String, StreamController<void>> _futureRefreshers; late final Map<String, StreamController<Map<String,dynamic>?>> _futureRefreshers;
AstromicPresenterController({ AstromicPresenterController({
Map<String, (Future Function()? fetch, int startCycle)> futures = const {}, Map<String, (Future Function(Map<String, dynamic> args)? fetch, int startCycle)> futures = const {},
Map<String, Stream Function(Map<String, dynamic> args)?> streams = const {}, Map<String, Stream Function(Map<String, dynamic> args)?> streams = const {},
}) : _futures = futures.map((k, v) => MapEntry(k, (v.$1, v.$2))), }) : _futures = futures.map((k, v) => MapEntry(k, (v.$1, v.$2))),
_futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController<void>.broadcast())), _futureRefreshers = futures.map((k, v) => MapEntry(k, StreamController<Map<String,dynamic>?>.broadcast())),
_streams = streams; _streams = streams;
Map<String,dynamic> providedArguments = {}; Map<String,dynamic> providedArguments = {};
@@ -35,7 +35,7 @@ class AstromicPresenterController {
Future<T?>? getFuture<T>(String id) { Future<T?>? getFuture<T>(String id) {
assert(_futures.containsKey(id), 'did you add a future with this id?'); assert(_futures.containsKey(id), 'did you add a future with this id?');
return _futures[id]!.$1!() as Future<T?>?; return _futures[id]!.$1!(getProvidedArguments()) as Future<T?>?;
} }
/// Get the stream using it's ID. /// Get the stream using it's ID.
@@ -46,7 +46,7 @@ class AstromicPresenterController {
} }
/// Get the refresh stream of a future using it's ID. /// Get the refresh stream of a future using it's ID.
Stream<void> getRefreshStream<T>(String id) { Stream<Map<String,dynamic>?> getRefreshStream<T>(String id) {
assert(_futures.containsKey(id), 'did you add a future with this id?'); assert(_futures.containsKey(id), 'did you add a future with this id?');
return _futureRefreshers[id]!.stream; return _futureRefreshers[id]!.stream;
@@ -60,10 +60,10 @@ class AstromicPresenterController {
} }
/// Refresh a future using it's ID. /// Refresh a future using it's ID.
void refreshFuture(String id) { void refreshFuture(String id, {Map<String,dynamic>? updatedArgs}) {
assert(_futures.containsKey(id), 'did you add a future with this id?'); 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. /// Dispose of a future using it's ID.

View File

@@ -124,7 +124,7 @@ class AstromicSheetHelper {
radius: sheetStyle.radius, radius: sheetStyle.radius,
// //
child: ChangeNotifierProvider<SheetStore>( child: ChangeNotifierProvider<SheetStore>(
create: (BuildContext c) => SheetStore(), create: (BuildContext c) => store,
child: BaseSheetWidget<T?>( child: BaseSheetWidget<T?>(
sheetType: SheetType.form, sheetType: SheetType.form,
// //
@@ -202,7 +202,7 @@ class AstromicSheetHelper {
// s2 -- Child // s2 -- Child
builder: (BuildContext context, ScrollController scrollController, ScrollPhysics scrollPhysics, int stop) { builder: (BuildContext context, ScrollController scrollController, ScrollPhysics scrollPhysics, int stop) {
return ChangeNotifierProvider<SheetStore>( return ChangeNotifierProvider<SheetStore>(
create: (BuildContext c) => SheetStore(), create: (BuildContext c) => store,
child: BaseSheetWidget<T?>( child: BaseSheetWidget<T?>(
sheetType: SheetType.scroller, sheetType: SheetType.scroller,
sheetConfiguration: sheetConfigs, sheetConfiguration: sheetConfigs,

View File

@@ -1,3 +1,6 @@
// ignore_for_file: close_sinks
import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@@ -9,6 +12,24 @@ class SheetStore extends ChangeNotifier {
/// An unmodifiable view of the items in the store. /// An unmodifiable view of the items in the store.
UnmodifiableMapView<String, dynamic> get items => UnmodifiableMapView<String, dynamic>(_items); UnmodifiableMapView<String, dynamic> get items => UnmodifiableMapView<String, dynamic>(_items);
static final StreamController<UnmodifiableMapView<String, dynamic>> _stateStreamController = StreamController<UnmodifiableMapView<String, dynamic>>.broadcast();
final Stream<UnmodifiableMapView<String, dynamic>> 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. /// Adds [item] to store.
void add(String itemID, dynamic value) { void add(String itemID, dynamic value) {
_items.addEntries(<MapEntry<String, dynamic>>[MapEntry<String, dynamic>(itemID, value)]); _items.addEntries(<MapEntry<String, dynamic>>[MapEntry<String, dynamic>(itemID, value)]);
@@ -17,21 +38,25 @@ class SheetStore extends ChangeNotifier {
} }
/// Updates [item] in the store. /// Updates [item] in the store.
void update(String itemID, dynamic value) { void update(String itemID, dynamic value, {bool addIfAbsent = false}) {
if (!_items.containsKey(itemID)) { if (!_items.containsKey(itemID)) {
if (addIfAbsent) {
add(itemID, value);
} else {
throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?'); 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. // This call tells the widgets that are listening to this store to rebuild.
notifyListeners(); notifyListeners();
} }
/// Removes [item] from the store. /// Removes [item] from the store.
void remove(String itemID, dynamic value) { void remove(String itemID, dynamic value) {
if (!_items.containsKey(itemID)) { if (_items.containsKey(itemID)) {
throw Exception('No item with the ID $itemID exist in the SheetStore, Are you sure you added it?');
}
_items.remove(itemID); _items.remove(itemID);
}
// This call tells the widgets that are listening to this store to rebuild. // This call tells the widgets that are listening to this store to rebuild.
notifyListeners(); notifyListeners();
} }