Unverified Commit 77c19f50 authored by Binni Goel's avatar Binni Goel Committed by GitHub

Test cover cupertino for memory leaks and fix Opacity Layer not disposed. (#136576)

parent 0f555046
...@@ -824,9 +824,10 @@ class RenderListWheelViewport ...@@ -824,9 +824,10 @@ class RenderListWheelViewport
@override @override
void dispose() { void dispose() {
_clipRectLayer.layer = null; _clipRectLayer.layer = null;
_childOpacityLayerHandler.layer = null;
super.dispose(); super.dispose();
} }
final LayerHandle<OpacityLayer> _childOpacityLayerHandler = LayerHandle<OpacityLayer>();
/// Paints all children visible in the current viewport. /// Paints all children visible in the current viewport.
void _paintVisibleChildren(PaintingContext context, Offset offset) { void _paintVisibleChildren(PaintingContext context, Offset offset) {
// The magnifier cannot be turned off if the opacity is less than 1.0. // The magnifier cannot be turned off if the opacity is less than 1.0.
...@@ -837,7 +838,7 @@ class RenderListWheelViewport ...@@ -837,7 +838,7 @@ class RenderListWheelViewport
// In order to reduce the number of opacity layers, we first paint all // In order to reduce the number of opacity layers, we first paint all
// partially opaque children, then finally paint the fully opaque children. // partially opaque children, then finally paint the fully opaque children.
context.pushOpacity(offset, (overAndUnderCenterOpacity * 255).round(), (PaintingContext context, Offset offset) { _childOpacityLayerHandler.layer = context.pushOpacity(offset, (overAndUnderCenterOpacity * 255).round(), (PaintingContext context, Offset offset) {
_paintAllChildren(context, offset, center: false); _paintAllChildren(context, offset, center: false);
}); });
_paintAllChildren(context, offset, center: true); _paintAllChildren(context, offset, center: true);
......
...@@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import '../rendering/rendering_tester.dart'; import '../rendering/rendering_tester.dart';
...@@ -18,7 +19,7 @@ class SpyFixedExtentScrollController extends FixedExtentScrollController { ...@@ -18,7 +19,7 @@ class SpyFixedExtentScrollController extends FixedExtentScrollController {
} }
void main() { void main() {
testWidgets('Picker respects theme styling', (WidgetTester tester) async { testWidgetsWithLeakTracking('Picker respects theme styling', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Align( home: Align(
...@@ -57,7 +58,7 @@ void main() { ...@@ -57,7 +58,7 @@ void main() {
group('layout', () { group('layout', () {
// Regression test for https://github.com/flutter/flutter/issues/22999 // Regression test for https://github.com/flutter/flutter/issues/22999
testWidgets('CupertinoPicker.builder test', (WidgetTester tester) async { testWidgetsWithLeakTracking('CupertinoPicker.builder test', (WidgetTester tester) async {
Widget buildFrame(int childCount) { Widget buildFrame(int childCount) {
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -80,9 +81,9 @@ void main() { ...@@ -80,9 +81,9 @@ void main() {
expect(tester.renderObject(find.text('1')).attached, true); expect(tester.renderObject(find.text('1')).attached, true);
}); });
testWidgets('selected item is in the middle', (WidgetTester tester) async { testWidgetsWithLeakTracking('selected item is in the middle', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 1); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 1);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -127,7 +128,7 @@ void main() { ...@@ -127,7 +128,7 @@ void main() {
}); });
}); });
testWidgets('picker dark mode', (WidgetTester tester) async { testWidgetsWithLeakTracking('picker dark mode', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.light), theme: const CupertinoThemeData(brightness: Brightness.light),
...@@ -177,9 +178,9 @@ void main() { ...@@ -177,9 +178,9 @@ void main() {
expect(find.byType(CupertinoPicker), paints..rrect(color: const Color.fromARGB(61,118, 118, 128))); expect(find.byType(CupertinoPicker), paints..rrect(color: const Color.fromARGB(61,118, 118, 128)));
expect(find.byType(CupertinoPicker), paints..rect(color: const Color(0xFF654321))); expect(find.byType(CupertinoPicker), paints..rect(color: const Color(0xFF654321)));
}); },leakTrackingTestConfig: LeakTrackingTestConfig.debugNotDisposed());
testWidgets('picker selectionOverlay', (WidgetTester tester) async { testWidgetsWithLeakTracking('picker selectionOverlay', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.light), theme: const CupertinoThemeData(brightness: Brightness.light),
...@@ -202,7 +203,7 @@ void main() { ...@@ -202,7 +203,7 @@ void main() {
expect(find.byType(CupertinoPicker), paints..rrect(color: const Color(0x12345678))); expect(find.byType(CupertinoPicker), paints..rrect(color: const Color(0x12345678)));
}); });
testWidgets('CupertinoPicker.selectionOverlay is nullable', (WidgetTester tester) async { testWidgetsWithLeakTracking('CupertinoPicker.selectionOverlay is nullable', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.light), theme: const CupertinoThemeData(brightness: Brightness.light),
...@@ -226,7 +227,7 @@ void main() { ...@@ -226,7 +227,7 @@ void main() {
}); });
group('scroll', () { group('scroll', () {
testWidgets( testWidgetsWithLeakTracking(
'scrolling calls onSelectedItemChanged and triggers haptic feedback', 'scrolling calls onSelectedItemChanged and triggers haptic feedback',
(WidgetTester tester) async { (WidgetTester tester) async {
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
...@@ -280,7 +281,7 @@ void main() { ...@@ -280,7 +281,7 @@ void main() {
variant: TargetPlatformVariant.only(TargetPlatform.iOS), variant: TargetPlatformVariant.only(TargetPlatform.iOS),
); );
testWidgets( testWidgetsWithLeakTracking(
'do not trigger haptic effects on non-iOS devices', 'do not trigger haptic effects on non-iOS devices',
(WidgetTester tester) async { (WidgetTester tester) async {
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
...@@ -317,8 +318,9 @@ void main() { ...@@ -317,8 +318,9 @@ void main() {
variant: TargetPlatformVariant(TargetPlatform.values.where((TargetPlatform platform) => platform != TargetPlatform.iOS).toSet()), variant: TargetPlatformVariant(TargetPlatform.values.where((TargetPlatform platform) => platform != TargetPlatform.iOS).toSet()),
); );
testWidgets('a drag in between items settles back', (WidgetTester tester) async { testWidgetsWithLeakTracking('a drag in between items settles back', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -371,9 +373,10 @@ void main() { ...@@ -371,9 +373,10 @@ void main() {
expect(selectedItems, <int>[9]); expect(selectedItems, <int>[9]);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('a big fling that overscrolls springs back', (WidgetTester tester) async { testWidgetsWithLeakTracking('a big fling that overscrolls springs back', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller =
FixedExtentScrollController(initialItem: 10); FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -433,7 +436,7 @@ void main() { ...@@ -433,7 +436,7 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
}); });
testWidgets('Picker adapts to MaterialApp dark mode', (WidgetTester tester) async { testWidgetsWithLeakTracking('Picker adapts to MaterialApp dark mode', (WidgetTester tester) async {
Widget buildCupertinoPicker(Brightness brightness) { Widget buildCupertinoPicker(Brightness brightness) {
return MaterialApp( return MaterialApp(
theme: ThemeData(brightness: brightness), theme: ThemeData(brightness: brightness),
...@@ -474,7 +477,7 @@ void main() { ...@@ -474,7 +477,7 @@ void main() {
}); });
group('CupertinoPickerDefaultSelectionOverlay', () { group('CupertinoPickerDefaultSelectionOverlay', () {
testWidgets('should be using directional decoration', (WidgetTester tester) async { testWidgetsWithLeakTracking('should be using directional decoration', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.light), theme: const CupertinoThemeData(brightness: Brightness.light),
...@@ -497,8 +500,9 @@ void main() { ...@@ -497,8 +500,9 @@ void main() {
}); });
}); });
testWidgets('Scroll controller is detached upon dispose', (WidgetTester tester) async { testWidgetsWithLeakTracking('Scroll controller is detached upon dispose', (WidgetTester tester) async {
final SpyFixedExtentScrollController controller = SpyFixedExtentScrollController(); final SpyFixedExtentScrollController controller = SpyFixedExtentScrollController();
addTearDown(controller.dispose);
expect(controller.hasListeners, false); expect(controller.hasListeners, false);
expect(controller.positions.length, 0); expect(controller.positions.length, 0);
...@@ -528,7 +532,8 @@ void main() { ...@@ -528,7 +532,8 @@ void main() {
expect(controller.positions.length, 0); expect(controller.positions.length, 0);
}); });
testWidgets('Registers taps and does not crash with certain diameterRatio', (WidgetTester tester) async { testWidgetsWithLeakTracking(
'Registers taps and does not crash with certain diameterRatio', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126491 // Regression test for https://github.com/flutter/flutter/issues/126491
final List<int> children = List<int>.generate(100, (int index) => index); final List<int> children = List<int>.generate(100, (int index) => index);
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() { void main() {
testWidgets('Passes textAlign to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes textAlign to underlying CupertinoTextField', (WidgetTester tester) async {
const TextAlign alignment = TextAlign.center; const TextAlign alignment = TextAlign.center;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -27,7 +28,7 @@ void main() { ...@@ -27,7 +28,7 @@ void main() {
expect(textFieldWidget.textAlign, alignment); expect(textFieldWidget.textAlign, alignment);
}); });
testWidgets('Passes scrollPhysics to underlying TextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes scrollPhysics to underlying TextField', (WidgetTester tester) async {
const ScrollPhysics scrollPhysics = ScrollPhysics(); const ScrollPhysics scrollPhysics = ScrollPhysics();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -47,7 +48,7 @@ void main() { ...@@ -47,7 +48,7 @@ void main() {
expect(textFieldWidget.scrollPhysics, scrollPhysics); expect(textFieldWidget.scrollPhysics, scrollPhysics);
}); });
testWidgets('Passes textAlignVertical to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes textAlignVertical to underlying CupertinoTextField', (WidgetTester tester) async {
const TextAlignVertical textAlignVertical = TextAlignVertical.bottom; const TextAlignVertical textAlignVertical = TextAlignVertical.bottom;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -67,7 +68,7 @@ void main() { ...@@ -67,7 +68,7 @@ void main() {
expect(textFieldWidget.textAlignVertical, textAlignVertical); expect(textFieldWidget.textAlignVertical, textAlignVertical);
}); });
testWidgets('Passes textInputAction to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes textInputAction to underlying CupertinoTextField', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -85,7 +86,7 @@ void main() { ...@@ -85,7 +86,7 @@ void main() {
expect(textFieldWidget.textInputAction, TextInputAction.next); expect(textFieldWidget.textInputAction, TextInputAction.next);
}); });
testWidgets('Passes onEditingComplete to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes onEditingComplete to underlying CupertinoTextField', (WidgetTester tester) async {
void onEditingComplete() {} void onEditingComplete() {}
await tester.pumpWidget( await tester.pumpWidget(
...@@ -105,7 +106,7 @@ void main() { ...@@ -105,7 +106,7 @@ void main() {
expect(textFieldWidget.onEditingComplete, onEditingComplete); expect(textFieldWidget.onEditingComplete, onEditingComplete);
}); });
testWidgets('Passes cursor attributes to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes cursor attributes to underlying CupertinoTextField', (WidgetTester tester) async {
const double cursorWidth = 3.14; const double cursorWidth = 3.14;
const double cursorHeight = 6.28; const double cursorHeight = 6.28;
const Radius cursorRadius = Radius.circular(2); const Radius cursorRadius = Radius.circular(2);
...@@ -133,7 +134,7 @@ void main() { ...@@ -133,7 +134,7 @@ void main() {
expect(textFieldWidget.cursorColor, cursorColor); expect(textFieldWidget.cursorColor, cursorColor);
}); });
testWidgets('onFieldSubmit callbacks are called', (WidgetTester tester) async { testWidgetsWithLeakTracking('onFieldSubmit callbacks are called', (WidgetTester tester) async {
bool called = false; bool called = false;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -154,7 +155,7 @@ void main() { ...@@ -154,7 +155,7 @@ void main() {
expect(called, true); expect(called, true);
}); });
testWidgets('onChanged callbacks are called', (WidgetTester tester) async { testWidgetsWithLeakTracking('onChanged callbacks are called', (WidgetTester tester) async {
late String value; late String value;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -174,7 +175,7 @@ void main() { ...@@ -174,7 +175,7 @@ void main() {
expect(value, 'Soup'); expect(value, 'Soup');
}); });
testWidgets('autovalidateMode is passed to super', (WidgetTester tester) async { testWidgetsWithLeakTracking('autovalidateMode is passed to super', (WidgetTester tester) async {
int validateCalled = 0; int validateCalled = 0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -197,7 +198,7 @@ void main() { ...@@ -197,7 +198,7 @@ void main() {
expect(validateCalled, 2); expect(validateCalled, 2);
}); });
testWidgets('validate is called if widget is enabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('validate is called if widget is enabled', (WidgetTester tester) async {
int validateCalled = 0; int validateCalled = 0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -221,7 +222,7 @@ void main() { ...@@ -221,7 +222,7 @@ void main() {
expect(validateCalled, 2); expect(validateCalled, 2);
}); });
testWidgets('readonly text form field will hide cursor by default', (WidgetTester tester) async { testWidgetsWithLeakTracking('readonly text form field will hide cursor by default', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -262,7 +263,7 @@ void main() { ...@@ -262,7 +263,7 @@ void main() {
expect(renderEditable, paintsExactlyCountTimes(#drawRect, 0)); expect(renderEditable, paintsExactlyCountTimes(#drawRect, 0));
}, skip: isBrowser); // [intended] We do not use Flutter-rendered context menu on the Web. }, skip: isBrowser); // [intended] We do not use Flutter-rendered context menu on the Web.
testWidgets('onTap is called upon tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('onTap is called upon tap', (WidgetTester tester) async {
int tapCount = 0; int tapCount = 0;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
...@@ -288,7 +289,7 @@ void main() { ...@@ -288,7 +289,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/54472. // Regression test for https://github.com/flutter/flutter/issues/54472.
testWidgets('reset resets the text fields value to the initialValue', (WidgetTester tester) async { testWidgetsWithLeakTracking('reset resets the text fields value to the initialValue', (WidgetTester tester) async {
await tester.pumpWidget(CupertinoApp( await tester.pumpWidget(CupertinoApp(
home: Center( home: Center(
child: CupertinoTextFormFieldRow( child: CupertinoTextFormFieldRow(
...@@ -307,7 +308,7 @@ void main() { ...@@ -307,7 +308,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/54472. // Regression test for https://github.com/flutter/flutter/issues/54472.
testWidgets('didChange changes text fields value', (WidgetTester tester) async { testWidgetsWithLeakTracking('didChange changes text fields value', (WidgetTester tester) async {
await tester.pumpWidget(CupertinoApp( await tester.pumpWidget(CupertinoApp(
home: Center( home: Center(
child: CupertinoTextFormFieldRow( child: CupertinoTextFormFieldRow(
...@@ -326,7 +327,7 @@ void main() { ...@@ -326,7 +327,7 @@ void main() {
expect(find.text('changedValue'), findsOneWidget); expect(find.text('changedValue'), findsOneWidget);
}); });
testWidgets('onChanged callbacks value and FormFieldState.value are sync', (WidgetTester tester) async { testWidgetsWithLeakTracking('onChanged callbacks value and FormFieldState.value are sync', (WidgetTester tester) async {
bool called = false; bool called = false;
late FormFieldState<String> state; late FormFieldState<String> state;
...@@ -352,7 +353,7 @@ void main() { ...@@ -352,7 +353,7 @@ void main() {
expect(called, true); expect(called, true);
}); });
testWidgets('autofillHints is passed to super', (WidgetTester tester) async { testWidgetsWithLeakTracking('autofillHints is passed to super', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -368,7 +369,7 @@ void main() { ...@@ -368,7 +369,7 @@ void main() {
expect(widget.autofillHints, equals(const <String>[AutofillHints.countryName])); expect(widget.autofillHints, equals(const <String>[AutofillHints.countryName]));
}); });
testWidgets('autovalidateMode is passed to super', (WidgetTester tester) async { testWidgetsWithLeakTracking('autovalidateMode is passed to super', (WidgetTester tester) async {
int validateCalled = 0; int validateCalled = 0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -391,7 +392,7 @@ void main() { ...@@ -391,7 +392,7 @@ void main() {
expect(validateCalled, 1); expect(validateCalled, 1);
}); });
testWidgets('AutovalidateMode.always mode shows error from the start', (WidgetTester tester) async { testWidgetsWithLeakTracking('AutovalidateMode.always mode shows error from the start', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -411,9 +412,9 @@ void main() { ...@@ -411,9 +412,9 @@ void main() {
expect(errorText.data, 'Error'); expect(errorText.data, 'Error');
}); });
testWidgets('Shows error text upon invalid input', (WidgetTester tester) async { testWidgetsWithLeakTracking('Shows error text upon invalid input', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: ''); final TextEditingController controller = TextEditingController(text: '');
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -439,7 +440,7 @@ void main() { ...@@ -439,7 +440,7 @@ void main() {
expect(errorText.data, 'Error'); expect(errorText.data, 'Error');
}); });
testWidgets('Shows prefix', (WidgetTester tester) async { testWidgetsWithLeakTracking('Shows prefix', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -457,7 +458,7 @@ void main() { ...@@ -457,7 +458,7 @@ void main() {
expect(errorText.data, 'Enter Value'); expect(errorText.data, 'Enter Value');
}); });
testWidgets('Passes textDirection to underlying CupertinoTextField', (WidgetTester tester) async { testWidgetsWithLeakTracking('Passes textDirection to underlying CupertinoTextField', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -491,7 +492,8 @@ void main() { ...@@ -491,7 +492,8 @@ void main() {
expect(rtlTextFieldWidget.textDirection, TextDirection.rtl); expect(rtlTextFieldWidget.textDirection, TextDirection.rtl);
}); });
testWidgets('CupertinoTextFormFieldRow onChanged is called when the form is reset', (WidgetTester tester) async { testWidgetsWithLeakTracking(
'CupertinoTextFormFieldRow onChanged is called when the form is reset', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/123009. // Regression test for https://github.com/flutter/flutter/issues/123009.
final GlobalKey<FormFieldState<String>> stateKey = GlobalKey<FormFieldState<String>>(); final GlobalKey<FormFieldState<String>> stateKey = GlobalKey<FormFieldState<String>>();
final GlobalKey<FormState> formKey = GlobalKey<FormState>(); final GlobalKey<FormState> formKey = GlobalKey<FormState>();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment