diff --git a/lib/src/form/form_helper.astromic.dart b/lib/src/form/form_helper.astromic.dart index f18108b..1e21931 100644 --- a/lib/src/form/form_helper.astromic.dart +++ b/lib/src/form/form_helper.astromic.dart @@ -1,4 +1,5 @@ export 'package:form_controller/form_controller.dart'; export 'src/controller.dart'; export 'src/form_field.dart'; +export 'src/form_value_wrapper.dart'; export 'src/enums/enums.exports.dart'; diff --git a/lib/src/form/src/controller.dart b/lib/src/form/src/controller.dart index 463af10..5472533 100644 --- a/lib/src/form/src/controller.dart +++ b/lib/src/form/src/controller.dart @@ -1,5 +1,7 @@ //s1 Imports //s2 Core Package Imports +// ignore_for_file: close_sinks + import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter/scheduler.dart'; @@ -18,13 +20,17 @@ class AstromicFormController extends FormController { // State Variables final Map fieldStates = {}; final Map fieldMessages = {}; - final Map _hostedValues = {}; + final Map _hostedValues = {}; // final Map>? streamedErrorMaps; // fieldId: {errorCode: errorMessage} - final Stream>? errorStream; + final Stream>? errorStream; // State Stream Variables static final StreamController<(String, AstromicFieldState)> _stateStreamController = StreamController<(String id, AstromicFieldState)>.broadcast(); final Stream<(String id, AstromicFieldState)> stateStream = _stateStreamController.stream; + // Hosted Value Validation Stream Variables + static final StreamController<(String, bool)> _hostedValueValidationStreamController = StreamController<(String id, bool)>.broadcast(); + final Stream<(String id, bool isValidationErrored)> hostedValueValidationStream = _hostedValueValidationStreamController.stream; + AstromicFormController({ Map? extraControllers, // this.streamedErrorMaps, @@ -35,7 +41,7 @@ class AstromicFormController extends FormController { // Listen on the error stream for values and push to the corresponding field state. // if (errorStream != null) { - // _handleErrorStream(); + // _handleErrorStream(); // } } @@ -63,20 +69,32 @@ class AstromicFormController extends FormController { setState(fieldId, AstromicFieldState.idle); } - /// Set the value of a hosted state variable using it's ID. - void setValue(String id, T data) { - if (_hostedValues.keys.toList().contains(id)) { - _hostedValues[id] = data; - } else { - _hostedValues.addEntries(>[MapEntry(id, data)]); + bool validateValues(List valueIDs) { + for (String hostedValueID in valueIDs) { + if (_hostedValues.containsKey(hostedValueID) && _hostedValues[hostedValueID]!.$2 && _hostedValues[hostedValueID]!.$1 == null) { + // Validation Error! + _hostedValueValidationStreamController.add((hostedValueID, true)); + return false; + } } + return true; + } + + /// Set the value of a hosted state variable using it's ID. + void setValue(String id, T data, {bool isRequired = false}) { + if (_hostedValues.keys.toList().contains(id)) { + _hostedValues[id] = (data, isRequired); + } else { + _hostedValues.addEntries(>[MapEntry(id, (data, isRequired))]); + } + _hostedValueValidationStreamController.add((id, false)); } /// Get the value of a hosted state variable using it's ID. T? getValue(String id) { if (_hostedValues.keys.toList().contains(id)) { - if (_hostedValues[id] is T) { - return _hostedValues[id]; + if (_hostedValues[id] is (T?, bool)) { + return _hostedValues[id]?.$1; } else { throw FlutterError('Value found but is not of the type $T'); } @@ -123,24 +141,5 @@ class AstromicFormController extends FormController { )) ?? >{}); } - - // _handleErrorStream() { - // errorStream!.distinct().listen((List errorCodes) { - // if (streamedErrorMaps != null && streamedErrorMaps!.isNotEmpty) { - // for (String errorMapId in streamedErrorMaps!.keys.toList()) { - // if (super.controllers != null && super.controllers!.containsKey(errorMapId)) { - // if (streamedErrorMaps![errorMapId] != null && - // streamedErrorMaps![errorMapId]!.isNotEmpty && - // streamedErrorMaps![errorMapId]!.keys.toList().where((String k) => errorCodes.contains(k)).toList().isNotEmpty) { - // for (String eC in streamedErrorMaps![errorMapId]!.keys.toList().where((String k) => errorCodes.contains(k)).toList()) { - // String? m = streamedErrorMaps![errorMapId]![eC]; - // setState(errorMapId, AstromicFieldState.withError, message: m ?? 'Error Message was not set!'); - // } - // } - // } - // } - // } - // }); - // } //!SECTION } diff --git a/lib/src/form/src/form_value_wrapper.dart b/lib/src/form/src/form_value_wrapper.dart new file mode 100644 index 0000000..7ba9c85 --- /dev/null +++ b/lib/src/form/src/form_value_wrapper.dart @@ -0,0 +1,123 @@ +//s1 Imports +//s2 Packages +//s3 Core Packages +import 'package:flutter/material.dart'; + +import '../../sheet/sheet_helper.astromic.dart'; +//s3 Internal Packages +//s3 3rd-party Packages +//s2 Utility +//s3 Configs +//s3 Misc +//s2 Domain +//s3 Entities +//s3 Usecases +//s2 Presentation +//s3 Design +//s3 Presenters +//s3 Widgets +//s1 Exports + +class FormValueWrapper extends StatefulWidget { + //SECTION - Widget Arguments + final AstromicFormController controller; + final String id; + final Widget Function(T? value, bool isErroredForValidation, void Function(T value, bool isRequired) valueSetter) builder; + //!SECTION + // + const FormValueWrapper({ + super.key, + required this.controller, + required this.id, + required this.builder, + }); + + @override + State> createState() => _FormValueWrapperState(); +} + +class _FormValueWrapperState extends State> { + // + //SECTION - State Variables + //s1 --State + //s1 --State + // + //s1 --Controllers + // late AstromicFormController _formController; + //s1 --Controllers + // + //s1 --Constants + //s1 --Constants + //!SECTION + + @override + void initState() { + super.initState(); + // + //SECTION - State Variables initializations & Listeners + //s1 --State + //s1 --State + // + //s1 --Controllers & Listeners + // _formController = widget.controller; + //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 StreamBuilder<(String, bool)>( + stream: widget.controller.hostedValueValidationStream, + builder: (BuildContext context, AsyncSnapshot<(String, bool)> validationSnapshot) { + return widget.builder(widget.controller.getValue(widget.id), + validationSnapshot.hasData && validationSnapshot.data != null && validationSnapshot.data!.$1 == widget.id && validationSnapshot.data!.$2 ? true : false, (T newValue, bool isRequired) { + return widget.controller.setValue(widget.id, (newValue, isRequired)); + }); + }); + //!SECTION + } + + @override + void dispose() { + //SECTION - Disposable variables + //!SECTION + super.dispose(); + } +}