diff --git a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart index 785423bfc7..fa6077dac4 100644 --- a/packages/flutter/lib/src/rendering/list_wheel_viewport.dart +++ b/packages/flutter/lib/src/rendering/list_wheel_viewport.dart @@ -824,9 +824,10 @@ class RenderListWheelViewport @override void dispose() { _clipRectLayer.layer = null; + _childOpacityLayerHandler.layer = null; super.dispose(); } - + final LayerHandle _childOpacityLayerHandler = LayerHandle(); /// Paints all children visible in the current viewport. void _paintVisibleChildren(PaintingContext context, Offset offset) { // The magnifier cannot be turned off if the opacity is less than 1.0. @@ -837,7 +838,7 @@ class RenderListWheelViewport // In order to reduce the number of opacity layers, we first paint all // 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: true); diff --git a/packages/flutter/test/cupertino/picker_test.dart b/packages/flutter/test/cupertino/picker_test.dart index 2522ada1c8..656839c53e 100644 --- a/packages/flutter/test/cupertino/picker_test.dart +++ b/packages/flutter/test/cupertino/picker_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import '../rendering/rendering_tester.dart'; @@ -18,7 +19,7 @@ class SpyFixedExtentScrollController extends FixedExtentScrollController { } void main() { - testWidgets('Picker respects theme styling', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Picker respects theme styling', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Align( @@ -57,7 +58,7 @@ void main() { group('layout', () { // 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) { return Directionality( textDirection: TextDirection.ltr, @@ -80,9 +81,9 @@ void main() { 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); - + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -127,7 +128,7 @@ void main() { }); }); - testWidgets('picker dark mode', (WidgetTester tester) async { + testWidgetsWithLeakTracking('picker dark mode', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.light), @@ -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..rect(color: const Color(0xFF654321))); - }); + },leakTrackingTestConfig: LeakTrackingTestConfig.debugNotDisposed()); - testWidgets('picker selectionOverlay', (WidgetTester tester) async { + testWidgetsWithLeakTracking('picker selectionOverlay', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.light), @@ -202,7 +203,7 @@ void main() { 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( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.light), @@ -226,7 +227,7 @@ void main() { }); group('scroll', () { - testWidgets( + testWidgetsWithLeakTracking( 'scrolling calls onSelectedItemChanged and triggers haptic feedback', (WidgetTester tester) async { final List selectedItems = []; @@ -280,7 +281,7 @@ void main() { variant: TargetPlatformVariant.only(TargetPlatform.iOS), ); - testWidgets( + testWidgetsWithLeakTracking( 'do not trigger haptic effects on non-iOS devices', (WidgetTester tester) async { final List selectedItems = []; @@ -317,8 +318,9 @@ void main() { 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); + addTearDown(controller.dispose); final List selectedItems = []; await tester.pumpWidget( @@ -371,9 +373,10 @@ void main() { expect(selectedItems, [9]); }, variant: const TargetPlatformVariant({ 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 = FixedExtentScrollController(initialItem: 10); + addTearDown(controller.dispose); final List selectedItems = []; await tester.pumpWidget( @@ -433,7 +436,7 @@ void main() { }, variant: const TargetPlatformVariant({ 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) { return MaterialApp( theme: ThemeData(brightness: brightness), @@ -474,7 +477,7 @@ void main() { }); group('CupertinoPickerDefaultSelectionOverlay', () { - testWidgets('should be using directional decoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('should be using directional decoration', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( theme: const CupertinoThemeData(brightness: Brightness.light), @@ -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(); + addTearDown(controller.dispose); expect(controller.hasListeners, false); expect(controller.positions.length, 0); @@ -528,7 +532,8 @@ void main() { 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 final List children = List.generate(100, (int index) => index); diff --git a/packages/flutter/test/cupertino/text_form_field_row_test.dart b/packages/flutter/test/cupertino/text_form_field_row_test.dart index 9ddff783b6..366c7af306 100644 --- a/packages/flutter/test/cupertino/text_form_field_row_test.dart +++ b/packages/flutter/test/cupertino/text_form_field_row_test.dart @@ -5,9 +5,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; 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; await tester.pumpWidget( @@ -27,7 +28,7 @@ void main() { 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(); await tester.pumpWidget( @@ -47,7 +48,7 @@ void main() { 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; await tester.pumpWidget( @@ -67,7 +68,7 @@ void main() { 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( CupertinoApp( home: Center( @@ -85,7 +86,7 @@ void main() { 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() {} await tester.pumpWidget( @@ -105,7 +106,7 @@ void main() { 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 cursorHeight = 6.28; const Radius cursorRadius = Radius.circular(2); @@ -133,7 +134,7 @@ void main() { expect(textFieldWidget.cursorColor, cursorColor); }); - testWidgets('onFieldSubmit callbacks are called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onFieldSubmit callbacks are called', (WidgetTester tester) async { bool called = false; await tester.pumpWidget( @@ -154,7 +155,7 @@ void main() { expect(called, true); }); - testWidgets('onChanged callbacks are called', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onChanged callbacks are called', (WidgetTester tester) async { late String value; await tester.pumpWidget( @@ -174,7 +175,7 @@ void main() { expect(value, 'Soup'); }); - testWidgets('autovalidateMode is passed to super', (WidgetTester tester) async { + testWidgetsWithLeakTracking('autovalidateMode is passed to super', (WidgetTester tester) async { int validateCalled = 0; await tester.pumpWidget( @@ -197,7 +198,7 @@ void main() { 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; await tester.pumpWidget( @@ -221,7 +222,7 @@ void main() { 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( CupertinoApp( home: Center( @@ -262,7 +263,7 @@ void main() { expect(renderEditable, paintsExactlyCountTimes(#drawRect, 0)); }, 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; await tester.pumpWidget( CupertinoApp( @@ -288,7 +289,7 @@ void main() { }); // 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( home: Center( child: CupertinoTextFormFieldRow( @@ -307,7 +308,7 @@ void main() { }); // 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( home: Center( child: CupertinoTextFormFieldRow( @@ -326,7 +327,7 @@ void main() { 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; late FormFieldState state; @@ -352,7 +353,7 @@ void main() { expect(called, true); }); - testWidgets('autofillHints is passed to super', (WidgetTester tester) async { + testWidgetsWithLeakTracking('autofillHints is passed to super', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -368,7 +369,7 @@ void main() { expect(widget.autofillHints, equals(const [AutofillHints.countryName])); }); - testWidgets('autovalidateMode is passed to super', (WidgetTester tester) async { + testWidgetsWithLeakTracking('autovalidateMode is passed to super', (WidgetTester tester) async { int validateCalled = 0; await tester.pumpWidget( @@ -391,7 +392,7 @@ void main() { 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( CupertinoApp( home: Center( @@ -411,9 +412,9 @@ void main() { 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: ''); - + addTearDown(controller.dispose); await tester.pumpWidget( CupertinoApp( home: Center( @@ -439,7 +440,7 @@ void main() { expect(errorText.data, 'Error'); }); - testWidgets('Shows prefix', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Shows prefix', (WidgetTester tester) async { await tester.pumpWidget( CupertinoApp( home: Center( @@ -457,7 +458,7 @@ void main() { 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( CupertinoApp( home: Center( @@ -491,7 +492,8 @@ void main() { 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. final GlobalKey> stateKey = GlobalKey>(); final GlobalKey formKey = GlobalKey();