@@ -77,7 +77,7 @@ class FastFormState extends State<FastForm> {
7777 void onChanged () {
7878 widget.onChanged? .call (values);
7979 for (final field in _fields.values) {
80- field.condition ();
80+ field.testConditions ();
8181 }
8282 }
8383
@@ -115,30 +115,76 @@ class _FastFormScope extends InheritedWidget {
115115 bool updateShouldNotify (InheritedWidget oldWidget) => true ;
116116}
117117
118+ /// A [Function] that tests whether a single [FastCondition] is met.
119+ typedef FastConditionTest = bool Function (
120+ dynamic value, FastFormFieldState field);
121+
122+ /// A [Function] that defines a conditional state of a [FastFormField] .
123+ ///
124+ /// Implements, what happens when a condition is met or not.
125+ ///
126+ /// Typically linked to [FastConditionList] .
127+ ///
128+ /// Called at the end of every [FastFormFieldState.testConditions] run.
118129typedef FastConditionHandler = void Function (
119130 bool isMet, FastFormFieldState field);
120131
132+ /// A single condition to be met for a conditional state to occur.
121133@immutable
122134class FastCondition {
123135 const FastCondition ({
124- required this .fieldName,
125- this .required = false ,
126- required this .condition,
136+ required this .target,
137+ required this .test,
127138 });
128139
129- final String fieldName;
130- final bool required ;
131- final bool Function (dynamic value, FastFormFieldState field) condition;
140+ /// The name of the [FastFormField] this [test] should be called upon.
141+ final String target;
142+
143+ final FastConditionTest test;
132144
145+ /// A [FastConditionHandler] that disables a [FastFormField] based
146+ /// on whether a [FastCondition] is met.
133147 static void disabled (bool isMet, FastFormFieldState field) {
134148 field.enabled = ! isMet;
135149 }
136150
151+ /// A [FastConditionHandler] that enables a [FastFormField] based
152+ /// on whether a [FastCondition] is met.
137153 static void enabled (bool isMet, FastFormFieldState field) {
138154 field.enabled = isMet;
139155 }
140156}
141157
158+ /// An enum to define a match logic for a [List] of multiple [FastCondition]
159+ /// elements.
160+ ///
161+ /// Typically specifies how all individual [FastCondition.test] results in a
162+ /// [FastConditionList] are evaluated to determine whether a condition in
163+ /// [FastFormField.conditions] is met.
164+ enum FastConditionMatch {
165+ /// Every [FastCondition.test] in a [FastConditionList] must be true to met
166+ /// the condition.
167+ every,
168+
169+ /// At least one [FastCondition.test] in a [FastConditionList] must be true to
170+ /// met the condition.
171+ any,
172+ }
173+
174+ /// A wrapper class for a [List] of [FastCondition] elements.
175+ @immutable
176+ class FastConditionList {
177+ const FastConditionList (
178+ this .conditions, {
179+ this .match = FastConditionMatch .any,
180+ });
181+
182+ final List <FastCondition > conditions;
183+
184+ /// Defaults to [FastConditionMatch.any] .
185+ final FastConditionMatch match;
186+ }
187+
142188/// A single fast form field.
143189///
144190/// Works identical to built-in [FormField] .
@@ -169,7 +215,7 @@ abstract class FastFormField<T> extends FormField<T> {
169215
170216 /// null represents a non-adaptive form field widget
171217 final bool ? adaptive;
172- final Map <FastConditionHandler , List < FastCondition > >? conditions;
218+ final Map <FastConditionHandler , FastConditionList >? conditions;
173219 final EdgeInsetsGeometry ? contentPadding;
174220 final InputDecoration ? decoration;
175221 final String ? helperText;
@@ -304,39 +350,40 @@ abstract class FastFormFieldState<T> extends FormFieldState<T> {
304350 });
305351 }
306352
307- void condition () {
353+ /// Determines for every [MapEntry] in [FastFormField.conditions] if the
354+ /// respective condition is met.
355+ ///
356+ /// Finally calls the corresponding [FastConditionHandler] , passing the
357+ /// result of the condition matching.
358+ void testConditions () {
308359 final FastFormField <T >(: conditions) = widget;
309360 if (conditions == null || conditions.isEmpty) return ;
310361
311- final Map <FastConditionHandler , bool > map = {};
312-
313- for (final MapEntry (key: handler, : value) in conditions.entries) {
314- map[handler] = false ;
362+ for (final MapEntry (key: handler, value: list) in conditions.entries) {
363+ final FastConditionList (: conditions, match: logic) = list;
364+ final bool isMet;
315365
316- for (final FastCondition (: condition, : fieldName, : required ) in value) {
317- final field = form? .getFieldByName (fieldName);
318- if (field == null ) continue ;
366+ test (condition) => _testCondition (condition);
319367
320- final isMet = condition (field.value, field);
321-
322- if (isMet && required ) {
323- map.update (handler, (value) => true );
324- } else if (isMet && ! required ) {
325- map.update (handler, (value) => true );
326- } else if (! isMet && required ) {
327- map.update (handler, (value) => false );
328- break ;
329- } else if (! isMet && ! required ) {
330- map.update (handler, (value) => value || false );
331- }
368+ switch (logic) {
369+ case FastConditionMatch .any:
370+ isMet = conditions.any (test);
371+ case FastConditionMatch .every:
372+ isMet = conditions.every (test);
332373 }
333- }
334374
335- for (final MapEntry (key: handler, value: isMet) in map.entries) {
336375 handler (isMet, this );
337376 }
338377 }
339378
379+ bool _testCondition (FastCondition condition) {
380+ final FastCondition (: target, : test) = condition;
381+ final field = form? .getFieldByName (target);
382+ if (field == null ) throw ArgumentError ('Target $target is null.' );
383+
384+ return test (field.value, field);
385+ }
386+
340387 void _onFocusChanged () {
341388 setState (() {
342389 if (focusNode.hasFocus) {
0 commit comments