From 4c8fa187f31dc5fb65c895533be429f2e0176a39 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Thu, 14 Oct 2021 14:53:03 -0500 Subject: [PATCH] Reland Remove autovalidate deprecations (#91443) --- .../flutter/lib/src/material/date_picker.dart | 36 +++- .../flutter/lib/src/material/dropdown.dart | 16 +- .../lib/src/material/text_form_field.dart | 16 +- .../flutter/lib/src/material/time_picker.dart | 36 +++- packages/flutter/lib/src/widgets/form.dart | 59 +------ .../material/dropdown_form_field_test.dart | 23 --- .../test/material/text_form_field_test.dart | 17 -- packages/flutter/test/widgets/form_test.dart | 157 +----------------- 8 files changed, 72 insertions(+), 288 deletions(-) diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index d51b8030ccc..3a6294ebaec 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -355,7 +355,7 @@ class DatePickerDialog extends StatefulWidget { class _DatePickerDialogState extends State with RestorationMixin { late final RestorableDateTime _selectedDate = RestorableDateTime(widget.initialDate); late final _RestorableDatePickerEntryMode _entryMode = _RestorableDatePickerEntryMode(widget.initialEntryMode); - final RestorableBool _autoValidate = RestorableBool(false); + final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); @override String? get restorationId => widget.restorationId; @@ -363,7 +363,7 @@ class _DatePickerDialogState extends State with RestorationMix @override void restoreState(RestorationBucket? oldBucket, bool initialRestore) { registerForRestoration(_selectedDate, 'selected_date'); - registerForRestoration(_autoValidate, 'autovalidate'); + registerForRestoration(_autovalidateMode, 'autovalidateMode'); registerForRestoration(_entryMode, 'calendar_entry_mode'); } @@ -374,7 +374,7 @@ class _DatePickerDialogState extends State with RestorationMix if (_entryMode.value == DatePickerEntryMode.input || _entryMode.value == DatePickerEntryMode.inputOnly) { final FormState form = _formKey.currentState!; if (!form.validate()) { - setState(() => _autoValidate.value = true); + setState(() => _autovalidateMode.value = AutovalidateMode.always); return; } form.save(); @@ -390,7 +390,7 @@ class _DatePickerDialogState extends State with RestorationMix setState(() { switch (_entryMode.value) { case DatePickerEntryMode.calendar: - _autoValidate.value = false; + _autovalidateMode.value = AutovalidateMode.disabled; _entryMode.value = DatePickerEntryMode.input; break; case DatePickerEntryMode.input: @@ -492,7 +492,7 @@ class _DatePickerDialogState extends State with RestorationMix Form inputDatePicker() { return Form( key: _formKey, - autovalidate: _autoValidate.value, + autovalidateMode: _autovalidateMode.value, child: Container( padding: const EdgeInsets.symmetric(horizontal: 24), height: orientation == Orientation.portrait ? _inputFormPortraitHeight : _inputFormLandscapeHeight, @@ -642,6 +642,32 @@ class _RestorableDatePickerEntryMode extends RestorableValue value.index; } +// A restorable [AutovalidateMode] value. +// +// This serializes each entry as a unique `int` value. +class _RestorableAutovalidateMode extends RestorableValue { + _RestorableAutovalidateMode( + AutovalidateMode defaultValue, + ) : _defaultValue = defaultValue; + + final AutovalidateMode _defaultValue; + + @override + AutovalidateMode createDefaultValue() => _defaultValue; + + @override + void didUpdateValue(AutovalidateMode? oldValue) { + assert(debugIsSerializableForRestoration(value.index)); + notifyListeners(); + } + + @override + AutovalidateMode fromPrimitives(Object? data) => AutovalidateMode.values[data! as int]; + + @override + Object? toPrimitives() => value.index; +} + /// Re-usable widget that displays the selected date (in large font) and the /// help text above it. /// diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 53bdb32e4b5..203c1af7dd6 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -1535,12 +1535,6 @@ class DropdownButtonFormField extends FormField { InputDecoration? decoration, FormFieldSetter? onSaved, FormFieldValidator? validator, - @Deprecated( - 'Use autovalidateMode parameter which provide more specific ' - 'behaviour related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - bool autovalidate = false, AutovalidateMode? autovalidateMode, double? menuMaxHeight, bool? enableFeedback, @@ -1560,21 +1554,13 @@ class DropdownButtonFormField extends FormField { assert(isExpanded != null), assert(itemHeight == null || itemHeight >= kMinInteractiveDimension), assert(autofocus != null), - assert(autovalidate != null), - assert( - autovalidate == false || - autovalidate == true && autovalidateMode == null, - 'autovalidate and autovalidateMode should not be used together.', - ), decoration = decoration ?? InputDecoration(focusColor: focusColor), super( key: key, onSaved: onSaved, initialValue: value, validator: validator, - autovalidateMode: autovalidate - ? AutovalidateMode.always - : (autovalidateMode ?? AutovalidateMode.disabled), + autovalidateMode: autovalidateMode ?? AutovalidateMode.disabled, builder: (FormFieldState field) { final _DropdownButtonFormFieldState state = field as _DropdownButtonFormFieldState; final InputDecoration decorationArg = decoration ?? InputDecoration(focusColor: focusColor); diff --git a/packages/flutter/lib/src/material/text_form_field.dart b/packages/flutter/lib/src/material/text_form_field.dart index 9d458f321a6..3b1c8108143 100644 --- a/packages/flutter/lib/src/material/text_form_field.dart +++ b/packages/flutter/lib/src/material/text_form_field.dart @@ -116,12 +116,6 @@ class TextFormField extends FormField { SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, bool enableSuggestions = true, - @Deprecated( - 'Use autovalidateMode parameter which provide more specific ' - 'behaviour related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - bool autovalidate = false, @Deprecated( 'Use maxLengthEnforcement parameter which provides more specific ' 'behavior related to the maxLength limit. ' @@ -164,12 +158,6 @@ class TextFormField extends FormField { assert(obscureText != null), assert(autocorrect != null), assert(enableSuggestions != null), - assert(autovalidate != null), - assert( - autovalidate == false || - autovalidate == true && autovalidateMode == null, - 'autovalidate and autovalidateMode should not be used together.', - ), assert(maxLengthEnforced != null), assert( maxLengthEnforced || maxLengthEnforcement == null, @@ -198,9 +186,7 @@ class TextFormField extends FormField { onSaved: onSaved, validator: validator, enabled: enabled ?? decoration?.enabled ?? true, - autovalidateMode: autovalidate - ? AutovalidateMode.always - : (autovalidateMode ?? AutovalidateMode.disabled), + autovalidateMode: autovalidateMode ?? AutovalidateMode.disabled, builder: (FormFieldState field) { final _TextFormFieldState state = field as _TextFormFieldState; final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration()) diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index aed81541068..31377f3168a 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -1915,6 +1915,32 @@ class _RestorableTimePickerMode extends RestorableValue<_TimePickerMode> { Object? toPrimitives() => value.index; } +// A restorable [AutovalidateMode] value. +// +// This serializes each entry as a unique `int` value. +class _RestorableAutovalidateMode extends RestorableValue { + _RestorableAutovalidateMode( + AutovalidateMode defaultValue, + ) : _defaultValue = defaultValue; + + final AutovalidateMode _defaultValue; + + @override + AutovalidateMode createDefaultValue() => _defaultValue; + + @override + void didUpdateValue(AutovalidateMode? oldValue) { + assert(debugIsSerializableForRestoration(value.index)); + notifyListeners(); + } + + @override + AutovalidateMode fromPrimitives(Object? data) => AutovalidateMode.values[data! as int]; + + @override + Object? toPrimitives() => value.index; +} + // A restorable [_RestorableTimePickerEntryMode] value. // // This serializes each entry as a unique `int` value. @@ -1949,7 +1975,7 @@ class _TimePickerDialogState extends State with RestorationMix late final _RestorableTimePickerEntryMode _entryMode = _RestorableTimePickerEntryMode(widget.initialEntryMode); final _RestorableTimePickerMode _mode = _RestorableTimePickerMode(_TimePickerMode.hour); final _RestorableTimePickerModeN _lastModeAnnounced = _RestorableTimePickerModeN(null); - final RestorableBool _autoValidate = RestorableBool(false); + final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); final RestorableBoolN _autofocusHour = RestorableBoolN(null); final RestorableBoolN _autofocusMinute = RestorableBoolN(null); final RestorableBool _announcedInitialTime = RestorableBool(false); @@ -1979,7 +2005,7 @@ class _TimePickerDialogState extends State with RestorationMix registerForRestoration(_entryMode, 'entry_mode'); registerForRestoration(_mode, 'mode'); registerForRestoration(_lastModeAnnounced, 'last_mode_announced'); - registerForRestoration(_autoValidate, 'autovalidate'); + registerForRestoration(_autovalidateMode, 'autovalidateMode'); registerForRestoration(_autofocusHour, 'autofocus_hour'); registerForRestoration(_autofocusMinute, 'autofocus_minute'); registerForRestoration(_announcedInitialTime, 'announced_initial_time'); @@ -2022,7 +2048,7 @@ class _TimePickerDialogState extends State with RestorationMix setState(() { switch (_entryMode.value) { case TimePickerEntryMode.dial: - _autoValidate.value = false; + _autovalidateMode.value = AutovalidateMode.disabled; _entryMode.value = TimePickerEntryMode.input; break; case TimePickerEntryMode.input: @@ -2096,7 +2122,7 @@ class _TimePickerDialogState extends State with RestorationMix if (_entryMode.value == TimePickerEntryMode.input) { final FormState form = _formKey.currentState!; if (!form.validate()) { - setState(() { _autoValidate.value = true; }); + setState(() { _autovalidateMode.value = AutovalidateMode.always; }); return; } form.save(); @@ -2257,7 +2283,7 @@ class _TimePickerDialogState extends State with RestorationMix case TimePickerEntryMode.input: picker = Form( key: _formKey, - autovalidate: _autoValidate.value, + autovalidateMode: _autovalidateMode.value, child: SingleChildScrollView( restorationId: 'time_picker_scroll_view', child: Column( diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 27e2b54da69..dd4d87a97c0 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -40,24 +40,11 @@ class Form extends StatefulWidget { const Form({ Key? key, required this.child, - @Deprecated( - 'Use autovalidateMode parameter which provides more specific ' - 'behavior related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - this.autovalidate = false, this.onWillPop, this.onChanged, AutovalidateMode? autovalidateMode, }) : assert(child != null), - assert(autovalidate != null), - assert( - autovalidate == false || - autovalidate == true && autovalidateMode == null, - 'autovalidate and autovalidateMode should not be used together.', - ), - autovalidateMode = autovalidateMode ?? - (autovalidate ? AutovalidateMode.always : AutovalidateMode.disabled), + autovalidateMode = autovalidateMode ?? AutovalidateMode.disabled, super(key: key); /// Returns the closest [FormState] which encloses the given context. @@ -104,15 +91,6 @@ class Form extends StatefulWidget { /// {@macro flutter.widgets.FormField.autovalidateMode} final AutovalidateMode autovalidateMode; - /// Used to enable/disable form fields auto validation and update their error - /// text. - @Deprecated( - 'Use autovalidateMode parameter which provides more specific ' - 'behavior related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - final bool autovalidate; - @override FormState createState() => FormState(); } @@ -287,23 +265,11 @@ class FormField extends StatefulWidget { this.onSaved, this.validator, this.initialValue, - @Deprecated( - 'Use autovalidateMode parameter which provides more specific ' - 'behavior related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - this.autovalidate = false, this.enabled = true, AutovalidateMode? autovalidateMode, this.restorationId, }) : assert(builder != null), - assert( - autovalidate == false || - autovalidate == true && autovalidateMode == null, - 'autovalidate and autovalidateMode should not be used together.', - ), - autovalidateMode = autovalidateMode ?? - (autovalidate ? AutovalidateMode.always : AutovalidateMode.disabled), + autovalidateMode = autovalidateMode ?? AutovalidateMode.disabled, super(key: key); /// An optional method to call with the final value when the form is saved via @@ -344,26 +310,15 @@ class FormField extends StatefulWidget { /// error text. /// /// {@template flutter.widgets.FormField.autovalidateMode} - /// If [AutovalidateMode.onUserInteraction] this form field will only - /// auto-validate after its content changes, if [AutovalidateMode.always] it - /// will auto validate even without user interaction and - /// if [AutovalidateMode.disabled] the auto validation will be disabled. + /// If [AutovalidateMode.onUserInteraction], this FormField will only + /// auto-validate after its content changes. If [AutovalidateMode.always], it + /// will auto-validate even without user interaction. If + /// [AutovalidateMode.disabled], auto-validation will be disabled. /// - /// Defaults to [AutovalidateMode.disabled] if `autovalidate` is false which - /// means no auto validation will occur. If `autovalidate` is true then this - /// is set to [AutovalidateMode.always] for backward compatibility. + /// Defaults to [AutovalidateMode.disabled], cannot be null. /// {@endtemplate} final AutovalidateMode autovalidateMode; - /// Used to enable/disable auto validation and update their error - /// text. - @Deprecated( - 'Use autovalidateMode parameter which provides more specific ' - 'behavior related to auto validation. ' - 'This feature was deprecated after v1.19.0.', - ) - final bool autovalidate; - /// Restoration ID to save and restore the state of the form field. /// /// Setting the restoration ID to a non-null value results in whether or not diff --git a/packages/flutter/test/material/dropdown_form_field_test.dart b/packages/flutter/test/material/dropdown_form_field_test.dart index 0ccfb18388a..5424c20b081 100644 --- a/packages/flutter/test/material/dropdown_form_field_test.dart +++ b/packages/flutter/test/material/dropdown_form_field_test.dart @@ -1078,29 +1078,6 @@ void main() { expect(_validateCalled, 1); }); - testWidgets('autovalidateMode and autovalidate should not be used at the same time', (WidgetTester tester) async { - Widget builder() { - return MaterialApp( - home: Material( - child: Center( - child: DropdownButtonFormField( - autovalidate: true, - autovalidateMode: AutovalidateMode.always, - items: menuItems.map((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - onChanged: onChanged, - ), - ), - ), - ); - } - expect(() => builder(), throwsAssertionError); - }); - testWidgets('DropdownButtonFormField - Custom button alignment', (WidgetTester tester) async { await tester.pumpWidget(buildFormFrame( buttonAlignment: AlignmentDirectional.center, diff --git a/packages/flutter/test/material/text_form_field_test.dart b/packages/flutter/test/material/text_form_field_test.dart index e41865cdab3..b567baffce8 100644 --- a/packages/flutter/test/material/text_form_field_test.dart +++ b/packages/flutter/test/material/text_form_field_test.dart @@ -649,23 +649,6 @@ void main() { expect(_validateCalled, 1); }); - testWidgets('autovalidateMode and autovalidate should not be used at the same time', (WidgetTester tester) async { - expect(() async { - await tester.pumpWidget( - MaterialApp( - home: Material( - child: Scaffold( - body: TextFormField( - autovalidate: true, - autovalidateMode: AutovalidateMode.onUserInteraction, - ), - ), - ), - ), - ); - }, throwsAssertionError); - }); - testWidgets('textSelectionControls is passed to super', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( diff --git a/packages/flutter/test/widgets/form_test.dart b/packages/flutter/test/widgets/form_test.dart index 8861d587773..f9b70f3f913 100644 --- a/packages/flutter/test/widgets/form_test.dart +++ b/packages/flutter/test/widgets/form_test.dart @@ -684,7 +684,7 @@ void main() { ); } - // Makes sure the Form widget won't autovalidate the form fields + // Makes sure the Form widget won't auto-validate the form fields // after rebuilds if there is not user interaction. await tester.pumpWidget(builder()); await tester.pumpWidget(builder()); @@ -733,38 +733,6 @@ void main() { expect(find.text(errorText('')!), findsOneWidget); }); - testWidgets('autovalidate parameter is still used if true', (WidgetTester tester) async { - late FormFieldState formFieldState; - String? errorText(String? value) => '$value/error'; - - Widget builder() { - return MaterialApp( - home: MediaQuery( - data: const MediaQueryData(), - child: Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: Material( - child: FormField( - initialValue: 'foo', - autovalidate: true, - builder: (FormFieldState state) { - formFieldState = state; - return Container(); - }, - validator: errorText, - ), - ), - ), - ), - ), - ); - } - - await tester.pumpWidget(builder()); - expect(formFieldState.hasError, isTrue); - }); - testWidgets('Form.reset() resets form fields, and auto validation will only happen on the next user interaction if autovalidateMode is onUserInteraction', (WidgetTester tester) async { final GlobalKey formState = GlobalKey(); String? errorText(String? value) => '$value/error'; @@ -809,46 +777,6 @@ void main() { expect(find.text(errorText('bar')!), findsNothing); }); - testWidgets('Form.autovalidateMode and Form.autovalidate should not be used at the same time', (WidgetTester tester) async { - Widget builder() { - return MaterialApp( - home: MediaQuery( - data: const MediaQueryData(), - child: Directionality( - textDirection: TextDirection.ltr, - child: Form( - autovalidate: true, - autovalidateMode: AutovalidateMode.disabled, - child: Container(), - ), - ), - ), - ); - } - expect(() => builder(), throwsAssertionError); - }); - - testWidgets('FormField.autovalidateMode and FormField.autovalidate should not be used at the same time', (WidgetTester tester) async { - Widget builder() { - return MaterialApp( - home: MediaQuery( - data: const MediaQueryData(), - child: Directionality( - textDirection: TextDirection.ltr, - child: FormField( - autovalidate: true, - autovalidateMode: AutovalidateMode.disabled, - builder: (_) { - return Container(); - }, - ), - ), - ), - ); - } - expect(() => builder(), throwsAssertionError); - }); - // Regression test for https://github.com/flutter/flutter/issues/63753. testWidgets('Validate form should return correct validation if the value is composing', (WidgetTester tester) async { final GlobalKey formKey = GlobalKey(); @@ -886,87 +814,4 @@ void main() { expect(fieldValue, '123456'); expect(formKey.currentState!.validate(), isFalse); }); - - testWidgets('FormField.autovalidate parameter is passed into class the property', (WidgetTester tester) async { - String? errorText(String? value) => '$value/error'; - const ObjectKey widgetKey = ObjectKey('key'); - - Widget builder({required bool autovalidate}) { - return MaterialApp( - home: MediaQuery( - data: const MediaQueryData(), - child: Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: Material( - child: FormField( - key: widgetKey, - initialValue: 'foo', - autovalidate: autovalidate, - builder: (FormFieldState state) { - return Container(); - }, - validator: errorText, - ), - ), - ), - ), - ), - ); - } - - // When autovalidate is true - await tester.pumpWidget(builder(autovalidate: true)); - - final Finder formFieldFinder = find.byKey(widgetKey); - FormField formField = tester.widget(formFieldFinder); - expect(formField.autovalidate, isTrue); - expect(formField.autovalidateMode, equals(AutovalidateMode.always)); - - // When autovalidate is false - await tester.pumpWidget(builder(autovalidate: false)); - - formField = tester.widget(formFieldFinder); - expect(formField.autovalidate, isFalse); - expect(formField.autovalidateMode, equals(AutovalidateMode.disabled)); - }); - - testWidgets('Form.autovalidate parameter is passed into class the property', (WidgetTester tester) async { - const ObjectKey widgetKey = ObjectKey('key'); - - Widget builder({required bool autovalidate}) { - return MaterialApp( - home: MediaQuery( - data: const MediaQueryData(), - child: Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: Material( - child: Form( - key: widgetKey, - autovalidate: autovalidate, - child: Container(), - ), - ), - ), - ), - ), - ); - } - - // When autovalidate is true - await tester.pumpWidget(builder(autovalidate: true)); - - final Finder formFinder = find.byKey(widgetKey); - Form formWidget = tester.widget(formFinder); - expect(formWidget.autovalidate, isTrue); - expect(formWidget.autovalidateMode, equals(AutovalidateMode.always)); - - // When autovalidate is false - await tester.pumpWidget(builder(autovalidate: false)); - - formWidget = tester.widget(formFinder); - expect(formWidget.autovalidate, isFalse); - expect(formWidget.autovalidateMode, equals(AutovalidateMode.disabled)); - }); }