diff --git a/packages/flutter/test/widgets/scroll_interaction_test.dart b/packages/flutter/test/widgets/scroll_interaction_test.dart index f0dd0c3b0a916..06a1735d06e36 100644 --- a/packages/flutter/test/widgets/scroll_interaction_test.dart +++ b/packages/flutter/test/widgets/scroll_interaction_test.dart @@ -4,9 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('Scroll flings twice in a row does not crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll flings twice in a row does not crash', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/widgets/scroll_notification_test.dart b/packages/flutter/test/widgets/scroll_notification_test.dart index 0493f8ab25231..d1b1c7f6d71d8 100644 --- a/packages/flutter/test/widgets/scroll_notification_test.dart +++ b/packages/flutter/test/widgets/scroll_notification_test.dart @@ -5,9 +5,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('ScrollMetricsNotification test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollMetricsNotification test', (WidgetTester tester) async { final List events = []; Widget buildFrame(double height) { return NotificationListener( @@ -62,7 +63,7 @@ void main() { expect(events.length, 0); }); - testWidgets('Scroll notification basics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll notification basics', (WidgetTester tester) async { late ScrollNotification notification; await tester.pumpWidget(NotificationListener( @@ -103,7 +104,7 @@ void main() { expect(end.dragDetails!.velocity, equals(Velocity.zero)); }); - testWidgets('Scroll notification depth', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll notification depth', (WidgetTester tester) async { final List depth0Types = []; final List depth1Types = []; final List depth0Values = []; @@ -158,7 +159,7 @@ void main() { expect(depth1Values, equals([1, 1, 1, 1, 1])); }); - testWidgets('ScrollNotifications bubble past Scaffold Material', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollNotifications bubble past Scaffold Material', (WidgetTester tester) async { final List notificationTypes = []; await tester.pumpWidget( @@ -206,7 +207,7 @@ void main() { expect(notificationTypes, equals(types)); }); - testWidgets('ScrollNotificationObserver', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollNotificationObserver', (WidgetTester tester) async { late ScrollNotificationObserverState observer; ScrollNotification? notification; diff --git a/packages/flutter/test/widgets/scroll_physics_test.dart b/packages/flutter/test/widgets/scroll_physics_test.dart index 6f209bc12bf7e..6185ff15e7111 100644 --- a/packages/flutter/test/widgets/scroll_physics_test.dart +++ b/packages/flutter/test/widgets/scroll_physics_test.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; class TestScrollPhysics extends ScrollPhysics { const TestScrollPhysics({ @@ -339,7 +340,7 @@ FlutterError } }); - testWidgets('PageScrollPhysics work with NestedScrollView', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PageScrollPhysics work with NestedScrollView', (WidgetTester tester) async { // Regression test for: https://github.com/flutter/flutter/issues/47850 await tester.pumpWidget(Material( child: Directionality( diff --git a/packages/flutter/test/widgets/scroll_position_test.dart b/packages/flutter/test/widgets/scroll_position_test.dart index 4ada8e22f3fee..8d6f0648329dd 100644 --- a/packages/flutter/test/widgets/scroll_position_test.dart +++ b/packages/flutter/test/widgets/scroll_position_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; ScrollController _controller = ScrollController( initialScrollOffset: 110.0, @@ -140,7 +141,7 @@ Future performTest(WidgetTester tester, bool maintainState) async { } void main() { - testWidgets("ScrollPosition jumpTo() doesn't call notifyListeners twice", (WidgetTester tester) async { + testWidgetsWithLeakTracking("ScrollPosition jumpTo() doesn't call notifyListeners twice", (WidgetTester tester) async { int count = 0; await tester.pumpWidget(MaterialApp( home: ListView.builder( @@ -159,15 +160,22 @@ void main() { expect(count, 1); }); - testWidgets('whether we remember our scroll position', (WidgetTester tester) async { + testWidgetsWithLeakTracking('whether we remember our scroll position', (WidgetTester tester) async { await performTest(tester, true); await performTest(tester, false); }); - testWidgets('scroll alignment is honored by ensureVisible', (WidgetTester tester) async { + testWidgetsWithLeakTracking('scroll alignment is honored by ensureVisible', (WidgetTester tester) async { final List items = List.generate(11, (int index) => index).toList(); final List nodes = List.generate(11, (int index) => FocusNode(debugLabel: 'Item ${index + 1}')).toList(); + addTearDown(() { + for (final FocusNode node in nodes) { + node.dispose(); + } + }); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + await tester.pumpWidget( MaterialApp( home: ListView( @@ -226,7 +234,7 @@ void main() { expect(controller.position.pixels, equals(0.0)); }); - testWidgets('jumpTo recommends deferred loading', (WidgetTester tester) async { + testWidgetsWithLeakTracking('jumpTo recommends deferred loading', (WidgetTester tester) async { int loadedWithDeferral = 0; int buildCount = 0; const double height = 500; diff --git a/packages/flutter/test/widgets/scroll_view_test.dart b/packages/flutter/test/widgets/scroll_view_test.dart index be22bf40741c6..a957e71560986 100644 --- a/packages/flutter/test/widgets/scroll_view_test.dart +++ b/packages/flutter/test/widgets/scroll_view_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show LogicalKeyboardKey; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'states.dart'; @@ -68,7 +69,7 @@ Widget primaryScrollControllerBoilerplate({ required Widget child, required Scro } void main() { - testWidgets('ListView control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView control test', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -110,8 +111,13 @@ void main() { log.clear(); }); - testWidgets('ListView dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView( @@ -143,7 +149,7 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView.builder supports null items', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder supports null items', (WidgetTester tester) async { await tester.pumpWidget(textFieldBoilerplate( child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -163,7 +169,7 @@ void main() { expect(find.text('item'), findsNWidgets(5)); }); - testWidgets('ListView.builder supports null items', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder supports null items', (WidgetTester tester) async { await tester.pumpWidget(textFieldBoilerplate( child: ListView.builder( itemCount: 42, @@ -180,11 +186,14 @@ void main() { expect(find.text('item'), findsNWidgets(5)); }); - testWidgets('PageView supports null items in itemBuilder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PageView supports null items in itemBuilder', (WidgetTester tester) async { + final PageController controller = PageController(viewportFraction: 1 / 5); + addTearDown(controller.dispose); + await tester.pumpWidget(textFieldBoilerplate( child: PageView.builder( itemCount: 5, - controller: PageController(viewportFraction: 1/5), + controller: controller, itemBuilder: (BuildContext context, int index) { if (index == 2) { return null; @@ -198,7 +207,7 @@ void main() { expect(find.text('item'), findsNWidgets(2)); }); - testWidgets('ListView.separated supports null items in itemBuilder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.separated supports null items in itemBuilder', (WidgetTester tester) async { await tester.pumpWidget(textFieldBoilerplate( child: ListView.separated( itemCount: 42, @@ -219,8 +228,13 @@ void main() { expect(find.text('separator'), findsNWidgets(5)); }); - testWidgets('ListView.builder dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.builder( @@ -253,8 +267,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('ListView.custom dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.custom dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.custom( @@ -289,8 +308,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('ListView.separated dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.separated dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.separated( @@ -324,8 +348,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView( @@ -358,8 +387,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView.builder dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.builder( @@ -393,8 +427,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView.count dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.count( @@ -427,8 +466,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView.extent dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.extent dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.extent( @@ -461,8 +505,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('GridView.custom dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.custom dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.custom( @@ -498,8 +547,13 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('ListView dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView( @@ -530,8 +584,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('ListView.builder dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.builder( @@ -563,8 +622,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('ListView.custom dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.custom dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.custom( @@ -598,8 +662,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('ListView.separated dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.separated dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: ListView.separated( @@ -632,8 +701,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('GridView dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView( @@ -665,8 +739,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('GridView.builder dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.builder( @@ -699,8 +778,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('GridView.count dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.count( @@ -732,8 +816,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('GridView.extent dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.extent dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.extent( @@ -765,8 +854,13 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('GridView.custom dismiss keyboard manual test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.custom dismiss keyboard manual test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: GridView.custom( @@ -801,7 +895,7 @@ void main() { expect(textField.focusNode!.hasFocus, isTrue); }); - testWidgets('ListView restart ballistic activity out of range', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView restart ballistic activity out of range', (WidgetTester tester) async { Widget buildListView(int n) { return Directionality( textDirection: TextDirection.ltr, @@ -831,7 +925,7 @@ void main() { expect(viewport.offset.pixels, equals(2400.0)); }); - testWidgets('CustomScrollView control test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CustomScrollView control test', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( @@ -879,8 +973,13 @@ void main() { log.clear(); }); - testWidgets('CustomScrollView dismiss keyboard onDrag test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CustomScrollView dismiss keyboard onDrag test', (WidgetTester tester) async { final List focusNodes = List.generate(50, (int i) => FocusNode()); + addTearDown(() { + for (final FocusNode node in focusNodes) { + node.dispose(); + } + }); await tester.pumpWidget(textFieldBoilerplate( child: CustomScrollView( @@ -918,9 +1017,10 @@ void main() { expect(textField.focusNode!.hasFocus, isFalse); }); - testWidgets('Can jumpTo during drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can jumpTo during drag', (WidgetTester tester) async { final List log = []; final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( @@ -977,23 +1077,26 @@ void main() { }); test('PrimaryScrollController.automaticallyInheritOnPlatforms defaults to all mobile platforms', (){ - final PrimaryScrollController primaryScrollController = PrimaryScrollController( - controller: ScrollController(), - child: const SizedBox(), - ); - expect( - primaryScrollController.automaticallyInheritForPlatforms, - TargetPlatformVariant.mobile().values, - ); + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + final PrimaryScrollController primaryScrollController = PrimaryScrollController( + controller: controller, + child: const SizedBox(), + ); + expect( + primaryScrollController.automaticallyInheritForPlatforms, + TargetPlatformVariant.mobile().values, + ); }); - testWidgets('Vertical CustomScrollViews are not primary by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical CustomScrollViews are not primary by default', (WidgetTester tester) async { const CustomScrollView view = CustomScrollView(); expect(view.primary, isNull); }); - testWidgets('Vertical CustomScrollViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical CustomScrollViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: const CustomScrollView(), controller: controller, @@ -1001,8 +1104,9 @@ void main() { expect(controller.hasClients, isTrue); }, variant: TargetPlatformVariant.mobile()); - testWidgets("Vertical CustomScrollViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Vertical CustomScrollViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: const CustomScrollView(), controller: controller, @@ -1010,13 +1114,14 @@ void main() { expect(controller.hasClients, isFalse); }, variant: TargetPlatformVariant.desktop()); - testWidgets('Vertical ListViews are not primary by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical ListViews are not primary by default', (WidgetTester tester) async { final ListView view = ListView(); expect(view.primary, isNull); }); - testWidgets('Vertical ListViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical ListViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: ListView(), controller: controller, @@ -1024,8 +1129,9 @@ void main() { expect(controller.hasClients, isTrue); }, variant: TargetPlatformVariant.mobile()); - testWidgets("Vertical ListViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Vertical ListViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: ListView(), controller: controller, @@ -1033,13 +1139,14 @@ void main() { expect(controller.hasClients, isFalse); }, variant: TargetPlatformVariant.desktop()); - testWidgets('Vertical GridViews are not primary by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical GridViews are not primary by default', (WidgetTester tester) async { final GridView view = GridView.count(crossAxisCount: 1); expect(view.primary, isNull); }); - testWidgets('Vertical GridViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical GridViews use PrimaryScrollController by default on mobile', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: GridView.count(crossAxisCount: 1), controller: controller, @@ -1047,8 +1154,9 @@ void main() { expect(controller.hasClients, isTrue); }, variant: TargetPlatformVariant.mobile()); - testWidgets("Vertical GridViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Vertical GridViews don't use PrimaryScrollController by default on desktop", (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: GridView.count(crossAxisCount: 1), controller: controller, @@ -1056,79 +1164,98 @@ void main() { expect(controller.hasClients, isFalse); }, variant: TargetPlatformVariant.desktop()); - testWidgets('Horizontal CustomScrollViews are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('Horizontal CustomScrollViews are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: CustomScrollView( scrollDirection: Axis.horizontal, - controller: ScrollController(), + controller: controller2, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('Horizontal ListViews are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('Horizontal ListViews are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: ListView( scrollDirection: Axis.horizontal, - controller: ScrollController(), + controller: controller2, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('Horizontal GridViews are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('Horizontal GridViews are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: GridView.count( scrollDirection: Axis.horizontal, - controller: ScrollController(), + controller: controller2, crossAxisCount: 1, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('CustomScrollViews with controllers are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('CustomScrollViews with controllers are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: CustomScrollView( - controller: ScrollController(), + controller: controller2, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('ListViews with controllers are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('ListViews with controllers are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: ListView( - controller: ScrollController(), + controller: controller2, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('GridViews with controllers are non-primary by default', (WidgetTester tester) async { - final ScrollController controller = ScrollController(); + testWidgetsWithLeakTracking('GridViews with controllers are non-primary by default', (WidgetTester tester) async { + final ScrollController controller1 = ScrollController(); + addTearDown(controller1.dispose); + final ScrollController controller2 = ScrollController(); + addTearDown(controller2.dispose); await tester.pumpWidget(primaryScrollControllerBoilerplate( child: GridView.count( - controller: ScrollController(), + controller: controller2, crossAxisCount: 1, ), - controller: controller, + controller: controller1, )); - expect(controller.hasClients, isFalse); + expect(controller1.hasClients, isFalse); }); - testWidgets('CustomScrollView sets PrimaryScrollController when primary', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CustomScrollView sets PrimaryScrollController when primary', (WidgetTester tester) async { final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1142,8 +1269,9 @@ void main() { expect(scrollable.controller, primaryScrollController); }); - testWidgets('ListView sets PrimaryScrollController when primary', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView sets PrimaryScrollController when primary', (WidgetTester tester) async { final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1157,8 +1285,9 @@ void main() { expect(scrollable.controller, primaryScrollController); }); - testWidgets('GridView sets PrimaryScrollController when primary', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView sets PrimaryScrollController when primary', (WidgetTester tester) async { final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1172,9 +1301,10 @@ void main() { expect(scrollable.controller, primaryScrollController); }); - testWidgets('Nested scrollables have a null PrimaryScrollController', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Nested scrollables have a null PrimaryScrollController', (WidgetTester tester) async { const Key innerKey = Key('inner'); final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1202,27 +1332,27 @@ void main() { expect(innerScrollable.controller, isNull); }); - testWidgets('Primary ListViews are always scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Primary ListViews are always scrollable', (WidgetTester tester) async { final ListView view = ListView(primary: true); expect(view.physics, isA()); }); - testWidgets('Non-primary ListViews are not always scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Non-primary ListViews are not always scrollable', (WidgetTester tester) async { final ListView view = ListView(primary: false); expect(view.physics, isNot(isA())); }); - testWidgets('Defaulting-to-primary ListViews are always scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Defaulting-to-primary ListViews are always scrollable', (WidgetTester tester) async { final ListView view = ListView(); expect(view.physics, isA()); }); - testWidgets('Defaulting-to-not-primary ListViews are not always scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Defaulting-to-not-primary ListViews are not always scrollable', (WidgetTester tester) async { final ListView view = ListView(scrollDirection: Axis.horizontal); expect(view.physics, isNot(isA())); }); - testWidgets('primary:true leads to scrolling', (WidgetTester tester) async { + testWidgetsWithLeakTracking('primary:true leads to scrolling', (WidgetTester tester) async { bool scrolled = false; await tester.pumpWidget( Directionality( @@ -1242,7 +1372,7 @@ void main() { expect(scrolled, isTrue); }); - testWidgets('primary:false leads to no scrolling', (WidgetTester tester) async { + testWidgetsWithLeakTracking('primary:false leads to no scrolling', (WidgetTester tester) async { bool scrolled = false; await tester.pumpWidget( Directionality( @@ -1262,7 +1392,7 @@ void main() { expect(scrolled, isFalse); }); - testWidgets('physics:AlwaysScrollableScrollPhysics actually overrides primary:false default behavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('physics:AlwaysScrollableScrollPhysics actually overrides primary:false default behavior', (WidgetTester tester) async { bool scrolled = false; await tester.pumpWidget( Directionality( @@ -1283,7 +1413,7 @@ void main() { expect(scrolled, isTrue); }); - testWidgets('physics:ScrollPhysics actually overrides primary:true default behavior', (WidgetTester tester) async { + testWidgetsWithLeakTracking('physics:ScrollPhysics actually overrides primary:true default behavior', (WidgetTester tester) async { bool scrolled = false; await tester.pumpWidget( Directionality( @@ -1304,7 +1434,7 @@ void main() { expect(scrolled, isFalse); }); - testWidgets('separatorBuilder must return something', (WidgetTester tester) async { + testWidgetsWithLeakTracking('separatorBuilder must return something', (WidgetTester tester) async { const List listOfValues = ['ALPHA', 'BETA', 'GAMMA', 'DELTA']; Widget buildFrame(Widget firstSeparator) { @@ -1332,7 +1462,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('when itemBuilder throws, creates Error Widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('when itemBuilder throws, creates Error Widget', (WidgetTester tester) async { const List listOfValues = ['ALPHA', 'BETA', 'GAMMA', 'DELTA']; Widget buildFrame(bool throwOnFirstItem) { @@ -1363,7 +1493,7 @@ void main() { expect(finder, findsOneWidget); }); - testWidgets('when separatorBuilder throws, creates ErrorWidget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('when separatorBuilder throws, creates ErrorWidget', (WidgetTester tester) async { const List listOfValues = ['ALPHA', 'BETA', 'GAMMA', 'DELTA']; const Key key = Key('list'); @@ -1399,14 +1529,14 @@ void main() { expect(finder, findsOneWidget); }); - testWidgets('ListView asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { expect(() => ListView( itemExtent: 100, prototypeItem: const SizedBox(), ), throwsAssertionError); }); - testWidgets('ListView.builder asserts on negative childCount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder asserts on negative childCount', (WidgetTester tester) async { expect(() => ListView.builder( itemBuilder: (BuildContext context, int index) { return const SizedBox(); @@ -1415,7 +1545,7 @@ void main() { ), throwsAssertionError); }); - testWidgets('ListView.builder asserts on negative semanticChildCount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder asserts on negative semanticChildCount', (WidgetTester tester) async { expect(() => ListView.builder( itemBuilder: (BuildContext context, int index) { return const SizedBox(); @@ -1425,7 +1555,7 @@ void main() { ), throwsAssertionError); }); - testWidgets('ListView.builder asserts on nonsensical childCount/semanticChildCount', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder asserts on nonsensical childCount/semanticChildCount', (WidgetTester tester) async { expect(() => ListView.builder( itemBuilder: (BuildContext context, int index) { return const SizedBox(); @@ -1435,7 +1565,7 @@ void main() { ), throwsAssertionError); }); - testWidgets('ListView.builder asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { expect(() => ListView.builder( itemBuilder: (BuildContext context, int index) { return const SizedBox(); @@ -1445,7 +1575,7 @@ void main() { ), throwsAssertionError); }); - testWidgets('ListView.custom asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.custom asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async { expect(() => ListView.custom( childrenDelegate: SliverChildBuilderDelegate( (BuildContext context, int index) { @@ -1457,7 +1587,7 @@ void main() { ), throwsAssertionError); }); - testWidgets('PrimaryScrollController provides fallback ScrollActions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PrimaryScrollController provides fallback ScrollActions', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: CustomScrollView( @@ -1501,7 +1631,7 @@ void main() { ); }); - testWidgets('Fallback ScrollActions handle too many positions with error message', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Fallback ScrollActions handle too many positions with error message', (WidgetTester tester) async { Widget getScrollView() { return SizedBox( width: 400.0, @@ -1550,7 +1680,7 @@ void main() { ); }); - testWidgets('if itemExtent is non-null, children have same extent in the scroll direction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('if itemExtent is non-null, children have same extent in the scroll direction', (WidgetTester tester) async { final List numbers = [0,1,2]; await tester.pumpWidget( @@ -1588,7 +1718,7 @@ void main() { expect(item2Height, 30.0); }); - testWidgets('if prototypeItem is non-null, children have same extent in the scroll direction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('if prototypeItem is non-null, children have same extent in the scroll direction', (WidgetTester tester) async { final List numbers = [0,1,2]; await tester.pumpWidget( diff --git a/packages/flutter/test/widgets/scrollable_animations_test.dart b/packages/flutter/test/widgets/scrollable_animations_test.dart index b055f6a635fe8..041d2496561e5 100644 --- a/packages/flutter/test/widgets/scrollable_animations_test.dart +++ b/packages/flutter/test/widgets/scrollable_animations_test.dart @@ -5,10 +5,12 @@ import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('Does not animate if already at target position', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not animate if already at target position', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -27,8 +29,9 @@ void main() { expect(controller.position.pixels, currentPosition); }); - testWidgets('Does not animate if already at target position within tolerance', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not animate if already at target position within tolerance', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -50,8 +53,9 @@ void main() { expect(controller.position.pixels, targetPosition); }); - testWidgets('Animates if going to a position outside of tolerance', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Animates if going to a position outside of tolerance', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/widgets/scrollable_dispose_test.dart b/packages/flutter/test/widgets/scrollable_dispose_test.dart index 79eaf6bc86196..90eec2250c3fa 100644 --- a/packages/flutter/test/widgets/scrollable_dispose_test.dart +++ b/packages/flutter/test/widgets/scrollable_dispose_test.dart @@ -4,11 +4,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'test_widgets.dart'; void main() { - testWidgets('simultaneously dispose a widget and end the scroll animation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('simultaneously dispose a widget and end the scroll animation', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -26,10 +27,11 @@ void main() { await tester.pump(const Duration(hours: 5)); }); - testWidgets('Disposing a (nested) Scrollable while holding in overscroll does not crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Disposing a (nested) Scrollable while holding in overscroll does not crash', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/27707. final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final Key outerContainer = GlobalKey(); await tester.pumpWidget( diff --git a/packages/flutter/test/widgets/scrollable_fling_test.dart b/packages/flutter/test/widgets/scrollable_fling_test.dart index 3d521377007b1..29dc51b0933bc 100644 --- a/packages/flutter/test/widgets/scrollable_fling_test.dart +++ b/packages/flutter/test/widgets/scrollable_fling_test.dart @@ -5,6 +5,7 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; const TextStyle testFont = TextStyle( color: Color(0xFF00FF00), @@ -31,7 +32,7 @@ Future pumpTest(WidgetTester tester, TargetPlatform platform) async { const double dragOffset = 213.82; void main() { - testWidgets('Flings on different platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Flings on different platforms', (WidgetTester tester) async { double getCurrentOffset() { return tester.state(find.byType(Scrollable)).position.pixels; } @@ -96,7 +97,7 @@ void main() { expect(linuxResult, equals(androidResult)); }); - testWidgets('fling and tap to stop', (WidgetTester tester) async { + testWidgetsWithLeakTracking('fling and tap to stop', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( Directionality( @@ -126,7 +127,7 @@ void main() { expect(log, equals(['tap 21', 'tap 35'])); }); - testWidgets('fling and wait and tap', (WidgetTester tester) async { + testWidgetsWithLeakTracking('fling and wait and tap', (WidgetTester tester) async { final List log = []; await tester.pumpWidget( Directionality( diff --git a/packages/flutter/test/widgets/scrollable_grid_test.dart b/packages/flutter/test/widgets/scrollable_grid_test.dart index 86c4dfaeb20a1..0f404f4188b93 100644 --- a/packages/flutter/test/widgets/scrollable_grid_test.dart +++ b/packages/flutter/test/widgets/scrollable_grid_test.dart @@ -4,9 +4,10 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { - testWidgets('GridView default control', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView default control', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -20,7 +21,7 @@ void main() { }); // Tests https://github.com/flutter/flutter/issues/5522 - testWidgets('GridView displays correct children with nonzero padding', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView displays correct children with nonzero padding', (WidgetTester tester) async { const EdgeInsets padding = EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0); final Widget testWidget = Directionality( @@ -76,7 +77,7 @@ void main() { expect(find.text('4'), findsNothing); }); - testWidgets('GridView.count() fixed itemExtent, scroll to end, append, scroll', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count() fixed itemExtent, scroll to end, append, scroll', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/9506 Widget buildFrame(int itemCount) { return Directionality( diff --git a/packages/flutter/test/widgets/scrollable_helpers_test.dart b/packages/flutter/test/widgets/scrollable_helpers_test.dart index 17b623dc09f61..f693442e5f14e 100644 --- a/packages/flutter/test/widgets/scrollable_helpers_test.dart +++ b/packages/flutter/test/widgets/scrollable_helpers_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; final LogicalKeyboardKey modifierKey = defaultTargetPlatform == TargetPlatform.macOS ? LogicalKeyboardKey.metaLeft @@ -13,8 +14,9 @@ final LogicalKeyboardKey modifierKey = defaultTargetPlatform == TargetPlatform.m void main() { group('ScrollableDetails', (){ - final ScrollController controller = ScrollController(); test('copyWith / == / hashCode', () { + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final ScrollableDetails details = ScrollableDetails( direction: AxisDirection.down, controller: controller, @@ -42,6 +44,8 @@ void main() { }); test('toString', (){ + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); const ScrollableDetails bareDetails = ScrollableDetails( direction: AxisDirection.right, ); @@ -86,8 +90,9 @@ void main() { }); }); - testWidgets("Keyboard scrolling doesn't happen if scroll physics are set to NeverScrollableScrollPhysics", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Keyboard scrolling doesn't happen if scroll physics are set to NeverScrollableScrollPhysics", (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -152,8 +157,9 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Vertical scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Vertical scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -223,8 +229,9 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Horizontal scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Horizontal scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -283,8 +290,9 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Horizontal scrollables are scrolled the correct direction in RTL locales.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Horizontal scrollables are scrolled the correct direction in RTL locales.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -346,9 +354,11 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Reversed vertical scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Reversed vertical scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(debugLabel: 'SizedBox'); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -420,9 +430,11 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Reversed horizontal scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Reversed horizontal scrollables are scrolled when activated via keyboard.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(debugLabel: 'SizedBox'); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( theme: ThemeData(platform: TargetPlatform.fuchsia), @@ -479,8 +491,9 @@ void main() { await tester.pumpAndSettle(); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Custom scrollables with a center sliver are scrolled when activated via keyboard.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Custom scrollables with a center sliver are scrolled when activated via keyboard.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final List items = List.generate(20, (int index) => 'Item $index'); await tester.pumpWidget( MaterialApp( @@ -550,7 +563,7 @@ void main() { ); }, variant: KeySimulatorTransitModeVariant.all()); - testWidgets('Can scroll using intents only', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can scroll using intents only', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: ListView( diff --git a/packages/flutter/test/widgets/scrollable_in_overlay_test.dart b/packages/flutter/test/widgets/scrollable_in_overlay_test.dart index c28f535b8625b..2207c762bcf7a 100644 --- a/packages/flutter/test/widgets/scrollable_in_overlay_test.dart +++ b/packages/flutter/test/widgets/scrollable_in_overlay_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { test('Can dispose ScrollPosition when hasPixels is false', () { @@ -18,9 +19,10 @@ void main() { position.dispose(); // Should not throw/assert. }); - testWidgets('scrollable in hidden overlay does not crash when unhidden', (WidgetTester tester) async { + testWidgetsWithLeakTracking('scrollable in hidden overlay does not crash when unhidden', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/44269. final TabController controller = TabController(vsync: const TestVSync(), length: 1); + addTearDown(controller.dispose); final OverlayEntry entry1 = OverlayEntry( maintainState: true, diff --git a/packages/flutter/test/widgets/scrollable_list_hit_testing_test.dart b/packages/flutter/test/widgets/scrollable_list_hit_testing_test.dart index e956f34bd9a09..15bef69c25d0b 100644 --- a/packages/flutter/test/widgets/scrollable_list_hit_testing_test.dart +++ b/packages/flutter/test/widgets/scrollable_list_hit_testing_test.dart @@ -5,11 +5,12 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; const List items = [0, 1, 2, 3, 4, 5]; void main() { - testWidgets('Tap item after scroll - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap item after scroll - horizontal', (WidgetTester tester) async { final List tapped = []; await tester.pumpWidget( Directionality( @@ -51,7 +52,7 @@ void main() { expect(tapped, equals([2])); }); - testWidgets('Tap item after scroll - vertical', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap item after scroll - vertical', (WidgetTester tester) async { final List tapped = []; await tester.pumpWidget( Directionality( @@ -94,7 +95,7 @@ void main() { expect(tapped, equals([1])); // the center of the third item is off-screen so it shouldn't get hit }); - testWidgets('Padding scroll anchor start', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Padding scroll anchor start', (WidgetTester tester) async { final List tapped = []; await tester.pumpWidget( @@ -126,7 +127,7 @@ void main() { expect(tapped, equals([0, 1, 1])); }); - testWidgets('Padding scroll anchor end', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Padding scroll anchor end', (WidgetTester tester) async { final List tapped = []; await tester.pumpWidget( @@ -159,7 +160,7 @@ void main() { expect(tapped, equals([0, 1, 1])); }); - testWidgets('Tap immediately following clamped overscroll', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tap immediately following clamped overscroll', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/5709 final List tapped = []; diff --git a/packages/flutter/test/widgets/scrollable_of_test.dart b/packages/flutter/test/widgets/scrollable_of_test.dart index 03ce562419b8c..1aec1979dd200 100644 --- a/packages/flutter/test/widgets/scrollable_of_test.dart +++ b/packages/flutter/test/widgets/scrollable_of_test.dart @@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; class ScrollPositionListener extends StatefulWidget { const ScrollPositionListener({ super.key, required this.child, required this.log}); @@ -123,9 +124,10 @@ class TestChildState extends State { } void main() { - testWidgets('Scrollable.of() dependent rebuilds when Scrollable position changes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollable.of() dependent rebuilds when Scrollable position changes', (WidgetTester tester) async { late String logValue; final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); // Changing the SingleChildScrollView's physics causes the // ScrollController's ScrollPosition to be rebuilt. @@ -163,7 +165,7 @@ void main() { expect(logValue, 'listener 400.0'); }); - testWidgets('Scrollable.of() is possible using ScrollNotification context', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollable.of() is possible using ScrollNotification context', (WidgetTester tester) async { late ScrollNotification notification; await tester.pumpWidget(NotificationListener( @@ -183,9 +185,11 @@ void main() { expect(Scrollable.of(notification.context!), equals(scrollableElement.state)); }); - testWidgets('Static Scrollable methods can target a specific axis', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Static Scrollable methods can target a specific axis', (WidgetTester tester) async { final TestScrollController horizontalController = TestScrollController(deferLoading: true); + addTearDown(horizontalController.dispose); final TestScrollController verticalController = TestScrollController(deferLoading: false); + addTearDown(verticalController.dispose); late final AxisDirection foundAxisDirection; late final bool foundRecommendation; @@ -218,7 +222,7 @@ void main() { expect(foundRecommendation, isTrue); }); - testWidgets('Axis targeting scrollables establishes the correct dependencies', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Axis targeting scrollables establishes the correct dependencies', (WidgetTester tester) async { final GlobalKey verticalKey = GlobalKey(); final GlobalKey childKey = GlobalKey(); @@ -237,12 +241,15 @@ void main() { expect(verticalKey.currentState!.dependenciesChanged, 1); expect(childKey.currentState!.dependenciesChanged, 1); + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + // Change the horizontal ScrollView, adding a controller await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SingleChildScrollView( scrollDirection: Axis.horizontal, - controller: ScrollController(), + controller: controller, child: TestScrollable( key: verticalKey, child: TestChild(key: childKey), diff --git a/packages/flutter/test/widgets/scrollable_restoration_test.dart b/packages/flutter/test/widgets/scrollable_restoration_test.dart index fb5e251941d32..2aac5a4148e14 100644 --- a/packages/flutter/test/widgets/scrollable_restoration_test.dart +++ b/packages/flutter/test/widgets/scrollable_restoration_test.dart @@ -5,9 +5,10 @@ import 'package:flutter/material.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('CustomScrollView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('CustomScrollView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: CustomScrollView( @@ -33,7 +34,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('ListView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListView( @@ -53,7 +54,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('ListView.builder restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.builder restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListView.builder( @@ -70,7 +71,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('ListView.separated restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.separated restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListView.separated( @@ -89,7 +90,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('ListView.custom restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListView.custom restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListView.custom( @@ -111,7 +112,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('GridView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: GridView( @@ -132,7 +133,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('GridView.builder restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.builder restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: GridView.builder( @@ -150,7 +151,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('GridView.custom restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.custom restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: GridView.custom( @@ -173,7 +174,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('GridView.count restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.count restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: GridView.count( @@ -194,7 +195,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('GridView.extent restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('GridView.extent restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: GridView.extent( @@ -215,7 +216,7 @@ void main() { await restoreScrollAndVerify(tester); }); - testWidgets('SingleChildScrollView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('SingleChildScrollView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: SingleChildScrollView( @@ -262,7 +263,7 @@ void main() { expect(tester.getTopLeft(find.text('Tile 1')), const Offset(0, -475)); }); - testWidgets('PageView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PageView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: PageView( @@ -278,7 +279,7 @@ void main() { await pageViewScrollAndRestore(tester); }); - testWidgets('PageView.builder restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PageView.builder restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: PageView.builder( @@ -294,7 +295,7 @@ void main() { await pageViewScrollAndRestore(tester); }); - testWidgets('PageView.custom restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PageView.custom restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: PageView.custom( @@ -315,7 +316,7 @@ void main() { await pageViewScrollAndRestore(tester); }); - testWidgets('ListWheelScrollView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListWheelScrollView restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListWheelScrollView( @@ -332,7 +333,7 @@ void main() { await restoreScrollAndVerify(tester, secondOffset: 542); }); - testWidgets('ListWheelScrollView.useDelegate restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ListWheelScrollView.useDelegate restoration', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListWheelScrollView.useDelegate( @@ -354,7 +355,7 @@ void main() { await restoreScrollAndVerify(tester, secondOffset: 542); }); - testWidgets('NestedScrollView restoration', (WidgetTester tester) async { + testWidgetsWithLeakTracking('NestedScrollView restoration', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: TestHarness( @@ -422,7 +423,7 @@ void main() { expect(find.text('Tile 10'), findsOneWidget); }); - testWidgets('RestorationData is flushed even if no frame is scheduled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RestorationData is flushed even if no frame is scheduled', (WidgetTester tester) async { await tester.pumpWidget( TestHarness( child: ListView( diff --git a/packages/flutter/test/widgets/scrollable_selection_test.dart b/packages/flutter/test/widgets/scrollable_selection_test.dart index ea3fa2843a3e0..fbafd52e28215 100644 --- a/packages/flutter/test/widgets/scrollable_selection_test.dart +++ b/packages/flutter/test/widgets/scrollable_selection_test.dart @@ -9,6 +9,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 'clipboard_utils.dart'; import 'keyboard_utils.dart'; @@ -36,7 +37,7 @@ void main() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null); }); - testWidgets('mouse can select multiple widgets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -74,7 +75,7 @@ void main() { await gesture.up(); }); - testWidgets('mouse can select multiple widgets - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets - horizontal', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -107,7 +108,7 @@ void main() { await gesture.up(); }); - testWidgets('mouse can select multiple widgets on double-click drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets on double-click drag', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -151,7 +152,7 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('mouse can select multiple widgets on double-click drag - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets on double-click drag - horizontal', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -190,8 +191,9 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('select to scroll forward', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll forward', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -239,8 +241,9 @@ void main() { await gesture.up(); }); - testWidgets('select to scroll works for small scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll works for small scrollable', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( theme: ThemeData(useMaterial3: false), home: SelectionArea( @@ -285,8 +288,9 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('select to scroll backward', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll backward', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -333,8 +337,9 @@ void main() { expect(paragraph3.selections[0], const TextSelection(baseOffset: 6, extentOffset: 0)); }); - testWidgets('select to scroll forward - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll forward - horizontal', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -381,8 +386,9 @@ void main() { await gesture.up(); }); - testWidgets('select to scroll backward - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll backward - horizontal', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -430,8 +436,9 @@ void main() { await gesture.up(); }); - testWidgets('preserve selection when out of view.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('preserve selection when out of view.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -477,8 +484,9 @@ void main() { expect(paragraph50.selections[0], const TextSelection(baseOffset: 2, extentOffset: 4)); }); - testWidgets('can select all non-Apple', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can select all non-Apple', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -503,8 +511,9 @@ void main() { expect(find.text('Item 13'), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.windows, TargetPlatform.linux, TargetPlatform.fuchsia })); - testWidgets('can select all - Apple', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can select all - Apple', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -529,8 +538,9 @@ void main() { expect(find.text('Item 13'), findsNothing); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('select to scroll by dragging selection handles forward', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll by dragging selection handles forward', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -586,8 +596,9 @@ void main() { await gesture.up(); }); - testWidgets('select to scroll by dragging start selection handle stops scroll when released', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll by dragging start selection handle stops scroll when released', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -640,8 +651,9 @@ void main() { expect(controller.offset, previousOffset); }); - testWidgets('select to scroll by dragging end selection handle stops scroll when released', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select to scroll by dragging end selection handle stops scroll when released', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -693,9 +705,11 @@ void main() { expect(controller.offset, previousOffset); }); - testWidgets('keyboard selection should auto scroll - vertical', (WidgetTester tester) async { + testWidgetsWithLeakTracking('keyboard selection should auto scroll - vertical', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -756,9 +770,11 @@ void main() { expect(controller.offset, 72.0); }, variant: TargetPlatformVariant.all()); - testWidgets('keyboard selection should auto scroll - vertical reversed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('keyboard selection should auto scroll - vertical reversed', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -820,9 +836,11 @@ void main() { expect(controller.offset, 72.0); }, variant: TargetPlatformVariant.all()); - testWidgets('keyboard selection should auto scroll - horizontal', (WidgetTester tester) async { + testWidgetsWithLeakTracking('keyboard selection should auto scroll - horizontal', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -866,9 +884,11 @@ void main() { expect(controller.offset, 352.0); }, variant: TargetPlatformVariant.all()); - testWidgets('keyboard selection should auto scroll - horizontal reversed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('keyboard selection should auto scroll - horizontal reversed', (WidgetTester tester) async { final FocusNode node = FocusNode(); + addTearDown(node.dispose); final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: node, @@ -922,8 +942,9 @@ void main() { }, variant: TargetPlatformVariant.all()); group('Complex cases', () { - testWidgets('selection starts outside of the scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('selection starts outside of the scrollable', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -966,9 +987,11 @@ void main() { expect(controller.offset, 1000.0); }); - testWidgets('nested scrollables keep selection alive', (WidgetTester tester) async { + testWidgetsWithLeakTracking('nested scrollables keep selection alive', (WidgetTester tester) async { final ScrollController outerController = ScrollController(); + addTearDown(outerController.dispose); final ScrollController innerController = ScrollController(); + addTearDown(innerController.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( selectionControls: materialTextSelectionControls, @@ -1030,9 +1053,11 @@ void main() { expect(innerParagraph24.selections[0], const TextSelection(baseOffset: 0, extentOffset: 2)); }); - testWidgets('can copy off screen selection - Apple', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can copy off screen selection - Apple', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: focusNode, @@ -1071,9 +1096,11 @@ void main() { expect(clipboardData['text'], 'em 0It'); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('can copy off screen selection - non-Apple', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can copy off screen selection - non-Apple', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget(MaterialApp( home: SelectionArea( focusNode: focusNode, diff --git a/packages/flutter/test/widgets/scrollable_semantics_test.dart b/packages/flutter/test/widgets/scrollable_semantics_test.dart index 5307fc7a6d73e..f9e2d1679e8df 100644 --- a/packages/flutter/test/widgets/scrollable_semantics_test.dart +++ b/packages/flutter/test/widgets/scrollable_semantics_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'semantics_tester.dart'; @@ -16,7 +17,7 @@ void main() { debugResetSemanticsIdCounter(); }); - testWidgets('scrollable exposes the correct semantic actions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('scrollable exposes the correct semantic actions', (WidgetTester tester) async { semantics = SemanticsTester(tester); await tester.pumpWidget( Directionality( @@ -42,7 +43,7 @@ void main() { semantics.dispose(); }); - testWidgets('showOnScreen works in scrollable', (WidgetTester tester) async { + testWidgetsWithLeakTracking('showOnScreen works in scrollable', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation const double kItemHeight = 40.0; @@ -57,6 +58,7 @@ void main() { final ScrollController scrollController = ScrollController( initialScrollOffset: kItemHeight / 2, ); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( @@ -80,7 +82,7 @@ void main() { semantics.dispose(); }); - testWidgets('showOnScreen works with pinned app bar and sliver list', (WidgetTester tester) async { + testWidgetsWithLeakTracking('showOnScreen works with pinned app bar and sliver list', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation const double kItemHeight = 100.0; @@ -96,6 +98,7 @@ void main() { final ScrollController scrollController = ScrollController( initialScrollOffset: kItemHeight / 2, ); + addTearDown(scrollController.dispose); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -142,7 +145,7 @@ void main() { semantics.dispose(); }); - testWidgets('showOnScreen works with pinned app bar and individual slivers', (WidgetTester tester) async { + testWidgetsWithLeakTracking('showOnScreen works with pinned app bar and individual slivers', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation const double kItemHeight = 100.0; @@ -166,6 +169,7 @@ void main() { final ScrollController scrollController = ScrollController( initialScrollOffset: 2.5 * kItemHeight, ); + addTearDown(scrollController.dispose); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -210,7 +214,7 @@ void main() { semantics.dispose(); }); - testWidgets('correct scrollProgress', (WidgetTester tester) async { + testWidgetsWithLeakTracking('correct scrollProgress', (WidgetTester tester) async { semantics = SemanticsTester(tester); await tester.pumpWidget(Directionality( @@ -253,7 +257,7 @@ void main() { semantics.dispose(); }); - testWidgets('correct scrollProgress for unbound', (WidgetTester tester) async { + testWidgetsWithLeakTracking('correct scrollProgress for unbound', (WidgetTester tester) async { semantics = SemanticsTester(tester); await tester.pumpWidget(Directionality( @@ -303,7 +307,7 @@ void main() { semantics.dispose(); }); - testWidgets('Semantics tree is populated mid-scroll', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Semantics tree is populated mid-scroll', (WidgetTester tester) async { semantics = SemanticsTester(tester); final List children = List.generate(80, (int i) => SizedBox( @@ -328,7 +332,7 @@ void main() { semantics.dispose(); }); - testWidgets('Can toggle semantics on, off, on without crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can toggle semantics on, off, on without crash', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -434,7 +438,7 @@ void main() { }); - testWidgets('brings item above leading edge to leading edge', (WidgetTester tester) async { + testWidgetsWithLeakTracking('brings item above leading edge to leading edge', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -450,7 +454,7 @@ void main() { semantics.dispose(); }); - testWidgets('brings item below trailing edge to trailing edge', (WidgetTester tester) async { + testWidgetsWithLeakTracking('brings item below trailing edge to trailing edge', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -466,7 +470,7 @@ void main() { semantics.dispose(); }); - testWidgets('does not change position of items already fully on-screen', (WidgetTester tester) async { + testWidgetsWithLeakTracking('does not change position of items already fully on-screen', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -536,10 +540,13 @@ void main() { ), ), ); + }); + tearDown(() { + scrollController.dispose(); }); - testWidgets('brings item above leading edge to leading edge', (WidgetTester tester) async { + testWidgetsWithLeakTracking('brings item above leading edge to leading edge', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -555,7 +562,7 @@ void main() { semantics.dispose(); }); - testWidgets('brings item below trailing edge to trailing edge', (WidgetTester tester) async { + testWidgetsWithLeakTracking('brings item below trailing edge to trailing edge', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -571,7 +578,7 @@ void main() { semantics.dispose(); }); - testWidgets('does not change position of items already fully on-screen', (WidgetTester tester) async { + testWidgetsWithLeakTracking('does not change position of items already fully on-screen', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation await tester.pumpWidget(widgetUnderTest); @@ -589,7 +596,7 @@ void main() { }); - testWidgets('transform of inner node from useTwoPaneSemantics scrolls correctly with nested scrollables', (WidgetTester tester) async { + testWidgetsWithLeakTracking('transform of inner node from useTwoPaneSemantics scrolls correctly with nested scrollables', (WidgetTester tester) async { semantics = SemanticsTester(tester); // enables semantics tree generation // Context: https://github.com/flutter/flutter/issues/61631 diff --git a/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart b/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart index ff54d1084e71b..8e35b357982e0 100644 --- a/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart +++ b/packages/flutter/test/widgets/scrollable_semantics_traversal_order_test.dart @@ -5,13 +5,17 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'semantics_tester.dart'; void main() { - testWidgets('Traversal Order of SliverList', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order of SliverList', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final ScrollController controller = ScrollController(initialScrollOffset: 3000.0); + addTearDown(controller.dispose); + final List listChildren = List.generate(30, (int i) { return SizedBox( height: 200.0, @@ -38,7 +42,7 @@ void main() { child: MediaQuery( data: const MediaQueryData(), child: CustomScrollView( - controller: ScrollController(initialScrollOffset: 3000.0), + controller: controller, semanticChildCount: 30, slivers: [ SliverList( @@ -182,9 +186,12 @@ void main() { semantics.dispose(); }); - testWidgets('Traversal Order of SliverFixedExtentList', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order of SliverFixedExtentList', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final ScrollController controller = ScrollController(initialScrollOffset: 3000.0); + addTearDown(controller.dispose); + final List listChildren = List.generate(30, (int i) { return SizedBox( height: 200.0, @@ -211,7 +218,7 @@ void main() { child: MediaQuery( data: const MediaQueryData(), child: CustomScrollView( - controller: ScrollController(initialScrollOffset: 3000.0), + controller: controller, slivers: [ SliverFixedExtentList( itemExtent: 200.0, @@ -321,9 +328,12 @@ void main() { semantics.dispose(); }); - testWidgets('Traversal Order of SliverGrid', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order of SliverGrid', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final ScrollController controller = ScrollController(initialScrollOffset: 1600.0); + addTearDown(controller.dispose); + final List listChildren = List.generate(30, (int i) { return SizedBox( height: 200.0, @@ -338,7 +348,7 @@ void main() { child: MediaQuery( data: const MediaQueryData(), child: CustomScrollView( - controller: ScrollController(initialScrollOffset: 1600.0), + controller: controller, slivers: [ SliverGrid.count( crossAxisCount: 2, @@ -449,9 +459,12 @@ void main() { semantics.dispose(); }); - testWidgets('Traversal Order of List of individual slivers', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order of List of individual slivers', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final ScrollController controller = ScrollController(initialScrollOffset: 3000.0); + addTearDown(controller.dispose); + final List listChildren = List.generate(30, (int i) { return SliverToBoxAdapter( child: SizedBox( @@ -480,7 +493,7 @@ void main() { child: MediaQuery( data: const MediaQueryData(), child: CustomScrollView( - controller: ScrollController(initialScrollOffset: 3000.0), + controller: controller, slivers: listChildren, ), ), @@ -585,9 +598,12 @@ void main() { semantics.dispose(); }); - testWidgets('Traversal Order of in a SingleChildScrollView', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order of in a SingleChildScrollView', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final ScrollController controller = ScrollController(initialScrollOffset: 3000.0); + addTearDown(controller.dispose); + final List listChildren = List.generate(30, (int i) { return SizedBox( height: 200.0, @@ -614,7 +630,7 @@ void main() { child: MediaQuery( data: const MediaQueryData(), child: SingleChildScrollView( - controller: ScrollController(initialScrollOffset: 3000.0), + controller: controller, child: Column( children: listChildren, ), @@ -671,7 +687,7 @@ void main() { semantics.dispose(); }); - testWidgets('Traversal Order with center child', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Traversal Order with center child', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); await tester.pumpWidget(Semantics( diff --git a/packages/flutter/test/widgets/scrollable_test.dart b/packages/flutter/test/widgets/scrollable_test.dart index c5d5b5fd014df..93efe9ff89853 100644 --- a/packages/flutter/test/widgets/scrollable_test.dart +++ b/packages/flutter/test/widgets/scrollable_test.dart @@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.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 'semantics_tester.dart'; @@ -109,7 +110,7 @@ void resetScrollOffset(WidgetTester tester) { } void main() { - testWidgets('Flings on different platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Flings on different platforms', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.android); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); expect(getScrollOffset(tester), dragOffset); @@ -145,7 +146,7 @@ void main() { expect(macOSResult, lessThan(iOSResult)); // iOS is slipperier than macOS }); - testWidgets('Holding scroll', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Holding scroll', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); await tester.drag(find.byType(Scrollable), const Offset(0.0, 200.0), touchSlopY: 0.0); expect(getScrollOffset(tester), -200.0); @@ -164,7 +165,7 @@ void main() { expect(getScrollOffset(tester), 0.0); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Repeated flings builds momentum', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Repeated flings builds momentum', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); await tester.pump(); // trigger fling @@ -177,7 +178,7 @@ void main() { expect(getScrollVelocity(tester), greaterThan(1100.0)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Repeated flings do not build momentum on Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Repeated flings do not build momentum on Android', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.android); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); await tester.pump(); // trigger fling @@ -190,7 +191,7 @@ void main() { expect(getScrollVelocity(tester), moreOrLessEquals(1000.0)); }); - testWidgets('A slower final fling does not apply carried momentum', (WidgetTester tester) async { + testWidgetsWithLeakTracking('A slower final fling does not apply carried momentum', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); await tester.pump(); // trigger fling @@ -207,7 +208,7 @@ void main() { expect(getScrollVelocity(tester), lessThan(200.0)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('No iOS/macOS momentum build with flings in opposite directions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('No iOS/macOS momentum build with flings in opposite directions', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); await tester.pump(); // trigger fling @@ -220,7 +221,7 @@ void main() { expect(getScrollVelocity(tester), -1000.0); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('No iOS/macOS momentum kept on hold gestures', (WidgetTester tester) async { + testWidgetsWithLeakTracking('No iOS/macOS momentum kept on hold gestures', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); await tester.pump(); // trigger fling @@ -233,7 +234,7 @@ void main() { expect(getScrollVelocity(tester), 0.0); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Drags creeping unaffected on Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Drags creeping unaffected on Android', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.android); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); await gesture.moveBy(const Offset(0.0, -0.5)); @@ -244,7 +245,7 @@ void main() { expect(getScrollOffset(tester), 1.5); }); - testWidgets('Drags creeping must break threshold on iOS/macOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Drags creeping must break threshold on iOS/macOS', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); await gesture.moveBy(const Offset(0.0, -0.5)); @@ -264,7 +265,7 @@ void main() { expect(getScrollOffset(tester), 0.5); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Big drag over threshold magnitude preserved on iOS/macOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Big drag over threshold magnitude preserved on iOS/macOS', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); await gesture.moveBy(const Offset(0.0, -30.0)); @@ -272,7 +273,7 @@ void main() { expect(getScrollOffset(tester), 30.0); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Slow threshold breaks are attenuated on iOS/macOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Slow threshold breaks are attenuated on iOS/macOS', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); // This is a typical 'hesitant' iOS scroll start. @@ -283,7 +284,7 @@ void main() { expect(getScrollOffset(tester), moreOrLessEquals(11.16666666666666673)); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Small continuing motion preserved on iOS/macOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Small continuing motion preserved on iOS/macOS', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); await gesture.moveBy(const Offset(0.0, -30.0)); // Break threshold. @@ -296,7 +297,7 @@ void main() { expect(getScrollOffset(tester), 31.5); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Motion stop resets threshold on iOS/macOS', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Motion stop resets threshold on iOS/macOS', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true)); await gesture.moveBy(const Offset(0.0, -30.0)); // Break threshold. @@ -319,7 +320,7 @@ void main() { expect(getScrollOffset(tester), 32.5); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS })); - testWidgets('Scroll pointer signals are handled on Fuchsia', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll pointer signals are handled on Fuchsia', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.fuchsia); final Offset scrollEventLocation = tester.getCenter(find.byType(Viewport)); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); @@ -332,7 +333,7 @@ void main() { expect(getScrollOffset(tester), 0.0); }); - testWidgets('Scroll pointer signals are handled when there is competition', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll pointer signals are handled when there is competition', (WidgetTester tester) async { // This is a regression test. When there are multiple scrollables listening // to the same event, for example when scrollables are nested, there used // to be exceptions at scrolling events. @@ -349,7 +350,7 @@ void main() { expect(getScrollOffset(tester), 0.0); }); - testWidgets('Scroll pointer signals are ignored when scrolling is disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll pointer signals are ignored when scrolling is disabled', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.fuchsia, scrollable: false); final Offset scrollEventLocation = tester.getCenter(find.byType(Viewport)); final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse); @@ -359,10 +360,12 @@ void main() { expect(getScrollOffset(tester), 0.0); }); - testWidgets('Holding scroll and Scroll pointer signal will update ScrollDirection.forward / ScrollDirection.reverse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Holding scroll and Scroll pointer signal will update ScrollDirection.forward / ScrollDirection.reverse', (WidgetTester tester) async { ScrollDirection? lastUserScrollingDirection; final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + await pumpTest(tester, TargetPlatform.fuchsia, controller: controller); controller.addListener(() { @@ -393,7 +396,7 @@ void main() { }); - testWidgets('Scrolls in correct direction when scroll axis is reversed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrolls in correct direction when scroll axis is reversed', (WidgetTester tester) async { await pumpTest(tester, TargetPlatform.fuchsia, reverse: true); final Offset scrollEventLocation = tester.getCenter(find.byType(Viewport)); @@ -405,7 +408,7 @@ void main() { expect(getScrollOffset(tester), 20.0); }); - testWidgets('Scrolls horizontally when shift is pressed by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrolls horizontally when shift is pressed by default', (WidgetTester tester) async { await pumpTest( tester, debugDefaultTargetPlatformOverride, @@ -432,7 +435,7 @@ void main() { expect(getScrollOffset(tester), 20.0); }, variant: TargetPlatformVariant.all()); - testWidgets('Scroll axis is not flipped for trackpad', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll axis is not flipped for trackpad', (WidgetTester tester) async { await pumpTest( tester, debugDefaultTargetPlatformOverride, @@ -459,7 +462,7 @@ void main() { expect(getScrollOffset(tester), 0.0); }, variant: TargetPlatformVariant.all()); - testWidgets('Scrolls horizontally when custom key is pressed', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrolls horizontally when custom key is pressed', (WidgetTester tester) async { await pumpTest( tester, debugDefaultTargetPlatformOverride, @@ -487,7 +490,7 @@ void main() { expect(getScrollOffset(tester), 20.0); }, variant: TargetPlatformVariant.all()); - testWidgets('Still scrolls horizontally when other keys are pressed at the same time', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Still scrolls horizontally when other keys are pressed at the same time', (WidgetTester tester) async { await pumpTest( tester, debugDefaultTargetPlatformOverride, @@ -536,7 +539,7 @@ void main() { ); } - testWidgets('Hold does not disable user interaction', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Hold does not disable user interaction', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/66816. await pumpTestWidget(tester, canDrag: true); final RenderIgnorePointer renderIgnorePointer = tester.renderObject( @@ -555,7 +558,7 @@ void main() { expect(renderIgnorePointer.ignoring, false); }); - testWidgets('Drag disables user interaction when recognized', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Drag disables user interaction when recognized', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/66816. await pumpTestWidget(tester, canDrag: true); final RenderIgnorePointer renderIgnorePointer = tester.renderObject( @@ -577,7 +580,7 @@ void main() { expect(renderIgnorePointer.ignoring, false); }); - testWidgets('Ballistic disables user interaction until it stops', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Ballistic disables user interaction until it stops', (WidgetTester tester) async { await pumpTestWidget(tester, canDrag: true); final RenderIgnorePointer renderIgnorePointer = tester.renderObject( find.descendant(of: find.byType(CustomScrollView), matching: find.byType(IgnorePointer)), @@ -595,11 +598,13 @@ void main() { }); }); - testWidgets('Can recommendDeferredLoadingForContext - animation', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can recommendDeferredLoadingForContext - animation', (WidgetTester tester) async { final List widgetTracker = []; int cheapWidgets = 0; int expensiveWidgets = 0; final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: ListView.builder( @@ -650,7 +655,7 @@ void main() { expect(widgetTracker.skip(17).skip(25).skip(70).every((String type) => type == 'expensive'), true); }); - testWidgets('Can recommendDeferredLoadingForContext - ballistics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can recommendDeferredLoadingForContext - ballistics', (WidgetTester tester) async { int cheapWidgets = 0; int expensiveWidgets = 0; await tester.pumpWidget(Directionality( @@ -687,7 +692,7 @@ void main() { expect(cheapWidgets, 21); }); - testWidgets('Can recommendDeferredLoadingForContext - override heuristic', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can recommendDeferredLoadingForContext - override heuristic', (WidgetTester tester) async { int cheapWidgets = 0; int expensiveWidgets = 0; await tester.pumpWidget(Directionality( @@ -731,7 +736,7 @@ void main() { expect(physics.count, 44 + 17); }); - testWidgets('Can recommendDeferredLoadingForContext - override heuristic and always return true', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Can recommendDeferredLoadingForContext - override heuristic and always return true', (WidgetTester tester) async { int cheapWidgets = 0; int expensiveWidgets = 0; await tester.pumpWidget(Directionality( @@ -772,8 +777,9 @@ void main() { expect(cheapWidgets, 61); }); - testWidgets('ensureVisible does not move PageViews', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ensureVisible does not move PageViews', (WidgetTester tester) async { final PageController controller = PageController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( @@ -855,7 +861,7 @@ void main() { expect(targetMidLeftPage1, findsOneWidget); }); - testWidgets('ensureVisible does not move TabViews', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ensureVisible does not move TabViews', (WidgetTester tester) async { final TickerProvider vsync = TestTickerProvider(); final TabController controller = TabController( length: 3, @@ -942,10 +948,13 @@ void main() { expect(targetMidLeftPage1, findsOneWidget); }); - testWidgets('PointerScroll on nested NeverScrollable ListView goes to outer Scrollable.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('PointerScroll on nested NeverScrollable ListView goes to outer Scrollable.', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/70948 final ScrollController outerController = ScrollController(); + addTearDown(outerController.dispose); final ScrollController innerController = ScrollController(); + addTearDown(innerController.dispose); + await tester.pumpWidget(MaterialApp( theme: ThemeData(useMaterial3: false), home: Scaffold( @@ -999,8 +1008,10 @@ void main() { }); // Regression test for https://github.com/flutter/flutter/issues/71949 - testWidgets('Zero offset pointer scroll should not trigger an assertion.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Zero offset pointer scroll should not trigger an assertion.', (WidgetTester tester) async { final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + Widget build(double height) { return MaterialApp( home: Scaffold( @@ -1039,7 +1050,7 @@ void main() { expect(tester.takeException(), null); }); - testWidgets('Accepts drag with unknown device kind by default', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Accepts drag with unknown device kind by default', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/90912. await tester.pumpWidget( const MaterialApp( @@ -1068,7 +1079,7 @@ void main() { await tester.pump(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS, TargetPlatform.android })); - testWidgets('Does not scroll with mouse pointer drag when behavior is configured to ignore them', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not scroll with mouse pointer drag when behavior is configured to ignore them', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride, enableMouseDrag: false); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true), kind: ui.PointerDeviceKind.mouse); @@ -1088,7 +1099,7 @@ void main() { await tester.pump(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS, TargetPlatform.android })); - testWidgets("Support updating 'ScrollBehavior.dragDevices' at runtime", (WidgetTester tester) async { + testWidgetsWithLeakTracking("Support updating 'ScrollBehavior.dragDevices' at runtime", (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/111716 Widget buildFrame(Set? dragDevices) { return MaterialApp( @@ -1122,7 +1133,7 @@ void main() { expect(getScrollOffset(tester), 200.0); }); - testWidgets('Does scroll with mouse pointer drag when behavior is not configured to ignore them', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does scroll with mouse pointer drag when behavior is not configured to ignore them', (WidgetTester tester) async { await pumpTest(tester, debugDefaultTargetPlatformOverride); final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Scrollable), warnIfMissed: true), kind: ui.PointerDeviceKind.mouse); @@ -1142,7 +1153,7 @@ void main() { await tester.pump(); }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.macOS, TargetPlatform.android })); - testWidgets('Updated content dimensions correctly reflect in semantics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Updated content dimensions correctly reflect in semantics', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/40419. final SemanticsHandle handle = tester.ensureSemantics(); final UniqueKey listView = UniqueKey(); @@ -1200,7 +1211,7 @@ void main() { handle.dispose(); }); - testWidgets('Two panel semantics is added to the sibling nodes of direct children', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Two panel semantics is added to the sibling nodes of direct children', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); final UniqueKey key = UniqueKey(); await tester.pumpWidget(MaterialApp( @@ -1245,7 +1256,7 @@ void main() { handle.dispose(); }); - testWidgets('Scroll inertia cancel event', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scroll inertia cancel event', (WidgetTester tester) async { await pumpTest(tester, null); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); expect(getScrollOffset(tester), dragOffset); @@ -1261,7 +1272,7 @@ void main() { expect(getScrollOffset(tester), closeTo(344.0642, 0.0001)); }); - testWidgets('Swapping viewports in a scrollable does not crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Swapping viewports in a scrollable does not crash', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); final GlobalKey key = GlobalKey(); final GlobalKey key1 = GlobalKey(); @@ -1270,11 +1281,13 @@ void main() { key: key, viewportBuilder: (BuildContext context, ViewportOffset position) { if (withViewPort) { + final ViewportOffset offset = ViewportOffset.zero(); + addTearDown(() => offset.dispose()); return Viewport( slivers: [ SliverToBoxAdapter(child: Semantics(key: key1, container: true, child: const Text('text1'))) ], - offset: ViewportOffset.zero(), + offset: offset, ); } return Semantics(key: key1, container: true, child: const Text('text1')); @@ -1306,7 +1319,7 @@ void main() { semantics.dispose(); }); - testWidgets('deltaToScrollOrigin getter', (WidgetTester tester) async { + testWidgetsWithLeakTracking('deltaToScrollOrigin getter', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: CustomScrollView( @@ -1327,7 +1340,7 @@ void main() { expect(scrollable.deltaToScrollOrigin, const Offset(0.0, 200)); }); - testWidgets('resolvedPhysics getter', (WidgetTester tester) async { + testWidgetsWithLeakTracking('resolvedPhysics getter', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( theme: ThemeData.light().copyWith( @@ -1357,7 +1370,7 @@ void main() { ); }); - testWidgets('dragDevices change updates widget', (WidgetTester tester) async { + testWidgetsWithLeakTracking('dragDevices change updates widget', (WidgetTester tester) async { bool enable = false; await tester.pumpWidget( @@ -1410,7 +1423,7 @@ void main() { expect(getScrollOffset(tester), 200); }); - testWidgets('dragDevices change updates widget when oldWidget scrollBehavior is null', (WidgetTester tester) async { + testWidgetsWithLeakTracking('dragDevices change updates widget when oldWidget scrollBehavior is null', (WidgetTester tester) async { ScrollBehavior? scrollBehavior; await tester.pumpWidget( diff --git a/packages/flutter/test/widgets/scrollbar_test.dart b/packages/flutter/test/widgets/scrollbar_test.dart index 1e2c831a44e09..c518dccc8f677 100644 --- a/packages/flutter/test/widgets/scrollbar_test.dart +++ b/packages/flutter/test/widgets/scrollbar_test.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/src/physics/utils.dart' show nearEqual; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; const Color _kScrollbarColor = Color(0xFF123456); const double _kThickness = 2.5; @@ -384,7 +385,7 @@ void main() { scrollMetrics: metrics, ); - testWidgets('down', (WidgetTester tester) async { + testWidgetsWithLeakTracking('down', (WidgetTester tester) async { painter.update( metrics.copyWith( viewportDimension: size.height, @@ -414,7 +415,7 @@ void main() { expect(size.width - rect1.right, padding.right); }); - testWidgets('up', (WidgetTester tester) async { + testWidgetsWithLeakTracking('up', (WidgetTester tester) async { painter.update( metrics.copyWith( viewportDimension: size.height, @@ -446,7 +447,7 @@ void main() { expect(size.width - rect1.right, padding.right); }); - testWidgets('left', (WidgetTester tester) async { + testWidgetsWithLeakTracking('left', (WidgetTester tester) async { painter.update( metrics.copyWith( viewportDimension: size.width, @@ -478,7 +479,7 @@ void main() { expect(rect1.left, padding.left); }); - testWidgets('right', (WidgetTester tester) async { + testWidgetsWithLeakTracking('right', (WidgetTester tester) async { painter.update( metrics.copyWith( viewportDimension: size.width, @@ -511,7 +512,7 @@ void main() { }); }); - testWidgets('thumb resizes gradually on overscroll', (WidgetTester tester) async { + testWidgetsWithLeakTracking('thumb resizes gradually on overscroll', (WidgetTester tester) async { const EdgeInsets padding = EdgeInsets.fromLTRB(1, 2, 3, 4); const Size size = Size(60, 300); final double scrollExtent = size.height * 10; @@ -664,7 +665,7 @@ void main() { expect(trackRRect.trRadius, const Radius.circular(2.0)); }); - testWidgets('ScrollbarPainter asserts if no TextDirection has been provided', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollbarPainter asserts if no TextDirection has been provided', (WidgetTester tester) async { final ScrollbarPainter painter = ScrollbarPainter( color: _kScrollbarColor, fadeoutOpacityAnimation: kAlwaysCompleteAnimation, @@ -683,8 +684,9 @@ void main() { } }); - testWidgets('Tapping the track area pages the Scroll View', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Tapping the track area pages the Scroll View', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -745,7 +747,7 @@ void main() { ); }); - testWidgets('Scrollbar never goes away until finger lift', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar never goes away until finger lift', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -803,7 +805,7 @@ void main() { ); }); - testWidgets('Scrollbar does not fade away while hovering', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar does not fade away while hovering', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -850,7 +852,7 @@ void main() { ); }); - testWidgets('Scrollbar will fade back in when hovering over known track area', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar will fade back in when hovering over known track area', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -911,7 +913,7 @@ void main() { ); }); - testWidgets('Scrollbar will show on hover without needing to scroll first for metrics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar will show on hover without needing to scroll first for metrics', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, @@ -946,8 +948,9 @@ void main() { ); }); - testWidgets('Scrollbar thumb can be dragged', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1001,8 +1004,9 @@ void main() { ); }); - testWidgets('Scrollbar thumb cannot be dragged into overscroll if the physics do not allow', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb cannot be dragged into overscroll if the physics do not allow', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1053,8 +1057,9 @@ void main() { ); }); - testWidgets('Scrollbar thumb cannot be dragged into overscroll if the platform does not allow it', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb cannot be dragged into overscroll if the platform does not allow it', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1121,8 +1126,9 @@ void main() { TargetPlatform.fuchsia, })); - testWidgets('Scrollbar thumb can be dragged into overscroll if the platform allows it', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged into overscroll if the platform allows it', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1188,7 +1194,7 @@ void main() { })); // Regression test for https://github.com/flutter/flutter/issues/66444 - testWidgets("RawScrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async { + testWidgetsWithLeakTracking("RawScrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async { final GlobalKey key1 = GlobalKey(); final GlobalKey key2 = GlobalKey(); final GlobalKey outerKey = GlobalKey(); @@ -1250,8 +1256,9 @@ void main() { ); }); - testWidgets('Scrollbar hit test area adjusts for PointerDeviceKind', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar hit test area adjusts for PointerDeviceKind', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1339,9 +1346,10 @@ void main() { ); }); - testWidgets('hit test', (WidgetTester tester) async { + testWidgetsWithLeakTracking('hit test', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/99324 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); bool onTap = false; await tester.pumpWidget( Directionality( @@ -1388,12 +1396,14 @@ void main() { expect(onTap, true); }); - testWidgets('RawScrollbar.thumbVisibility asserts that a ScrollPosition is attached', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RawScrollbar.thumbVisibility asserts that a ScrollPosition is attached', (WidgetTester tester) async { final FlutterExceptionHandler? handler = FlutterError.onError; FlutterErrorDetails? error; FlutterError.onError = (FlutterErrorDetails details) { error = details; }; + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( @@ -1402,7 +1412,7 @@ void main() { data: const MediaQueryData(), child: RawScrollbar( thumbVisibility: true, - controller: ScrollController(), + controller: controller, thumbColor: const Color(0x11111111), child: const SingleChildScrollView( child: SizedBox( @@ -1426,12 +1436,14 @@ void main() { FlutterError.onError = handler; }); - testWidgets('RawScrollbar.thumbVisibility asserts that a ScrollPosition is attached', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RawScrollbar.thumbVisibility asserts that a ScrollPosition is attached', (WidgetTester tester) async { final FlutterExceptionHandler? handler = FlutterError.onError; FlutterErrorDetails? error; FlutterError.onError = (FlutterErrorDetails details) { error = details; }; + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); await tester.pumpWidget( Directionality( @@ -1440,7 +1452,7 @@ void main() { data: const MediaQueryData(), child: RawScrollbar( thumbVisibility: true, - controller: ScrollController(), + controller: controller, thumbColor: const Color(0x11111111), child: const SingleChildScrollView( child: SizedBox( @@ -1464,9 +1476,11 @@ void main() { FlutterError.onError = handler; }); - testWidgets('Interactive scrollbars should have a valid scroll controller', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Interactive scrollbars should have a valid scroll controller', (WidgetTester tester) async { final ScrollController primaryScrollController = ScrollController(); + addTearDown(primaryScrollController.dispose); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( @@ -1505,9 +1519,10 @@ void main() { ); }); - testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/70105 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1679,8 +1694,9 @@ void main() { ); }); - testWidgets('Scrollbar thumb can be dragged in reverse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged in reverse', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1735,7 +1751,7 @@ void main() { ); }); - testWidgets('ScrollbarPainter asserts if scrollbarOrientation is used with wrong axisDirection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('ScrollbarPainter asserts if scrollbarOrientation is used with wrong axisDirection', (WidgetTester tester) async { final ScrollbarPainter painter = ScrollbarPainter( color: _kScrollbarColor, fadeoutOpacityAnimation: kAlwaysCompleteAnimation, @@ -1753,8 +1769,9 @@ void main() { expect(() => painter.paint(testCanvas, size), throwsA(isA())); }); - testWidgets('RawScrollbar mainAxisMargin property works properly', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RawScrollbar mainAxisMargin property works properly', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1783,8 +1800,9 @@ void main() { ); }); - testWidgets('shape property of RawScrollbar can draw a BeveledRectangleBorder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('shape property of RawScrollbar can draw a BeveledRectangleBorder', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1820,8 +1838,9 @@ void main() { ); }); - testWidgets('minThumbLength property of RawScrollbar is respected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('minThumbLength property of RawScrollbar is respected', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1846,8 +1865,9 @@ void main() { ..rect(rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 21.0))); // thumb }); - testWidgets('shape property of RawScrollbar can draw a CircleBorder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('shape property of RawScrollbar can draw a CircleBorder', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1882,8 +1902,9 @@ void main() { ); }); - testWidgets('crossAxisMargin property of RawScrollbar is respected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('crossAxisMargin property of RawScrollbar is respected', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1907,8 +1928,9 @@ void main() { ..rect(rect: const Rect.fromLTRB(764.0, 0.0, 770.0, 360.0))); }); - testWidgets('shape property of RawScrollbar can draw a RoundedRectangleBorder', (WidgetTester tester) async { + testWidgetsWithLeakTracking('shape property of RawScrollbar can draw a RoundedRectangleBorder', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1941,8 +1963,9 @@ void main() { ); }); - testWidgets('minOverscrollLength property of RawScrollbar is respected', (WidgetTester tester) async { + testWidgetsWithLeakTracking('minOverscrollLength property of RawScrollbar is respected', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1973,8 +1996,9 @@ void main() { ..rect(rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 8.0))); }); - testWidgets('not passing any shape or radius to RawScrollbar will draw the usual rectangular thumb', (WidgetTester tester) async { + testWidgetsWithLeakTracking('not passing any shape or radius to RawScrollbar will draw the usual rectangular thumb', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1999,8 +2023,9 @@ void main() { ); }); - testWidgets('The bar can show or hide when the viewport size change', (WidgetTester tester) async { + testWidgetsWithLeakTracking('The bar can show or hide when the viewport size change', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildFrame(double height) { return Directionality( textDirection: TextDirection.ltr, @@ -2030,10 +2055,11 @@ void main() { expect(find.byType(RawScrollbar), isNot(paints..rect())); // Hide the bar. }); - testWidgets('The bar can show or hide when the view size change', (WidgetTester tester) async { + testWidgetsWithLeakTracking('The bar can show or hide when the view size change', (WidgetTester tester) async { addTearDown(tester.view.reset); final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildFrame() { return Directionality( textDirection: TextDirection.ltr, @@ -2072,10 +2098,12 @@ void main() { expect(find.byType(RawScrollbar), isNot(paints..rect())); // Not shown. }); - testWidgets('Scrollbar will not flip axes based on notification is there is a scroll controller', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar will not flip axes based on notification is there is a scroll controller', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/87697 final ScrollController verticalScrollController = ScrollController(); + addTearDown(verticalScrollController.dispose); final ScrollController horizontalScrollController = ScrollController(); + addTearDown(horizontalScrollController.dispose); Widget buildFrame() { return Directionality( textDirection: TextDirection.ltr, @@ -2133,8 +2161,9 @@ void main() { ); }); - testWidgets('notificationPredicate depth test.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('notificationPredicate depth test.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); final List depths = []; Widget buildFrame() { return Directionality( @@ -2167,8 +2196,9 @@ void main() { }); // Regression test for https://github.com/flutter/flutter/issues/92262 - testWidgets('Do not crash when resize from scrollable to non-scrollable.', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Do not crash when resize from scrollable to non-scrollable.', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildFrame(double height) { return Directionality( textDirection: TextDirection.ltr, @@ -2203,10 +2233,11 @@ void main() { await tester.pumpAndSettle(); }); - testWidgets('Scrollbar thumb can be dragged when the scrollable widget has a negative minScrollExtent - desktop', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged when the scrollable widget has a negative minScrollExtent - desktop', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/95840 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); final UniqueKey uniqueKey = UniqueKey(); await tester.pumpWidget( Directionality( @@ -2285,10 +2316,11 @@ void main() { ); }, variant: TargetPlatformVariant.desktop()); - testWidgets('Scrollbar thumb can be dragged when the scrollable widget has a negative minScrollExtent - mobile', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar thumb can be dragged when the scrollable widget has a negative minScrollExtent - mobile', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/95840 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); final UniqueKey uniqueKey = UniqueKey(); await tester.pumpWidget( Directionality( @@ -2421,8 +2453,9 @@ void main() { expect(painter.shouldRepaint(createPainter(scrollbarOrientation: ScrollbarOrientation.bottom)), true); }); - testWidgets('Scrollbar track can be drawn', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar track can be drawn', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2464,8 +2497,9 @@ void main() { ); }); - testWidgets('RawScrollbar correctly assigns colors', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RawScrollbar correctly assigns colors', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2510,8 +2544,9 @@ void main() { ); }); - testWidgets('trackRadius and radius properties of RawScrollbar can draw RoundedRectangularRect', (WidgetTester tester) async { + testWidgetsWithLeakTracking('trackRadius and radius properties of RawScrollbar can draw RoundedRectangularRect', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2549,8 +2584,9 @@ void main() { ); }); - testWidgets('Scrollbar asserts that a visible track has a visible thumb', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar asserts that a visible track has a visible thumb', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildApp() { return Directionality( textDirection: TextDirection.ltr, @@ -2573,9 +2609,10 @@ void main() { expect(() => tester.pumpWidget(buildApp()), throwsAssertionError); }); - testWidgets('Skip the ScrollPosition check if the bar was unmounted', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Skip the ScrollPosition check if the bar was unmounted', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/103939 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildApp(bool buildBar) { return Directionality( textDirection: TextDirection.ltr, @@ -2611,9 +2648,10 @@ void main() { // Go without throw. }); - testWidgets('Track offset respects MediaQuery padding', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Track offset respects MediaQuery padding', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/106834 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2643,8 +2681,9 @@ void main() { ); // thumb }); - testWidgets('RawScrollbar.padding replaces MediaQueryData.padding', (WidgetTester tester) async { + testWidgetsWithLeakTracking('RawScrollbar.padding replaces MediaQueryData.padding', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2675,8 +2714,9 @@ void main() { ); // thumb }); - testWidgets('Scrollbar respect the NeverScrollableScrollPhysics physics', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Scrollbar respect the NeverScrollableScrollPhysics physics', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2717,9 +2757,10 @@ void main() { expect(scrollController.offset, 0.0); }); - testWidgets('The thumb should follow the pointer when the scroll metrics changed during dragging', (WidgetTester tester) async { + testWidgetsWithLeakTracking('The thumb should follow the pointer when the scroll metrics changed during dragging', (WidgetTester tester) async { // Regressing test for https://github.com/flutter/flutter/issues/112072 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2787,9 +2828,10 @@ void main() { ); }); - testWidgets('The scrollable should not stutter when the scroll metrics shrink during dragging', (WidgetTester tester) async { + testWidgetsWithLeakTracking('The scrollable should not stutter when the scroll metrics shrink during dragging', (WidgetTester tester) async { // Regressing test for https://github.com/flutter/flutter/issues/121574 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -2851,9 +2893,10 @@ void main() { expect(scrollController.offset, greaterThan(lastPosition)); }); - testWidgets('The bar support mouse wheel event', (WidgetTester tester) async { + testWidgetsWithLeakTracking('The bar support mouse wheel event', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/pull/109659 final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); Widget buildFrame() { return Directionality( textDirection: TextDirection.ltr, diff --git a/packages/flutter/test/widgets/selectable_region_context_menu_test.dart b/packages/flutter/test/widgets/selectable_region_context_menu_test.dart index 5ec1891246ce1..a7a43dab80158 100644 --- a/packages/flutter/test/widgets/selectable_region_context_menu_test.dart +++ b/packages/flutter/test/widgets/selectable_region_context_menu_test.dart @@ -9,6 +9,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; import 'package:web/web.dart' as web; extension on web.HTMLCollection { @@ -59,8 +60,9 @@ void main() { expect(foundStyle, isTrue); }); - testWidgets('right click can trigger select word', (WidgetTester tester) async { + testWidgetsWithLeakTracking('right click can trigger select word', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); final UniqueKey spy = UniqueKey(); await tester.pumpWidget( MaterialApp( diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart index 1e25ba4e7ff41..e28fb1640370e 100644 --- a/packages/flutter/test/widgets/selectable_region_test.dart +++ b/packages/flutter/test/widgets/selectable_region_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 'clipboard_utils.dart'; import 'keyboard_utils.dart'; @@ -37,12 +38,15 @@ void main() { }); group('SelectableRegion', () { - testWidgets('mouse selection single click sends correct events', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse selection single click sends correct events', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -75,12 +79,15 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/102410. - testWidgets('mouse double click sends select-word event', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse double click sends select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -103,8 +110,11 @@ void main() { expect(selectionEvent.globalPosition, const Offset(200.0, 200.0)); }); - testWidgets('Does not crash when using Navigator pages', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Does not crash when using Navigator pages', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/119776 + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: Navigator( @@ -114,7 +124,7 @@ void main() { children: [ const Text('How are you?'), SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const SelectAllWidget(child: SizedBox(width: 100, height: 100)), ), @@ -134,15 +144,18 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('can draw handles when they are at rect boundaries', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can draw handles when they are at rect boundaries', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: Column( children: [ const Text('How are you?'), SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectAllWidget(key: spy, child: const SizedBox(width: 100, height: 100)), ), @@ -164,12 +177,15 @@ void main() { expect(renderSpy.endHandle, isNotNull); }); - testWidgets('touch does not accept drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('touch does not accept drag', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -187,12 +203,15 @@ void main() { ); }); - testWidgets('does not merge semantics node of the children', (WidgetTester tester) async { + testWidgetsWithLeakTracking('does not merge semantics node of the children', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -262,12 +281,15 @@ void main() { semantics.dispose(); }); - testWidgets('mouse selection always cancels previous selection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse selection always cancels previous selection', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -283,12 +305,15 @@ void main() { expect(renderSelectionSpy.events[0], isA()); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/102410. - testWidgets('touch long press sends select-word event', (WidgetTester tester) async { + testWidgetsWithLeakTracking('touch long press sends select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -308,12 +333,15 @@ void main() { expect(selectionEvent.globalPosition, const Offset(200.0, 200.0)); }); - testWidgets('touch long press and drag sends correct events', (WidgetTester tester) async { + testWidgetsWithLeakTracking('touch long press and drag sends correct events', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -341,14 +369,17 @@ void main() { expect(edgeEvent.granularity, TextGranularity.word); }); - testWidgets( + testWidgetsWithLeakTracking( 'touch long press cancel does not send ClearSelectionEvent', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -373,11 +404,14 @@ void main() { }, ); - testWidgets( + testWidgetsWithLeakTracking( 'scrolling after the selection does not send ClearSelectionEvent', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/128765 final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SizedBox( @@ -386,7 +420,7 @@ void main() { child: SizedBox( height: 2000, child: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -417,12 +451,15 @@ void main() { }, ); - testWidgets('mouse long press does not send select-word event', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse long press does not send select-word event', (WidgetTester tester) async { final UniqueKey spy = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: SelectionSpy(key: spy), ), @@ -443,7 +480,7 @@ void main() { }); }); - testWidgets('dragging handle or selecting word triggers haptic feedback on Android', (WidgetTester tester) async { + testWidgetsWithLeakTracking('dragging handle or selecting word triggers haptic feedback on Android', (WidgetTester tester) async { final List log = []; tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { log.add(methodCall); @@ -452,11 +489,13 @@ void main() { addTearDown(() { tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, mockClipboard.handleMethodCall); }); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('How are you?'), ), @@ -504,11 +543,14 @@ void main() { }, variant: TargetPlatformVariant.all()); group('SelectionArea integration', () { - testWidgets('mouse can select single text', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select single text', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -548,11 +590,14 @@ void main() { await gesture.up(); }); - testWidgets('mouse can select word-by-word on double click drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select word-by-word on double click drag', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -617,11 +662,14 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('mouse can select multiple widgets on double click drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets on double click drag', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -663,11 +711,14 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('mouse can select multiple widgets on double click drag and return to origin word', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets on double click drag and return to origin word', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -721,11 +772,14 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('mouse can reverse selection across multiple widgets on double click drag', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can reverse selection across multiple widgets on double click drag', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -766,11 +820,14 @@ void main() { await gesture.up(); }, skip: kIsWeb); // https://github.com/flutter/flutter/issues/125582. - testWidgets('mouse can select multiple widgets', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select multiple widgets', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -806,11 +863,14 @@ void main() { await gesture.up(); }); - testWidgets('mouse can work with disabled container', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can work with disabled container', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -847,11 +907,14 @@ void main() { await gesture.up(); }); - testWidgets('mouse can reverse selection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can reverse selection', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -886,15 +949,18 @@ void main() { await gesture.up(); }); - testWidgets( + testWidgetsWithLeakTracking( 'single tap on the previous selection toggles the toolbar on iOS', (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -958,15 +1024,18 @@ void main() { skip: kIsWeb, // [intended] Web uses its native context menu. ); - testWidgets( + testWidgetsWithLeakTracking( 'right-click mouse can select word at position on Apple platforms', (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -1032,15 +1101,18 @@ void main() { skip: kIsWeb, // [intended] Web uses its native context menu. ); - testWidgets( + testWidgetsWithLeakTracking( 'right-click mouse at the same position as previous right-click toggles the context menu on macOS', (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -1130,15 +1202,18 @@ void main() { skip: kIsWeb, // [intended] Web uses its native context menu. ); - testWidgets( + testWidgetsWithLeakTracking( 'right-click mouse shows the context menu at position on Android, Fucshia, and Windows', (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -1238,15 +1313,18 @@ void main() { skip: kIsWeb, // [intended] Web uses its native context menu. ); - testWidgets( + testWidgetsWithLeakTracking( 'right-click mouse toggles the context menu on Linux', (WidgetTester tester) async { Set buttonTypes = {}; final UniqueKey toolbarKey = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -1355,11 +1433,14 @@ void main() { skip: kIsWeb, // [intended] Web uses its native context menu. ); - testWidgets('can copy a selection made with the mouse', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can copy a selection made with the mouse', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1388,12 +1469,16 @@ void main() { expect(clipboardData['text'], 'w are you?Good, and you?Fine, '); }, variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.windows, TargetPlatform.linux, TargetPlatform.fuchsia })); - testWidgets( + testWidgetsWithLeakTracking( 'does not override TextField keyboard shortcuts if the TextField is focused - non apple', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'I am fine, thank you.'); + addTearDown(controller.dispose); final FocusNode selectableRegionFocus = FocusNode(); + addTearDown(selectableRegionFocus.dispose); final FocusNode textFieldFocus = FocusNode(); + addTearDown(textFieldFocus.dispose); + await tester.pumpWidget( MaterialApp( home: Material( @@ -1441,12 +1526,16 @@ void main() { skip: kIsWeb, // [intended] the web handles this on its own. ); - testWidgets( + testWidgetsWithLeakTracking( 'does not override TextField keyboard shortcuts if the TextField is focused - apple', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'I am fine, thank you.'); + addTearDown(controller.dispose); final FocusNode selectableRegionFocus = FocusNode(); + addTearDown(selectableRegionFocus.dispose); final FocusNode textFieldFocus = FocusNode(); + addTearDown(textFieldFocus.dispose); + await tester.pumpWidget( MaterialApp( home: Material( @@ -1494,8 +1583,10 @@ void main() { skip: kIsWeb, // [intended] the web handles this on its own. ); - testWidgets('select all', (WidgetTester tester) async { + testWidgetsWithLeakTracking('select all', (WidgetTester tester) async { final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( @@ -1525,13 +1616,16 @@ void main() { expect(paragraph1.selections[0], const TextSelection(baseOffset: 0, extentOffset: 12)); }, variant: const TargetPlatformVariant({ TargetPlatform.android, TargetPlatform.windows, TargetPlatform.linux, TargetPlatform.fuchsia })); - testWidgets( + testWidgetsWithLeakTracking( 'mouse selection can handle widget span', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -1564,14 +1658,17 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/61020 ); - testWidgets( + testWidgetsWithLeakTracking( 'can select word when a selectables rect is completely inside of another selectables rect', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/127076. final UniqueKey outerText = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center( @@ -1612,14 +1709,17 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/61020 ); - testWidgets( + testWidgetsWithLeakTracking( 'widget span is ignored if it does not contain text - non Apple', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -1652,14 +1752,17 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/61020 ); - testWidgets( + testWidgetsWithLeakTracking( 'widget span is ignored if it does not contain text - Apple', (WidgetTester tester) async { final UniqueKey outerText = UniqueKey(); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Center( child: Text.rich( @@ -1692,11 +1795,14 @@ void main() { skip: isBrowser, // https://github.com/flutter/flutter/issues/61020 ); - testWidgets('mouse can select across bidi text', (WidgetTester tester) async { + testWidgetsWithLeakTracking('mouse can select across bidi text', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1733,11 +1839,14 @@ void main() { await gesture.up(); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61020 - testWidgets('long press and drag touch moves selection word by word', (WidgetTester tester) async { + testWidgetsWithLeakTracking('long press and drag touch moves selection word by word', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1764,15 +1873,18 @@ void main() { await gesture.up(); }); - testWidgets('can drag end handle when not covering entire screen', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can drag end handle when not covering entire screen', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/104620. + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: Column( children: [ const Text('How are you?'), SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('Good, and you?'), ), @@ -1801,15 +1913,18 @@ void main() { await gesture.up(); }); - testWidgets('can drag start handle when not covering entire screen', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can drag start handle when not covering entire screen', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/104620. + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: Column( children: [ const Text('How are you?'), SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('Good, and you?'), ), @@ -1837,11 +1952,14 @@ void main() { await gesture.up(); }); - testWidgets('can drag start selection handle', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can drag start selection handle', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1879,11 +1997,14 @@ void main() { await gesture.up(); }); - testWidgets('can drag start selection handle across end selection handle', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can drag start selection handle across end selection handle', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1916,11 +2037,14 @@ void main() { await gesture.up(); }); - testWidgets('can drag end selection handle across start selection handle', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can drag end selection handle across start selection handle', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1953,11 +2077,14 @@ void main() { await gesture.up(); }); - testWidgets('can select all from toolbar', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can select all from toolbar', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -1989,11 +2116,14 @@ void main() { expect(paragraph1.selections[0], const TextSelection(baseOffset: 0, extentOffset: 12)); }, skip: kIsWeb); // [intended] Web uses its native context menu. - testWidgets('can copy from toolbar', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can copy from toolbar', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2029,11 +2159,14 @@ void main() { expect(clipboardData['text'], 'thank'); }, skip: kIsWeb); // [intended] Web uses its native context menu. - testWidgets('can use keyboard to granularly extend selection - character', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use keyboard to granularly extend selection - character', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2089,11 +2222,14 @@ void main() { } }, variant: TargetPlatformVariant.all()); - testWidgets('can use keyboard to granularly extend selection - word', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use keyboard to granularly extend selection - word', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2197,11 +2333,14 @@ void main() { expect(paragraph2.selections.length, 0); }, variant: TargetPlatformVariant.all()); - testWidgets('can use keyboard to granularly extend selection - line', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use keyboard to granularly extend selection - line', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2286,11 +2425,14 @@ void main() { expect(paragraph1.selections[0].end, 2); }, variant: TargetPlatformVariant.all()); - testWidgets('can use keyboard to granularly extend selection - document', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use keyboard to granularly extend selection - document', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2362,11 +2504,14 @@ void main() { expect(paragraph3.selections.length, 0); }, variant: TargetPlatformVariant.all()); - testWidgets('can use keyboard to directionally extend selection', (WidgetTester tester) async { + testWidgetsWithLeakTracking('can use keyboard to directionally extend selection', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -2459,9 +2604,11 @@ void main() { late ValueNotifier magnifierInfo; final Widget fakeMagnifier = Container(key: UniqueKey()); - testWidgets('Can drag handles to show, unshow, and update magnifier', + testWidgetsWithLeakTracking('Can drag handles to show, unshow, and update magnifier', (WidgetTester tester) async { const String text = 'Monkeys and rabbits in my soup'; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( @@ -2475,7 +2622,7 @@ void main() { return fakeMagnifier; }, ), - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text(text), ), @@ -2525,13 +2672,15 @@ void main() { }); }); - testWidgets('toolbar is hidden on mobile when orientation changes', (WidgetTester tester) async { + testWidgetsWithLeakTracking('toolbar is hidden on mobile when orientation changes', (WidgetTester tester) async { addTearDown(tester.view.reset); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Text('How are you?'), ), @@ -2567,12 +2716,15 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.android }), ); - testWidgets('the selection behavior when clicking `Copy` item in mobile platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('the selection behavior when clicking `Copy` item in mobile platforms', (WidgetTester tester) async { List buttonItems = []; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2621,12 +2773,15 @@ void main() { skip: kIsWeb, // [intended] ); - testWidgets('the handles do not disappear when clicking `Select all` item in mobile platforms', (WidgetTester tester) async { + testWidgetsWithLeakTracking('the handles do not disappear when clicking `Select all` item in mobile platforms', (WidgetTester tester) async { List buttonItems = []; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2674,12 +2829,15 @@ void main() { variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.android, TargetPlatform.fuchsia }), ); - testWidgets('builds the correct button items', (WidgetTester tester) async { + testWidgetsWithLeakTracking('builds the correct button items', (WidgetTester tester) async { Set buttonTypes = {}; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionHandleControls, contextMenuBuilder: ( BuildContext context, @@ -2713,14 +2871,16 @@ void main() { skip: kIsWeb, // [intended] ); - testWidgets('onSelectionChange is called when the selection changes through gestures', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onSelectionChange is called when the selection changes through gestures', (WidgetTester tester) async { SelectedContent? content; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) => content = selectedContent, - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -2845,14 +3005,16 @@ void main() { await tester.pumpAndSettle(); }); - testWidgets('onSelectionChange is called when the selection changes through keyboard actions', (WidgetTester tester) async { + testWidgetsWithLeakTracking('onSelectionChange is called when the selection changes through keyboard actions', (WidgetTester tester) async { SelectedContent? content; + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) => content = selectedContent, - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Column( children: [ @@ -3003,12 +3165,15 @@ void main() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.contextMenu, null); }); - testWidgets('web can show flutter context menu when the browser context menu is disabled', (WidgetTester tester) async { + testWidgetsWithLeakTracking('web can show flutter context menu when the browser context menu is disabled', (WidgetTester tester) async { + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( onSelectionChanged: (SelectedContent? selectedContent) {}, - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: const Center( child: Text('How are you'), @@ -3034,14 +3199,17 @@ void main() { ); }); - testWidgets('Multiple selectables on a single line should be in screen order', (WidgetTester tester) async { + testWidgetsWithLeakTracking('Multiple selectables on a single line should be in screen order', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/127942. final UniqueKey outerText = UniqueKey(); const TextStyle textStyle = TextStyle(fontSize: 10); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + await tester.pumpWidget( MaterialApp( home: SelectableRegion( - focusNode: FocusNode(), + focusNode: focusNode, selectionControls: materialTextSelectionControls, child: Scaffold( body: Center(