From 03dbdfe15f6b98c42f80f85d3fb3a2b3f46f8647 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 27 Sep 2023 11:44:28 +0200 Subject: [PATCH 01/11] introduced `validateGranually` to `FormState` --- packages/flutter/lib/src/widgets/form.dart | 49 +++++++- packages/flutter/test/widgets/form_test.dart | 118 +++++++++++++++++++ 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index ca980ad93304..2961b3a26863 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -293,19 +293,46 @@ class FormState extends State
{ /// returns true if there are no errors. /// /// The form will rebuild to report the results. + /// See also: [validateGranually] bool validate() { _hasInteractedByUser = true; _forceRebuild(); - return _validate(); + return _validate().isValid; } - bool _validate() { + + /// Validates every [FormField]s that is a descendant of this [Form], and + /// returns a [Map] which keys' are [Key]s of the descendant [FormField]s + /// and values' are their corresponding validation results. + /// + /// Note that any [FormField] that has not been asigned a key will be ignored in + /// the result. To get the absolute validation value of the Form, Consider using + /// [validate] + /// + /// Common usage of this method is when you need draw the user's attention + /// to the invalid field(s) using their widget key(s). + /// + /// The form will rebuild to report the results. + Map validateGranually() { + _hasInteractedByUser = true; + _forceRebuild(); + return _validate().fieldsValidationStatus; + } + + _FormValidationStatus _validate() { + final Map fieldsValidationStatus = {}; bool hasError = false; String errorMessage = ''; for (final FormFieldState field in _fields) { hasError = !field.validate() || hasError; errorMessage += field.errorText ?? ''; + final Key? key = field.widget.key; + if (key != null) { + fieldsValidationStatus[key] = field.validate(); + } } + final _FormValidationStatus formValidationStatus = _FormValidationStatus( + fieldsValidationStatus: fieldsValidationStatus, isValid: !hasError); if (errorMessage.isNotEmpty) { final TextDirection directionality = Directionality.of(context); @@ -318,7 +345,7 @@ class FormState extends State { SemanticsService.announce(errorMessage, directionality, assertiveness: Assertiveness.assertive); } } - return !hasError; + return formValidationStatus; } } @@ -611,3 +638,19 @@ enum AutovalidateMode { /// interaction. onUserInteraction, } + +// Used to encapsultae `Form`'s validation state +// Note that having all vlaues of `fieldsValidationStatus` as true +// is not a guarantee of a valid form. Fields with no keys are skipped in this +// Map. +// a Form with no validation erros is only represented by setting `isValid` to true. +class _FormValidationStatus { + + _FormValidationStatus({ + required this.fieldsValidationStatus, + required this.isValid, + }); + + final Map fieldsValidationStatus; + final bool isValid; +} diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 90f5fe727bf3..c7076821c39a 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -273,6 +273,124 @@ void main() { }, ); + testWidgetsWithLeakTracking( + 'validateGranually returns correct values for fields with keys and ignores fields without keys', + (WidgetTester tester) async { + final GlobalKey formKey = GlobalKey(); + final GlobalKey> fieldKey1 = GlobalKey>(); + final GlobalKey> fieldKey2 = GlobalKey>(); + const String validString = 'Valid string'; + const String invalidString = 'Invalid string'; + String? validator(String? s) => s == validString ? null : 'Error text'; + + Widget builder() { + return MaterialApp( + home: MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: Material( + child: Form( + key: formKey, + child: ListView( + children: [ + TextFormField( + key: fieldKey1, + initialValue: validString, + validator: validator, + autovalidateMode: AutovalidateMode.disabled, + ), + TextFormField( + initialValue: invalidString, + validator: validator, + autovalidateMode: AutovalidateMode.disabled, + ), + TextFormField( + key: fieldKey2, + initialValue: invalidString, + validator: validator, + autovalidateMode: AutovalidateMode.disabled, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(builder()); + + final Map validationResult = formKey.currentState!.validateGranually(); + + expect(validationResult.length, equals(2)); + expect(validationResult[fieldKey1], isTrue); + expect(validationResult[fieldKey2], isFalse); + }, + ); + + testWidgetsWithLeakTracking( + 'Should announce error text when validateGranually is called even if an invalid field has no key', + (WidgetTester tester) async { + final GlobalKey formKey = GlobalKey(); + final GlobalKey> fieldKey1 = GlobalKey>(); + const String validString = 'Valid string'; + String? validator(String? s) => s == validString ? null : 'error'; + + Widget builder() { + return MaterialApp( + home: MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: Material( + child: Form( + key: formKey, + child: ListView( + children: [ + TextFormField( + key: fieldKey1, + initialValue: validString, + validator: validator, + autovalidateMode: AutovalidateMode.disabled, + ), + TextFormField( + initialValue: '', + validator: validator, + autovalidateMode: AutovalidateMode.disabled, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(builder()); + expect(find.text('error'), findsNothing); + + final Map validationResult = formKey.currentState!.validateGranually(); + + expect(validationResult.length, equals(1)); + expect(validationResult.values, everyElement(isTrue)); + + await tester.pump(); + expect(find.text('error'), findsOneWidget); + + final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single; + expect(announcement.message, 'error'); + expect(announcement.textDirection, TextDirection.ltr); + expect(announcement.assertiveness, Assertiveness.assertive); + }, + ); + testWidgetsWithLeakTracking('Multiple TextFormFields communicate', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final GlobalKey> fieldKey = GlobalKey>(); From 58b69f34cb4451fc6d52c2d8a75d3e1072d4ab28 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 27 Sep 2023 12:57:24 +0200 Subject: [PATCH 02/11] Removed taboo words from comments --- packages/flutter/lib/src/widgets/form.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 2961b3a26863..03b023676037 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -305,9 +305,8 @@ class FormState extends State { /// returns a [Map] which keys' are [Key]s of the descendant [FormField]s /// and values' are their corresponding validation results. /// - /// Note that any [FormField] that has not been asigned a key will be ignored in - /// the result. To get the absolute validation value of the Form, Consider using - /// [validate] + /// Any [FormField] that has not been asigned a key will be ignored in the result. + /// To get the absolute validation value of the Form, Consider using [validate] /// /// Common usage of this method is when you need draw the user's attention /// to the invalid field(s) using their widget key(s). @@ -639,9 +638,9 @@ enum AutovalidateMode { onUserInteraction, } -// Used to encapsultae `Form`'s validation state -// Note that having all vlaues of `fieldsValidationStatus` as true -// is not a guarantee of a valid form. Fields with no keys are skipped in this +// Used to encapsultae `Form`'s validation state. +// Having all vlaues of `fieldsValidationStatus` as true is not a guarantee +// of a valid form. Fields with no keys are skipped in this // Map. // a Form with no validation erros is only represented by setting `isValid` to true. class _FormValidationStatus { From bccf8c5bbfa22cfe052e869b692c8856925d346d Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 18 Oct 2023 14:46:28 +0200 Subject: [PATCH 03/11] fix typos and docs formatting --- packages/flutter/lib/src/widgets/form.dart | 14 ++++++++------ packages/flutter/test/widgets/form_test.dart | 8 ++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 03b023676037..d40e393ccf22 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -293,7 +293,9 @@ class FormState extends State { /// returns true if there are no errors. /// /// The form will rebuild to report the results. - /// See also: [validateGranually] + /// See also: + /// * [validateGranularly], which returns a [Map] describing the validation + /// status for each [FormField] bool validate() { _hasInteractedByUser = true; _forceRebuild(); @@ -305,14 +307,14 @@ class FormState extends State { /// returns a [Map] which keys' are [Key]s of the descendant [FormField]s /// and values' are their corresponding validation results. /// - /// Any [FormField] that has not been asigned a key will be ignored in the result. + /// Any [FormField] that has not been assigned a key will be ignored in the result. /// To get the absolute validation value of the Form, Consider using [validate] /// /// Common usage of this method is when you need draw the user's attention /// to the invalid field(s) using their widget key(s). /// /// The form will rebuild to report the results. - Map validateGranually() { + Map validateGranularly() { _hasInteractedByUser = true; _forceRebuild(); return _validate().fieldsValidationStatus; @@ -638,11 +640,11 @@ enum AutovalidateMode { onUserInteraction, } -// Used to encapsultae `Form`'s validation state. -// Having all vlaues of `fieldsValidationStatus` as true is not a guarantee +// Used to encapsulate `Form`'s validation state. +// Having all values of `fieldsValidationStatus` as true is not a guarantee // of a valid form. Fields with no keys are skipped in this // Map. -// a Form with no validation erros is only represented by setting `isValid` to true. +// A Form with no validation errors is only represented by setting `isValid` to true. class _FormValidationStatus { _FormValidationStatus({ diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index c7076821c39a..6b3bd588c88f 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -274,7 +274,7 @@ void main() { ); testWidgetsWithLeakTracking( - 'validateGranually returns correct values for fields with keys and ignores fields without keys', + 'validateGranularly returns correct values for fields with keys and ignores fields without keys', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final GlobalKey> fieldKey1 = GlobalKey>(); @@ -324,7 +324,7 @@ void main() { await tester.pumpWidget(builder()); - final Map validationResult = formKey.currentState!.validateGranually(); + final Map validationResult = formKey.currentState!.validateGranularly(); expect(validationResult.length, equals(2)); expect(validationResult[fieldKey1], isTrue); @@ -333,7 +333,7 @@ void main() { ); testWidgetsWithLeakTracking( - 'Should announce error text when validateGranually is called even if an invalid field has no key', + 'Should announce error text when validateGranularly is called even if an invalid field has no key', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final GlobalKey> fieldKey1 = GlobalKey>(); @@ -376,7 +376,7 @@ void main() { await tester.pumpWidget(builder()); expect(find.text('error'), findsNothing); - final Map validationResult = formKey.currentState!.validateGranually(); + final Map validationResult = formKey.currentState!.validateGranularly(); expect(validationResult.length, equals(1)); expect(validationResult.values, everyElement(isTrue)); From 1c43aee0868e1d4d04e1af25837ad42e6bafec87 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 18 Oct 2023 16:31:00 +0200 Subject: [PATCH 04/11] Use a Map reference to return validation results --- packages/flutter/lib/src/widgets/form.dart | 25 ++++++---------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index d40e393ccf22..cdb43a830a9e 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -299,7 +299,7 @@ class FormState extends State { bool validate() { _hasInteractedByUser = true; _forceRebuild(); - return _validate().isValid; + return _validate(); } @@ -315,25 +315,24 @@ class FormState extends State { /// /// The form will rebuild to report the results. Map validateGranularly() { + final Map validationResults = {}; _hasInteractedByUser = true; _forceRebuild(); - return _validate().fieldsValidationStatus; + _validate(validationResults); + return validationResults; } - _FormValidationStatus _validate() { - final Map fieldsValidationStatus = {}; + bool _validate([Map? validationResults]) { bool hasError = false; String errorMessage = ''; for (final FormFieldState field in _fields) { hasError = !field.validate() || hasError; errorMessage += field.errorText ?? ''; final Key? key = field.widget.key; - if (key != null) { fieldsValidationStatus[key] = field.validate(); + if (validationResults != null && key != null) { } } - final _FormValidationStatus formValidationStatus = _FormValidationStatus( - fieldsValidationStatus: fieldsValidationStatus, isValid: !hasError); if (errorMessage.isNotEmpty) { final TextDirection directionality = Directionality.of(context); @@ -346,7 +345,7 @@ class FormState extends State { SemanticsService.announce(errorMessage, directionality, assertiveness: Assertiveness.assertive); } } - return formValidationStatus; + return !hasError; } } @@ -645,13 +644,3 @@ enum AutovalidateMode { // of a valid form. Fields with no keys are skipped in this // Map. // A Form with no validation errors is only represented by setting `isValid` to true. -class _FormValidationStatus { - - _FormValidationStatus({ - required this.fieldsValidationStatus, - required this.isValid, - }); - - final Map fieldsValidationStatus; - final bool isValid; -} From 05d300b605d014ca8f3b4972786203f7488ebf4c Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 18 Oct 2023 16:32:03 +0200 Subject: [PATCH 05/11] Remove duplicated method call --- packages/flutter/lib/src/widgets/form.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index cdb43a830a9e..598a33af85e2 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -326,11 +326,12 @@ class FormState extends State { bool hasError = false; String errorMessage = ''; for (final FormFieldState field in _fields) { - hasError = !field.validate() || hasError; + final bool isFieldValid = field.validate(); + hasError = !isFieldValid || hasError; errorMessage += field.errorText ?? ''; final Key? key = field.widget.key; - fieldsValidationStatus[key] = field.validate(); if (validationResults != null && key != null) { + validationResults[key] = isFieldValid; } } From 53b0e83e791bed1e80867411af04521d4863c9e6 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 18 Oct 2023 16:39:41 +0200 Subject: [PATCH 06/11] Remove deleted class docs --- packages/flutter/lib/src/widgets/form.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 598a33af85e2..07123bc161b6 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -639,9 +639,3 @@ enum AutovalidateMode { /// interaction. onUserInteraction, } - -// Used to encapsulate `Form`'s validation state. -// Having all values of `fieldsValidationStatus` as true is not a guarantee -// of a valid form. Fields with no keys are skipped in this -// Map. -// A Form with no validation errors is only represented by setting `isValid` to true. From 7de28b0fa62eba674e682e1173193922040a0940 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Wed, 18 Oct 2023 20:17:41 +0200 Subject: [PATCH 07/11] added empty line to docs --- packages/flutter/lib/src/widgets/form.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 07123bc161b6..dc932f90f2c2 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -293,6 +293,7 @@ class FormState extends State { /// returns true if there are no errors. /// /// The form will rebuild to report the results. + /// /// See also: /// * [validateGranularly], which returns a [Map] describing the validation /// status for each [FormField] From 60bb5dadc0f2e7b2760b5e66d1d761f3ffce872b Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Sat, 4 Nov 2023 23:52:04 +0100 Subject: [PATCH 08/11] return List of invalid fields instead of a Map --- packages/flutter/lib/src/widgets/form.dart | 19 +++++++-------- packages/flutter/test/widgets/form_test.dart | 25 +++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index dc932f90f2c2..a5b3277670cd 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -305,34 +305,31 @@ class FormState extends State { /// Validates every [FormField]s that is a descendant of this [Form], and - /// returns a [Map] which keys' are [Key]s of the descendant [FormField]s - /// and values' are their corresponding validation results. + /// returns a [List] of [FormFieldState] of the invalid field(s) only, if any. /// - /// Any [FormField] that has not been assigned a key will be ignored in the result. /// To get the absolute validation value of the Form, Consider using [validate] /// /// Common usage of this method is when you need draw the user's attention /// to the invalid field(s) using their widget key(s). /// /// The form will rebuild to report the results. - Map validateGranularly() { - final Map validationResults = {}; + List> validateGranularly() { + final List> invalidFields = List>.empty(growable: true); _hasInteractedByUser = true; _forceRebuild(); - _validate(validationResults); - return validationResults; + _validate(invalidFields); + return invalidFields; } - bool _validate([Map? validationResults]) { + bool _validate([List>? invalidFields]) { bool hasError = false; String errorMessage = ''; for (final FormFieldState field in _fields) { final bool isFieldValid = field.validate(); hasError = !isFieldValid || hasError; errorMessage += field.errorText ?? ''; - final Key? key = field.widget.key; - if (validationResults != null && key != null) { - validationResults[key] = isFieldValid; + if (invalidFields != null && !isFieldValid) { + invalidFields.add(field); } } diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 6b3bd588c88f..a33673810fa7 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -277,8 +277,9 @@ void main() { 'validateGranularly returns correct values for fields with keys and ignores fields without keys', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); - final GlobalKey> fieldKey1 = GlobalKey>(); - final GlobalKey> fieldKey2 = GlobalKey>(); + final UniqueKey validFieldsKey = UniqueKey(); + final UniqueKey invalidFieldsKey = UniqueKey(); + const String validString = 'Valid string'; const String invalidString = 'Invalid string'; String? validator(String? s) => s == validString ? null : 'Error text'; @@ -296,18 +297,19 @@ void main() { child: ListView( children: [ TextFormField( - key: fieldKey1, + key: validFieldsKey, initialValue: validString, validator: validator, autovalidateMode: AutovalidateMode.disabled, ), TextFormField( + key: invalidFieldsKey, initialValue: invalidString, validator: validator, autovalidateMode: AutovalidateMode.disabled, ), TextFormField( - key: fieldKey2, + key: invalidFieldsKey, initialValue: invalidString, validator: validator, autovalidateMode: AutovalidateMode.disabled, @@ -324,19 +326,18 @@ void main() { await tester.pumpWidget(builder()); - final Map validationResult = formKey.currentState!.validateGranularly(); + final List> validationResult = formKey.currentState!.validateGranularly(); expect(validationResult.length, equals(2)); - expect(validationResult[fieldKey1], isTrue); - expect(validationResult[fieldKey2], isFalse); + expect(validationResult.where((FormFieldState field) => field.widget.key == invalidFieldsKey).length, equals(2)); + expect(validationResult.where((FormFieldState field) => field.widget.key == validFieldsKey).length, equals(0)); }, ); testWidgetsWithLeakTracking( - 'Should announce error text when validateGranularly is called even if an invalid field has no key', + 'Should announce error text when validateGranularly is called', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); - final GlobalKey> fieldKey1 = GlobalKey>(); const String validString = 'Valid string'; String? validator(String? s) => s == validString ? null : 'error'; @@ -353,7 +354,6 @@ void main() { child: ListView( children: [ TextFormField( - key: fieldKey1, initialValue: validString, validator: validator, autovalidateMode: AutovalidateMode.disabled, @@ -376,10 +376,7 @@ void main() { await tester.pumpWidget(builder()); expect(find.text('error'), findsNothing); - final Map validationResult = formKey.currentState!.validateGranularly(); - - expect(validationResult.length, equals(1)); - expect(validationResult.values, everyElement(isTrue)); + formKey.currentState!.validateGranularly(); await tester.pump(); expect(find.text('error'), findsOneWidget); From bfbfd5b5a0563a58097e2b3c0b93e10cc5517f33 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Sun, 5 Nov 2023 18:14:58 +0100 Subject: [PATCH 09/11] use Set instead of a List --- packages/flutter/lib/src/widgets/form.dart | 24 +++++++++++--------- packages/flutter/test/widgets/form_test.dart | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 5024f6d192c4..45d5b582d15c 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -293,8 +293,8 @@ class FormState extends State { /// The form will rebuild to report the results. /// /// See also: - /// * [validateGranularly], which returns a [Map] describing the validation - /// status for each [FormField] + /// * [validateGranularly], which also validates descendant [FormField]s, + /// but instead returns a [Set] of fields with errors bool validate() { _hasInteractedByUser = true; _forceRebuild(); @@ -302,24 +302,26 @@ class FormState extends State { } - /// Validates every [FormField]s that is a descendant of this [Form], and - /// returns a [List] of [FormFieldState] of the invalid field(s) only, if any. - /// - /// To get the absolute validation value of the Form, Consider using [validate] + /// Validates every [FormField] that is a descendant of this [Form], and + /// returns a [Set] of [FormFieldState] of the invalid field(s) only, if any. /// - /// Common usage of this method is when you need draw the user's attention - /// to the invalid field(s) using their widget key(s). + /// This method can be useful to highlight field(s) with errors, + /// possibly using their widget key(s). /// /// The form will rebuild to report the results. - List> validateGranularly() { - final List> invalidFields = List>.empty(growable: true); + /// + /// See also: + /// * [validate], which also validates descendant [FormField]s, + /// and return true if there are no errors + Set> validateGranularly() { + final Set> invalidFields = >{}; _hasInteractedByUser = true; _forceRebuild(); _validate(invalidFields); return invalidFields; } - bool _validate([List>? invalidFields]) { + bool _validate([Set>? invalidFields]) { bool hasError = false; String errorMessage = ''; for (final FormFieldState field in _fields) { diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index fe286303de08..f8b920c41374 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -274,7 +274,7 @@ void main() { ); testWidgetsWithLeakTracking( - 'validateGranularly returns correct values for fields with keys and ignores fields without keys', + 'validateGranularly returns a set containing all, and only, invalid fields', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); final UniqueKey validFieldsKey = UniqueKey(); @@ -326,7 +326,7 @@ void main() { await tester.pumpWidget(builder()); - final List> validationResult = formKey.currentState!.validateGranularly(); + final Set> validationResult = formKey.currentState!.validateGranularly(); expect(validationResult.length, equals(2)); expect(validationResult.where((FormFieldState field) => field.widget.key == invalidFieldsKey).length, equals(2)); From 674febb0d80006c692cbd8e7dee5ade5c25ea2c0 Mon Sep 17 00:00:00 2001 From: SharbelOkzan Date: Sat, 16 Dec 2023 15:09:54 +0100 Subject: [PATCH 10/11] fix punctuation --- packages/flutter/lib/src/widgets/form.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 45d5b582d15c..71b0d1a9fa27 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -294,7 +294,7 @@ class FormState extends State { /// /// See also: /// * [validateGranularly], which also validates descendant [FormField]s, - /// but instead returns a [Set] of fields with errors + /// but instead returns a [Set] of fields with errors. bool validate() { _hasInteractedByUser = true; _forceRebuild(); @@ -312,7 +312,7 @@ class FormState extends State { /// /// See also: /// * [validate], which also validates descendant [FormField]s, - /// and return true if there are no errors + /// and return true if there are no errors. Set> validateGranularly() { final Set> invalidFields = >{}; _hasInteractedByUser = true; From 01e22aa429e5f492d0f37e5d86602ed6e0a35a7d Mon Sep 17 00:00:00 2001 From: SharbelOkzan <89970141+SharbelOkzan@users.noreply.github.com> Date: Wed, 3 Jan 2024 23:36:57 +0100 Subject: [PATCH 11/11] Update validateGranularly doc comments --- packages/flutter/lib/src/widgets/form.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 71b0d1a9fa27..657bb04280bc 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -305,8 +305,7 @@ class FormState extends State { /// Validates every [FormField] that is a descendant of this [Form], and /// returns a [Set] of [FormFieldState] of the invalid field(s) only, if any. /// - /// This method can be useful to highlight field(s) with errors, - /// possibly using their widget key(s). + /// This method can be useful to highlight field(s) with errors. /// /// The form will rebuild to report the results. ///