Unverified Commit 95eae5f9 authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

Cover more test/widgets tests with leak tracking #12. (#135385)

parent fdde2419
...@@ -435,7 +435,7 @@ void main() { ...@@ -435,7 +435,7 @@ void main() {
); );
}); });
testWidgets('disposes animation and controller', (WidgetTester tester) async { testWidgetsWithLeakTracking('disposes animation and controller', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const Center( const Center(
child: AnimatedSize( child: AnimatedSize(
......
...@@ -1062,13 +1062,14 @@ void main() { ...@@ -1062,13 +1062,14 @@ void main() {
element.createChild(0, after: null); element.createChild(0, after: null);
}); });
testWidgets('GlobalKey - re-attach child to new parents, and the old parent is deactivated(unmounted)', (WidgetTester tester) async { testWidgetsWithLeakTracking('GlobalKey - re-attach child to new parents, and the old parent is deactivated(unmounted)', (WidgetTester tester) async {
// This is a regression test for https://github.com/flutter/flutter/issues/62055 // This is a regression test for https://github.com/flutter/flutter/issues/62055
const Key key1 = GlobalObjectKey('key1'); const Key key1 = GlobalObjectKey('key1');
const Key key2 = GlobalObjectKey('key2'); const Key key2 = GlobalObjectKey('key2');
late StateSetter setState; late StateSetter setState;
int tabBarViewCnt = 2; int tabBarViewCnt = 2;
TabController tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync()); TabController tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync());
addTearDown(tabController.dispose);
await tester.pumpWidget(Directionality( await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1101,6 +1102,7 @@ void main() { ...@@ -1101,6 +1102,7 @@ void main() {
setState(() { setState(() {
tabBarViewCnt = 1; tabBarViewCnt = 1;
tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync()); tabController = TabController(length: tabBarViewCnt, vsync: const TestVSync());
addTearDown(tabController.dispose);
}); });
await tester.pump(const Duration(seconds: 1)); // finish the animation await tester.pump(const Duration(seconds: 1)); // finish the animation
......
...@@ -15,6 +15,7 @@ import 'package:flutter/src/widgets/_html_element_view_web.dart' ...@@ -15,6 +15,7 @@ import 'package:flutter/src/widgets/_html_element_view_web.dart'
show debugOverridePlatformViewRegistry; show debugOverridePlatformViewRegistry;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'package:web/web.dart' as web; import 'package:web/web.dart' as web;
final Object _mockHtmlElement = Object(); final Object _mockHtmlElement = Object();
...@@ -42,7 +43,7 @@ void main() { ...@@ -42,7 +43,7 @@ void main() {
}); });
group('HtmlElementView', () { group('HtmlElementView', () {
testWidgets('Create HTML view', (WidgetTester tester) async { testWidgetsWithLeakTracking('Create HTML view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
...@@ -64,7 +65,7 @@ void main() { ...@@ -64,7 +65,7 @@ void main() {
); );
}); });
testWidgets('Create HTML view with PlatformViewCreatedCallback', (WidgetTester tester) async { testWidgetsWithLeakTracking('Create HTML view with PlatformViewCreatedCallback', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
...@@ -97,7 +98,7 @@ void main() { ...@@ -97,7 +98,7 @@ void main() {
); );
}); });
testWidgets('Create HTML view with creation params', (WidgetTester tester) async { testWidgetsWithLeakTracking('Create HTML view with creation params', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -132,7 +133,7 @@ void main() { ...@@ -132,7 +133,7 @@ void main() {
); );
}); });
testWidgets('Resize HTML view', (WidgetTester tester) async { testWidgetsWithLeakTracking('Resize HTML view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -168,7 +169,7 @@ void main() { ...@@ -168,7 +169,7 @@ void main() {
); );
}); });
testWidgets('Change HTML view type', (WidgetTester tester) async { testWidgetsWithLeakTracking('Change HTML view type', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
fakePlatformViewRegistry.registerViewFactory('maps', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('maps', _mockViewFactory);
...@@ -200,7 +201,7 @@ void main() { ...@@ -200,7 +201,7 @@ void main() {
); );
}); });
testWidgets('Dispose HTML view', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dispose HTML view', (WidgetTester tester) async {
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
await tester.pumpWidget( await tester.pumpWidget(
const Center( const Center(
...@@ -227,7 +228,7 @@ void main() { ...@@ -227,7 +228,7 @@ void main() {
); );
}); });
testWidgets('HTML view survives widget tree change', (WidgetTester tester) async { testWidgetsWithLeakTracking('HTML view survives widget tree change', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory); fakePlatformViewRegistry.registerViewFactory('webview', _mockViewFactory);
final GlobalKey key = GlobalKey(); final GlobalKey key = GlobalKey();
...@@ -259,7 +260,7 @@ void main() { ...@@ -259,7 +260,7 @@ void main() {
); );
}); });
testWidgets('HtmlElementView has correct semantics', (WidgetTester tester) async { testWidgetsWithLeakTracking('HtmlElementView has correct semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
expect(currentViewId, greaterThanOrEqualTo(0)); expect(currentViewId, greaterThanOrEqualTo(0));
...@@ -306,7 +307,7 @@ void main() { ...@@ -306,7 +307,7 @@ void main() {
debugOverridePlatformViewRegistry = null; debugOverridePlatformViewRegistry = null;
}); });
testWidgets('Create platform view from tagName', (WidgetTester tester) async { testWidgetsWithLeakTracking('Create platform view from tagName', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -331,7 +332,7 @@ void main() { ...@@ -331,7 +332,7 @@ void main() {
expect(htmlElement.tagName, equalsIgnoringCase('div')); expect(htmlElement.tagName, equalsIgnoringCase('div'));
}); });
testWidgets('Create invisible platform view', (WidgetTester tester) async { testWidgetsWithLeakTracking('Create invisible platform view', (WidgetTester tester) async {
final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); final int currentViewId = platformViewsRegistry.getNextPlatformViewId();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -357,7 +358,7 @@ void main() { ...@@ -357,7 +358,7 @@ void main() {
expect(htmlElement.tagName, equalsIgnoringCase('script')); expect(htmlElement.tagName, equalsIgnoringCase('script'));
}); });
testWidgets('onElementCreated', (WidgetTester tester) async { testWidgetsWithLeakTracking('onElementCreated', (WidgetTester tester) async {
final List<Object> createdElements = <Object>[]; final List<Object> createdElements = <Object>[];
void onElementCreated(Object element) { void onElementCreated(Object element) {
createdElements.add(element); createdElements.add(element);
......
...@@ -55,7 +55,7 @@ void main() { ...@@ -55,7 +55,7 @@ void main() {
}); });
group('construction check', () { group('construction check', () {
testWidgets('ListWheelScrollView needs positive diameter ratio', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView needs positive diameter ratio', (WidgetTester tester) async {
expect( expect(
() => ListWheelScrollView( () => ListWheelScrollView(
diameterRatio: nonconst(-2.0), diameterRatio: nonconst(-2.0),
...@@ -70,7 +70,7 @@ void main() { ...@@ -70,7 +70,7 @@ void main() {
); );
}); });
testWidgets('ListWheelScrollView can have zero child', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView can have zero child', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -114,7 +114,7 @@ void main() { ...@@ -114,7 +114,7 @@ void main() {
expect(detach, 1); expect(detach, 1);
}); });
testWidgets('ListWheelScrollView needs positive magnification', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView needs positive magnification', (WidgetTester tester) async {
expect( expect(
() { () {
ListWheelScrollView( ListWheelScrollView(
...@@ -128,7 +128,7 @@ void main() { ...@@ -128,7 +128,7 @@ void main() {
); );
}); });
testWidgets('ListWheelScrollView needs valid overAndUnderCenterOpacity', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView needs valid overAndUnderCenterOpacity', (WidgetTester tester) async {
expect( expect(
() { () {
ListWheelScrollView( ListWheelScrollView(
...@@ -175,9 +175,9 @@ void main() { ...@@ -175,9 +175,9 @@ void main() {
}); });
group('infinite scrolling', () { group('infinite scrolling', () {
testWidgets('infinite looping list', (WidgetTester tester) async { testWidgetsWithLeakTracking('infinite looping list', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController();
FixedExtentScrollController(); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -221,9 +221,9 @@ void main() { ...@@ -221,9 +221,9 @@ void main() {
); );
}); });
testWidgets('infinite child builder', (WidgetTester tester) async { testWidgetsWithLeakTracking('infinite child builder', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController();
FixedExtentScrollController(); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -262,12 +262,12 @@ void main() { ...@@ -262,12 +262,12 @@ void main() {
); );
}); });
testWidgets('child builder with lower and upper limits', (WidgetTester tester) async { testWidgetsWithLeakTracking('child builder with lower and upper limits', (WidgetTester tester) async {
// Adjust the content dimensions at the end of `RenderListWheelViewport.performLayout()` // Adjust the content dimensions at the end of `RenderListWheelViewport.performLayout()`
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: -10);
FixedExtentScrollController(initialItem: -10); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -320,9 +320,11 @@ void main() { ...@@ -320,9 +320,11 @@ void main() {
}); });
group('layout', () { group('layout', () {
testWidgets('Flings with high velocity should not break the children lower and upper limits', (WidgetTester tester) async { testWidgetsWithLeakTracking('Flings with high velocity should not break the children lower and upper limits', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/112526 // Regression test for https://github.com/flutter/flutter/issues/112526
final FixedExtentScrollController controller = FixedExtentScrollController(); final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
Widget buildFrame() { Widget buildFrame() {
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -359,8 +361,10 @@ void main() { ...@@ -359,8 +361,10 @@ void main() {
}, variant: TargetPlatformVariant(TargetPlatform.values.toSet())); }, variant: TargetPlatformVariant(TargetPlatform.values.toSet()));
// Regression test for https://github.com/flutter/flutter/issues/90953 // Regression test for https://github.com/flutter/flutter/issues/90953
testWidgets('ListWheelScrollView childDelegate update test 2', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView childDelegate update test 2', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController( initialItem: 2 ); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 2);
addTearDown(controller.dispose);
Widget buildFrame(int childCount) { Widget buildFrame(int childCount) {
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -422,8 +426,10 @@ void main() { ...@@ -422,8 +426,10 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/58144 // Regression test for https://github.com/flutter/flutter/issues/58144
testWidgets('ListWheelScrollView childDelegate update test', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView childDelegate update test', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(); final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
Widget buildFrame(int childCount) { Widget buildFrame(int childCount) {
return Directionality( return Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -453,7 +459,7 @@ void main() { ...@@ -453,7 +459,7 @@ void main() {
expect(tester.renderObject(find.text('1')).attached, true); expect(tester.renderObject(find.text('1')).attached, true);
}); });
testWidgets("ListWheelScrollView takes parent's size with small children", (WidgetTester tester) async { testWidgetsWithLeakTracking("ListWheelScrollView takes parent's size with small children", (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -474,7 +480,7 @@ void main() { ...@@ -474,7 +480,7 @@ void main() {
expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0)); expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0));
}); });
testWidgets("ListWheelScrollView takes parent's size with large children", (WidgetTester tester) async { testWidgetsWithLeakTracking("ListWheelScrollView takes parent's size with large children", (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -495,7 +501,7 @@ void main() { ...@@ -495,7 +501,7 @@ void main() {
expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0)); expect(tester.getBottomRight(find.byType(ListWheelScrollView)), const Offset(800.0, 600.0));
}); });
testWidgets("ListWheelScrollView children can't be bigger than itemExtent", (WidgetTester tester) async { testWidgetsWithLeakTracking("ListWheelScrollView children can't be bigger than itemExtent", (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -517,10 +523,10 @@ void main() { ...@@ -517,10 +523,10 @@ void main() {
expect(find.text('blah'), findsOneWidget); expect(find.text('blah'), findsOneWidget);
}); });
testWidgets('builder is never called twice for same index', (WidgetTester tester) async { testWidgetsWithLeakTracking('builder is never called twice for same index', (WidgetTester tester) async {
final Set<int> builtChildren = <int>{}; final Set<int> builtChildren = <int>{};
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController();
FixedExtentScrollController(); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -554,9 +560,9 @@ void main() { ...@@ -554,9 +560,9 @@ void main() {
await tester.pump(); await tester.pump();
}); });
testWidgets('only visible children are maintained as children of the rendered viewport', (WidgetTester tester) async { testWidgetsWithLeakTracking('only visible children are maintained as children of the rendered viewport', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController();
FixedExtentScrollController(); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -591,9 +597,9 @@ void main() { ...@@ -591,9 +597,9 @@ void main() {
expect(viewport.childCount, 4); expect(viewport.childCount, 4);
}); });
testWidgets('a tighter squeeze lays out more children', (WidgetTester tester) async { testWidgetsWithLeakTracking('a tighter squeeze lays out more children', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
FixedExtentScrollController(initialItem: 10); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -636,7 +642,7 @@ void main() { ...@@ -636,7 +642,7 @@ void main() {
expect(viewport.childCount, 13); expect(viewport.childCount, 13);
}); });
testWidgets('Active children are laid out with correct offset', (WidgetTester tester) async { testWidgetsWithLeakTracking('Active children are laid out with correct offset', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/123497 // Regression test for https://github.com/flutter/flutter/issues/123497
Future<void> buildWidget(double width) async { Future<void> buildWidget(double width) async {
return tester.pumpWidget( return tester.pumpWidget(
...@@ -673,8 +679,9 @@ void main() { ...@@ -673,8 +679,9 @@ void main() {
}); });
group('pre-transform viewport', () { group('pre-transform viewport', () {
testWidgets('ListWheelScrollView starts and ends from the middle', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView starts and ends from the middle', (WidgetTester tester) async {
final ScrollController controller = ScrollController(); final ScrollController controller = ScrollController();
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -715,8 +722,9 @@ void main() { ...@@ -715,8 +722,9 @@ void main() {
expect(paintedChildren, <int>[96, 97, 98, 99]); expect(paintedChildren, <int>[96, 97, 98, 99]);
}); });
testWidgets('A child gets painted as soon as its first pixel is in the viewport', (WidgetTester tester) async { testWidgetsWithLeakTracking('A child gets painted as soon as its first pixel is in the viewport', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 50.0); final ScrollController controller = ScrollController(initialScrollOffset: 50.0);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -749,8 +757,9 @@ void main() { ...@@ -749,8 +757,9 @@ void main() {
expect(paintedChildren, <int>[0, 1, 2, 3, 4]); expect(paintedChildren, <int>[0, 1, 2, 3, 4]);
}); });
testWidgets('A child is no longer painted after its last pixel leaves the viewport', (WidgetTester tester) async { testWidgetsWithLeakTracking('A child is no longer painted after its last pixel leaves the viewport', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 250.0); final ScrollController controller = ScrollController(initialScrollOffset: 250.0);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -793,7 +802,7 @@ void main() { ...@@ -793,7 +802,7 @@ void main() {
}); });
group('viewport transformation', () { group('viewport transformation', () {
testWidgets('Center child is magnified', (WidgetTester tester) async { testWidgetsWithLeakTracking('Center child is magnified', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -817,7 +826,7 @@ void main() { ...@@ -817,7 +826,7 @@ void main() {
); );
}); });
testWidgets('Default middle transform', (WidgetTester tester) async { testWidgetsWithLeakTracking('Default middle transform', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -846,8 +855,10 @@ void main() { ...@@ -846,8 +855,10 @@ void main() {
)); ));
}); });
testWidgets('Curve the wheel to the left', (WidgetTester tester) async { testWidgetsWithLeakTracking('Curve the wheel to the left', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 300.0); final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -871,8 +882,9 @@ void main() { ...@@ -871,8 +882,9 @@ void main() {
); );
}); });
testWidgets('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async { testWidgetsWithLeakTracking('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 200.0); final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -990,8 +1002,9 @@ void main() { ...@@ -990,8 +1002,9 @@ void main() {
)); ));
}); });
testWidgets('offAxisFraction, magnification changes matrix', (WidgetTester tester) async { testWidgetsWithLeakTracking('offAxisFraction, magnification changes matrix', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 200.0); final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -1089,7 +1102,7 @@ void main() { ...@@ -1089,7 +1102,7 @@ void main() {
}); });
group('scroll notifications', () { group('scroll notifications', () {
testWidgets('no onSelectedItemChanged callback on first build', (WidgetTester tester) async { testWidgetsWithLeakTracking('no onSelectedItemChanged callback on first build', (WidgetTester tester) async {
bool itemChangeCalled = false; bool itemChangeCalled = false;
void onItemChange(int _) { itemChangeCalled = true; } void onItemChange(int _) { itemChangeCalled = true; }
...@@ -1114,7 +1127,7 @@ void main() { ...@@ -1114,7 +1127,7 @@ void main() {
expect(itemChangeCalled, false); expect(itemChangeCalled, false);
}); });
testWidgets('onSelectedItemChanged when a new item is closest to center', (WidgetTester tester) async { testWidgetsWithLeakTracking('onSelectedItemChanged when a new item is closest to center', (WidgetTester tester) async {
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1151,7 +1164,7 @@ void main() { ...@@ -1151,7 +1164,7 @@ void main() {
expect(selectedItems, <int>[1, 2, 1]); expect(selectedItems, <int>[1, 2, 1]);
}); });
testWidgets('onSelectedItemChanged reports only in valid range', (WidgetTester tester) async { testWidgetsWithLeakTracking('onSelectedItemChanged reports only in valid range', (WidgetTester tester) async {
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1187,8 +1200,9 @@ void main() { ...@@ -1187,8 +1200,9 @@ void main() {
}); });
group('scroll controller', () { group('scroll controller', () {
testWidgets('initialItem', (WidgetTester tester) async { testWidgetsWithLeakTracking('initialItem', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1213,8 +1227,9 @@ void main() { ...@@ -1213,8 +1227,9 @@ void main() {
expect(controller.selectedItem, 10); expect(controller.selectedItem, 10);
}); });
testWidgets('controller jump', (WidgetTester tester) async { testWidgetsWithLeakTracking('controller jump', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1245,8 +1260,9 @@ void main() { ...@@ -1245,8 +1260,9 @@ void main() {
expect(controller.selectedItem, 0); expect(controller.selectedItem, 0);
}); });
testWidgets('controller animateToItem', (WidgetTester tester) async { testWidgetsWithLeakTracking('controller animateToItem', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1282,9 +1298,10 @@ void main() { ...@@ -1282,9 +1298,10 @@ void main() {
expect(controller.selectedItem, 0); expect(controller.selectedItem, 0);
}); });
testWidgets('onSelectedItemChanged and controller are in sync', (WidgetTester tester) async { testWidgetsWithLeakTracking('onSelectedItemChanged and controller are in sync', (WidgetTester tester) async {
final List<int> selectedItems = <int>[]; final List<int> selectedItems = <int>[];
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -1334,8 +1351,7 @@ void main() { ...@@ -1334,8 +1351,7 @@ void main() {
await tester.drag(find.byType(ListWheelScrollView), const Offset(0.0, -500.0)); await tester.drag(find.byType(ListWheelScrollView), const Offset(0.0, -500.0));
await tester.pump(); await tester.pump();
final FixedExtentScrollController controller1 = final FixedExtentScrollController controller1 = FixedExtentScrollController(initialItem: 30);
FixedExtentScrollController(initialItem: 30);
addTearDown(controller1.dispose); addTearDown(controller1.dispose);
// Attaching first controller. // Attaching first controller.
...@@ -1360,8 +1376,7 @@ void main() { ...@@ -1360,8 +1376,7 @@ void main() {
expect(controller1.selectedItem, 50); expect(controller1.selectedItem, 50);
expect(controller1.position.pixels, 5000.0); expect(controller1.position.pixels, 5000.0);
final FixedExtentScrollController controller2 = final FixedExtentScrollController controller2 = FixedExtentScrollController(initialItem: 33);
FixedExtentScrollController(initialItem: 33);
addTearDown(controller2.dispose); addTearDown(controller2.dispose);
// Attaching the second controller. // Attaching the second controller.
...@@ -1407,8 +1422,7 @@ void main() { ...@@ -1407,8 +1422,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('controller can be reused', (WidgetTester tester) async { testWidgetsWithLeakTracking('controller can be reused', (WidgetTester tester) async {
final FixedExtentScrollController controller = final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 3);
FixedExtentScrollController(initialItem: 3);
addTearDown(controller.dispose); addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1458,8 +1472,9 @@ void main() { ...@@ -1458,8 +1472,9 @@ void main() {
}); });
group('physics', () { group('physics', () {
testWidgets('fling velocities too low snaps back to the same item', (WidgetTester tester) async { testWidgetsWithLeakTracking('fling velocities too low snaps back to the same item', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40);
addTearDown(controller.dispose);
final List<double> scrolledPositions = <double>[]; final List<double> scrolledPositions = <double>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1508,8 +1523,9 @@ void main() { ...@@ -1508,8 +1523,9 @@ void main() {
expect(scrolledPositions.last, moreOrLessEquals(40 * 1000.0, epsilon: 0.2)); expect(scrolledPositions.last, moreOrLessEquals(40 * 1000.0, epsilon: 0.2));
}); });
testWidgets('high fling velocities lands exactly on items', (WidgetTester tester) async { testWidgetsWithLeakTracking('high fling velocities lands exactly on items', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 40);
addTearDown(controller.dispose);
final List<double> scrolledPositions = <double>[]; final List<double> scrolledPositions = <double>[];
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1561,9 +1577,11 @@ void main() { ...@@ -1561,9 +1577,11 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
}); });
testWidgets('ListWheelScrollView getOffsetToReveal', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView getOffsetToReveal', (WidgetTester tester) async {
List<Widget> outerChildren; List<Widget> outerChildren;
final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container()); final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container());
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -1573,7 +1591,7 @@ void main() { ...@@ -1573,7 +1591,7 @@ void main() {
height: 500.0, height: 500.0,
width: 300.0, width: 300.0,
child: ListWheelScrollView( child: ListWheelScrollView(
controller: ScrollController(initialScrollOffset: 300.0), controller: controller,
itemExtent: 100.0, itemExtent: 100.0,
children: outerChildren = List<Widget>.generate(10, (int i) { children: outerChildren = List<Widget>.generate(10, (int i) {
return Center( return Center(
...@@ -1629,7 +1647,10 @@ void main() { ...@@ -1629,7 +1647,10 @@ void main() {
expect(revealed.rect, const Rect.fromLTWH(165.0, 265.0, 10.0, 10.0)); expect(revealed.rect, const Rect.fromLTWH(165.0, 265.0, 10.0, 10.0));
}); });
testWidgets('will not assert on getOffsetToReveal Axis', (WidgetTester tester) async { testWidgetsWithLeakTracking('will not assert on getOffsetToReveal Axis', (WidgetTester tester) async {
final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1638,7 +1659,7 @@ void main() { ...@@ -1638,7 +1659,7 @@ void main() {
height: 500.0, height: 500.0,
width: 300.0, width: 300.0,
child: ListWheelScrollView( child: ListWheelScrollView(
controller: ScrollController(initialScrollOffset: 300.0), controller: controller,
itemExtent: 100.0, itemExtent: 100.0,
children: List<Widget>.generate(10, (int i) { children: List<Widget>.generate(10, (int i) {
return Center( return Center(
...@@ -1660,10 +1681,11 @@ void main() { ...@@ -1660,10 +1681,11 @@ void main() {
viewport.getOffsetToReveal(target, 0.0, axis: Axis.horizontal); viewport.getOffsetToReveal(target, 0.0, axis: Axis.horizontal);
}); });
testWidgets('ListWheelScrollView showOnScreen', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView showOnScreen', (WidgetTester tester) async {
List<Widget> outerChildren; List<Widget> outerChildren;
final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container()); final List<Widget> innerChildren = List<Widget>.generate(10, (int index) => Container());
ScrollController controller; final ScrollController controller = ScrollController(initialScrollOffset: 300.0);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -1673,7 +1695,7 @@ void main() { ...@@ -1673,7 +1695,7 @@ void main() {
height: 500.0, height: 500.0,
width: 300.0, width: 300.0,
child: ListWheelScrollView( child: ListWheelScrollView(
controller: controller = ScrollController(initialScrollOffset: 300.0), controller: controller,
itemExtent: 100.0, itemExtent: 100.0,
children: children:
outerChildren = List<Widget>.generate(10, (int i) { outerChildren = List<Widget>.generate(10, (int i) {
...@@ -1716,8 +1738,9 @@ void main() { ...@@ -1716,8 +1738,9 @@ void main() {
}); });
group('gestures', () { group('gestures', () {
testWidgets('ListWheelScrollView allows taps for on its children', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView allows taps for on its children', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController controller = FixedExtentScrollController(initialItem: 10);
addTearDown(controller.dispose);
final List<int> children = List<int>.generate(100, (int index) => index); final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{}; final Set<int> tappedChildren = <int>{};
...@@ -1758,8 +1781,9 @@ void main() { ...@@ -1758,8 +1781,9 @@ void main() {
expect(tappedChildren, paintedChildren); expect(tappedChildren, paintedChildren);
}); });
testWidgets('ListWheelScrollView allows for horizontal drags on its children', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView allows for horizontal drags on its children', (WidgetTester tester) async {
final PageController pageController = PageController(); final PageController pageController = PageController();
addTearDown(pageController.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -1785,10 +1809,11 @@ void main() { ...@@ -1785,10 +1809,11 @@ void main() {
expect(pageController.page, 1.0); expect(pageController.page, 1.0);
}); });
testWidgets('ListWheelScrollView does not crash and does not allow taps on children that were laid out, but not painted', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView does not crash and does not allow taps on children that were laid out, but not painted', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126491 // Regression test for https://github.com/flutter/flutter/issues/126491
final FixedExtentScrollController controller = FixedExtentScrollController(); final FixedExtentScrollController controller = FixedExtentScrollController();
addTearDown(controller.dispose);
final List<int> children = List<int>.generate(100, (int index) => index); final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[]; final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{}; final Set<int> tappedChildren = <int>{};
...@@ -1845,7 +1870,7 @@ void main() { ...@@ -1845,7 +1870,7 @@ void main() {
}); });
}); });
testWidgets('ListWheelScrollView creates only one opacity layer for all children', (WidgetTester tester) async { testWidgetsWithLeakTracking('ListWheelScrollView creates only one opacity layer for all children', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
ListWheelScrollView( ListWheelScrollView(
overAndUnderCenterOpacity: 0.5, overAndUnderCenterOpacity: 0.5,
......
...@@ -121,7 +121,7 @@ class SlideInOutPageRoute<T> extends PageRouteBuilder<T> { ...@@ -121,7 +121,7 @@ class SlideInOutPageRoute<T> extends PageRouteBuilder<T> {
} }
void main() { void main() {
testWidgets('Can navigator navigate to and from a stateful widget', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can navigator navigate to and from a stateful widget', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(), // X '/': (BuildContext context) => const FirstWidget(), // X
'/second': (BuildContext context) => const SecondWidget(), // Y '/second': (BuildContext context) => const SecondWidget(), // Y
...@@ -171,7 +171,7 @@ void main() { ...@@ -171,7 +171,7 @@ void main() {
expect(find.text('Y', skipOffstage: false), findsNothing); expect(find.text('Y', skipOffstage: false), findsNothing);
}); });
testWidgets('Navigator.of fails gracefully when not found in context', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.of fails gracefully when not found in context', (WidgetTester tester) async {
const Key targetKey = Key('foo'); const Key targetKey = Key('foo');
dynamic exception; dynamic exception;
final Widget widget = ThirdWidget( final Widget widget = ThirdWidget(
...@@ -186,7 +186,7 @@ void main() { ...@@ -186,7 +186,7 @@ void main() {
expect('$exception', startsWith('Navigator operation requested with a context')); expect('$exception', startsWith('Navigator operation requested with a context'));
}); });
testWidgets('Navigator can push Route created through page class as Pageless route', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator can push Route created through page class as Pageless route', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -206,7 +206,7 @@ void main() { ...@@ -206,7 +206,7 @@ void main() {
expect(find.text('home'), findsOneWidget); expect(find.text('home'), findsOneWidget);
}); });
testWidgets('Navigator can set clip behavior', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator can set clip behavior', (WidgetTester tester) async {
const MaterialPage<void> page = MaterialPage<void>(child: Text('page')); const MaterialPage<void> page = MaterialPage<void>(child: Text('page'));
await tester.pumpWidget( await tester.pumpWidget(
MediaQuery( MediaQuery(
...@@ -239,7 +239,7 @@ void main() { ...@@ -239,7 +239,7 @@ void main() {
expect(tester.widget<Overlay>(find.byType(Overlay)).clipBehavior, Clip.none); expect(tester.widget<Overlay>(find.byType(Overlay)).clipBehavior, Clip.none);
}); });
testWidgets('Zero transition page-based route correctly notifies observers when it is popped', (WidgetTester tester) async { testWidgetsWithLeakTracking('Zero transition page-based route correctly notifies observers when it is popped', (WidgetTester tester) async {
final List<Page<void>> pages = <Page<void>>[ final List<Page<void>> pages = <Page<void>>[
const ZeroTransitionPage(name: 'Page 1'), const ZeroTransitionPage(name: 'Page 1'),
const ZeroTransitionPage(name: 'Page 2'), const ZeroTransitionPage(name: 'Page 2'),
...@@ -279,7 +279,7 @@ void main() { ...@@ -279,7 +279,7 @@ void main() {
expect(observations[0].previous, 'Page 1'); expect(observations[0].previous, 'Page 1');
}); });
testWidgets('Navigator.of rootNavigator finds root Navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.of rootNavigator finds root Navigator', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Material( home: Material(
child: Column( child: Column(
...@@ -347,7 +347,7 @@ void main() { ...@@ -347,7 +347,7 @@ void main() {
expect(tester.getTopLeft(find.text('Dialog')).dy, 0.0); expect(tester.getTopLeft(find.text('Dialog')).dy, 0.0);
}); });
testWidgets('Gestures between push and build are ignored', (WidgetTester tester) async { testWidgetsWithLeakTracking('Gestures between push and build are ignored', (WidgetTester tester) async {
final List<String> log = <String>[]; final List<String> log = <String>[];
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) { '/': (BuildContext context) {
...@@ -377,7 +377,7 @@ void main() { ...@@ -377,7 +377,7 @@ void main() {
expect(log, equals(<String>['left'])); expect(log, equals(<String>['left']));
}); });
testWidgets('pushnamed can handle Object as type', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushnamed can handle Object as type', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const Text('/'), '/': (BuildContext context) => const Text('/'),
...@@ -397,7 +397,7 @@ void main() { ...@@ -397,7 +397,7 @@ void main() {
expect(find.text('/second'), findsOneWidget); expect(find.text('/second'), findsOneWidget);
}); });
testWidgets('Pending gestures are rejected', (WidgetTester tester) async { testWidgetsWithLeakTracking('Pending gestures are rejected', (WidgetTester tester) async {
final List<String> log = <String>[]; final List<String> log = <String>[];
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) { '/': (BuildContext context) {
...@@ -428,7 +428,7 @@ void main() { ...@@ -428,7 +428,7 @@ void main() {
expect(log, equals(<String>['left'])); expect(log, equals(<String>['left']));
}); });
testWidgets('popAndPushNamed', (WidgetTester tester) async { testWidgetsWithLeakTracking('popAndPushNamed', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed(context, '/B'); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed(context, '/B'); }),
...@@ -455,7 +455,7 @@ void main() { ...@@ -455,7 +455,7 @@ void main() {
expect(find.text('B'), findsOneWidget); expect(find.text('B'), findsOneWidget);
}); });
testWidgets('popAndPushNamed with explicit void type parameter', (WidgetTester tester) async { testWidgetsWithLeakTracking('popAndPushNamed with explicit void type parameter', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed<void>(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed<void>(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed<void, void>(context, '/B'); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.popAndPushNamed<void, void>(context, '/B'); }),
...@@ -482,7 +482,7 @@ void main() { ...@@ -482,7 +482,7 @@ void main() {
expect(find.text('B'), findsOneWidget); expect(find.text('B'), findsOneWidget);
}); });
testWidgets('Push and pop should trigger the observers', (WidgetTester tester) async { testWidgetsWithLeakTracking('Push and pop should trigger the observers', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
...@@ -542,7 +542,7 @@ void main() { ...@@ -542,7 +542,7 @@ void main() {
expect(isPopped, isTrue); expect(isPopped, isTrue);
}); });
testWidgets('Add and remove an observer should work', (WidgetTester tester) async { testWidgetsWithLeakTracking('Add and remove an observer should work', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
...@@ -589,7 +589,7 @@ void main() { ...@@ -589,7 +589,7 @@ void main() {
expect(isPopped, isFalse); expect(isPopped, isFalse);
}); });
testWidgets('initial route trigger observer in the right order', (WidgetTester tester) async { testWidgetsWithLeakTracking('initial route trigger observer in the right order', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const Text('/'), '/' : (BuildContext context) => const Text('/'),
'/A': (BuildContext context) => const Text('A'), '/A': (BuildContext context) => const Text('A'),
...@@ -663,7 +663,7 @@ void main() { ...@@ -663,7 +663,7 @@ void main() {
MemoryAllocations.instance.removeListener(listener); MemoryAllocations.instance.removeListener(listener);
}); });
testWidgets('Route didAdd and dispose in same frame work', (WidgetTester tester) async { testWidgetsWithLeakTracking('Route didAdd and dispose in same frame work', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/61346. // Regression Test for https://github.com/flutter/flutter/issues/61346.
Widget buildNavigator() { Widget buildNavigator() {
return Navigator( return Navigator(
...@@ -676,6 +676,8 @@ void main() { ...@@ -676,6 +676,8 @@ void main() {
); );
} }
final TabController controller = TabController(length: 3, vsync: tester); final TabController controller = TabController(length: 3, vsync: tester);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
TestDependencies( TestDependencies(
child: TabBarView( child: TabBarView(
...@@ -694,7 +696,7 @@ void main() { ...@@ -694,7 +696,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('Page-based route pop before push finishes', (WidgetTester tester) async { testWidgetsWithLeakTracking('Page-based route pop before push finishes', (WidgetTester tester) async {
List<Page<void>> pages = <Page<void>>[const MaterialPage<void>(child: Text('Page 1'))]; List<Page<void>> pages = <Page<void>>[const MaterialPage<void>(child: Text('Page 1'))];
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
Widget buildNavigator() { Widget buildNavigator() {
...@@ -730,7 +732,7 @@ void main() { ...@@ -730,7 +732,7 @@ void main() {
expect(find.text('Page 1'), findsOneWidget); expect(find.text('Page 1'), findsOneWidget);
}); });
testWidgets('Pages update does update overlay correctly', (WidgetTester tester) async { testWidgetsWithLeakTracking('Pages update does update overlay correctly', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/64941. // Regression Test for https://github.com/flutter/flutter/issues/64941.
List<Page<void>> pages = const <Page<void>>[ List<Page<void>> pages = const <Page<void>>[
MaterialPage<void>( MaterialPage<void>(
...@@ -779,7 +781,7 @@ void main() { ...@@ -779,7 +781,7 @@ void main() {
expect(find.text('page 0'), findsNothing); expect(find.text('page 0'), findsNothing);
}); });
testWidgets('replaceNamed replaces', (WidgetTester tester) async { testWidgetsWithLeakTracking('replaceNamed replaces', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushReplacementNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushReplacementNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushReplacementNamed(context, '/B'); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushReplacementNamed(context, '/B'); }),
...@@ -801,7 +803,7 @@ void main() { ...@@ -801,7 +803,7 @@ void main() {
expect(find.text('B'), findsOneWidget); expect(find.text('B'), findsOneWidget);
}); });
testWidgets('pushReplacement sets secondaryAnimation after transition, with history change during transition', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushReplacement sets secondaryAnimation after transition, with history change during transition', (WidgetTester tester) async {
final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{}; final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{};
final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage( '/' : (BuildContext context) => OnTapPage(
...@@ -858,7 +860,7 @@ void main() { ...@@ -858,7 +860,7 @@ void main() {
expect(routes['/A']!.secondaryAnimation!.value, equals(routes['/C']!.animation!.value)); expect(routes['/A']!.secondaryAnimation!.value, equals(routes['/C']!.animation!.value));
}); });
testWidgets('new route removed from navigator history during pushReplacement transition', (WidgetTester tester) async { testWidgetsWithLeakTracking('new route removed from navigator history during pushReplacement transition', (WidgetTester tester) async {
final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{}; final Map<String, SlideInOutPageRoute<dynamic>> routes = <String, SlideInOutPageRoute<dynamic>>{};
final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> builders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage( '/' : (BuildContext context) => OnTapPage(
...@@ -904,7 +906,7 @@ void main() { ...@@ -904,7 +906,7 @@ void main() {
expect(routes['/']!.animation!.value, equals(1.0)); expect(routes['/']!.animation!.value, equals(1.0));
}); });
testWidgets('pushReplacement triggers secondaryAnimation', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushReplacement triggers secondaryAnimation', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage( '/' : (BuildContext context) => OnTapPage(
id: '/', id: '/',
...@@ -953,7 +955,7 @@ void main() { ...@@ -953,7 +955,7 @@ void main() {
expect(aOffset.dx, lessThan(aOffsetOriginal.dx)); expect(aOffset.dx, lessThan(aOffsetOriginal.dx));
}); });
testWidgets('pushReplacement correctly reports didReplace to the observer', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushReplacement correctly reports didReplace to the observer', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/56892. // Regression test for https://github.com/flutter/flutter/issues/56892.
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const OnTapPage( '/' : (BuildContext context) => const OnTapPage(
...@@ -1021,7 +1023,7 @@ void main() { ...@@ -1021,7 +1023,7 @@ void main() {
expect(find.text('C'), isOnstage); expect(find.text('C'), isOnstage);
}); });
testWidgets('Able to pop all routes', (WidgetTester tester) async { testWidgetsWithLeakTracking('Able to pop all routes', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => const OnTapPage( '/' : (BuildContext context) => const OnTapPage(
id: '/', id: '/',
...@@ -1048,7 +1050,7 @@ void main() { ...@@ -1048,7 +1050,7 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('pushAndRemoveUntil triggers secondaryAnimation', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushAndRemoveUntil triggers secondaryAnimation', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage( '/' : (BuildContext context) => OnTapPage(
id: '/', id: '/',
...@@ -1102,7 +1104,7 @@ void main() { ...@@ -1102,7 +1104,7 @@ void main() {
expect(find.text('B'), isOnstage); expect(find.text('B'), isOnstage);
}); });
testWidgets('pushAndRemoveUntil does not remove routes below the first route that pass the predicate', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushAndRemoveUntil does not remove routes below the first route that pass the predicate', (WidgetTester tester) async {
// Regression https://github.com/flutter/flutter/issues/56688 // Regression https://github.com/flutter/flutter/issues/56688
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
...@@ -1138,7 +1140,7 @@ void main() { ...@@ -1138,7 +1140,7 @@ void main() {
expect(find.text('home'), isOnstage); expect(find.text('home'), isOnstage);
}); });
testWidgets('replaceNamed returned value', (WidgetTester tester) async { testWidgetsWithLeakTracking('replaceNamed returned value', (WidgetTester tester) async {
late Future<String?> value; late Future<String?> value;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
...@@ -1187,7 +1189,7 @@ void main() { ...@@ -1187,7 +1189,7 @@ void main() {
expect(replaceNamedValue, 'B'); expect(replaceNamedValue, 'B');
}); });
testWidgets('removeRoute', (WidgetTester tester) async { testWidgetsWithLeakTracking('removeRoute', (WidgetTester tester) async {
final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushNamed(context, '/B'); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pushNamed(context, '/B'); }),
...@@ -1273,7 +1275,7 @@ void main() { ...@@ -1273,7 +1275,7 @@ void main() {
expect(previousRoute, routes['/']); expect(previousRoute, routes['/']);
}); });
testWidgets('remove a route whose value is awaited', (WidgetTester tester) async { testWidgetsWithLeakTracking('remove a route whose value is awaited', (WidgetTester tester) async {
late Future<String?> pageValue; late Future<String?> pageValue;
final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> pageBuilders = <String, WidgetBuilder>{
'/': (BuildContext context) => OnTapPage(id: '/', onTap: () { pageValue = Navigator.pushNamed(context, '/A'); }), '/': (BuildContext context) => OnTapPage(id: '/', onTap: () { pageValue = Navigator.pushNamed(context, '/A'); }),
...@@ -1301,7 +1303,7 @@ void main() { ...@@ -1301,7 +1303,7 @@ void main() {
navigator.removeRoute(routes['/A']!); // stack becomes /, pageValue will not complete navigator.removeRoute(routes['/A']!); // stack becomes /, pageValue will not complete
}); });
testWidgets('replacing route can be observed', (WidgetTester tester) async { testWidgetsWithLeakTracking('replacing route can be observed', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[]; final List<String> log = <String>[];
final TestObserver observer = TestObserver() final TestObserver observer = TestObserver()
...@@ -1371,7 +1373,7 @@ void main() { ...@@ -1371,7 +1373,7 @@ void main() {
expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)', 'pushed C (previous is B)', 'replaced B with D']); expect(log, <String>['pushed / (previous is <none>)', 'pushed B (previous is /)', 'pushed C (previous is B)', 'replaced B with D']);
}); });
testWidgets('didStartUserGesture observable', (WidgetTester tester) async { testWidgetsWithLeakTracking('didStartUserGesture observable', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }), '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { Navigator.pushNamed(context, '/A'); }),
'/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }), '/A': (BuildContext context) => OnTapPage(id: 'A', onTap: () { Navigator.pop(context); }),
...@@ -1402,7 +1404,7 @@ void main() { ...@@ -1402,7 +1404,7 @@ void main() {
expect(observedPreviousRoute.settings.name, '/'); expect(observedPreviousRoute.settings.name, '/');
}); });
testWidgets('ModalRoute.of sets up a route to rebuild if its state changes', (WidgetTester tester) async { testWidgetsWithLeakTracking('ModalRoute.of sets up a route to rebuild if its state changes', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[]; final List<String> log = <String>[];
late Route<void> routeB; late Route<void> routeB;
...@@ -1469,7 +1471,7 @@ void main() { ...@@ -1469,7 +1471,7 @@ void main() {
expect(log, <String>['building B', 'building C', 'found C', 'building D']); expect(log, <String>['building B', 'building C', 'found C', 'building D']);
}); });
testWidgets("Routes don't rebuild just because their animations ended", (WidgetTester tester) async { testWidgetsWithLeakTracking("Routes don't rebuild just because their animations ended", (WidgetTester tester) async {
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
final List<String> log = <String>[]; final List<String> log = <String>[];
Route<dynamic>? nextRoute = PageRouteBuilder<int>( Route<dynamic>? nextRoute = PageRouteBuilder<int>(
...@@ -1512,7 +1514,7 @@ void main() { ...@@ -1512,7 +1514,7 @@ void main() {
expect(log, <String>['building page 1 - false', 'building page 2 - false', 'building page 3 - false']); expect(log, <String>['building page 1 - false', 'building page 2 - false', 'building page 3 - false']);
}); });
testWidgets('route semantics', (WidgetTester tester) async { testWidgetsWithLeakTracking('route semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => OnTapPage(id: '1', onTap: () { Navigator.pushNamed(context, '/A'); }), '/': (BuildContext context) => OnTapPage(id: '1', onTap: () { Navigator.pushNamed(context, '/A'); }),
...@@ -1569,7 +1571,7 @@ void main() { ...@@ -1569,7 +1571,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('arguments for named routes on Navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('arguments for named routes on Navigator', (WidgetTester tester) async {
late GlobalKey currentRouteKey; late GlobalKey currentRouteKey;
final List<Object?> arguments = <Object?>[]; final List<Object?> arguments = <Object?>[];
...@@ -1643,7 +1645,7 @@ void main() { ...@@ -1643,7 +1645,7 @@ void main() {
arguments.clear(); arguments.clear();
}); });
testWidgets('arguments for named routes on NavigatorState', (WidgetTester tester) async { testWidgetsWithLeakTracking('arguments for named routes on NavigatorState', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final List<Object?> arguments = <Object?>[]; final List<Object?> arguments = <Object?>[];
...@@ -1714,7 +1716,7 @@ void main() { ...@@ -1714,7 +1716,7 @@ void main() {
arguments.clear(); arguments.clear();
}); });
testWidgets('Initial route can have gaps', (WidgetTester tester) async { testWidgetsWithLeakTracking('Initial route can have gaps', (WidgetTester tester) async {
final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>();
const Key keyRoot = Key('Root'); const Key keyRoot = Key('Root');
const Key keyA = Key('A'); const Key keyA = Key('A');
...@@ -1745,7 +1747,7 @@ void main() { ...@@ -1745,7 +1747,7 @@ void main() {
expect(find.byKey(keyABC, skipOffstage: false), findsNothing); expect(find.byKey(keyABC, skipOffstage: false), findsNothing);
}); });
testWidgets('The full initial route has to be matched', (WidgetTester tester) async { testWidgetsWithLeakTracking('The full initial route has to be matched', (WidgetTester tester) async {
final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> keyNav = GlobalKey<NavigatorState>();
const Key keyRoot = Key('Root'); const Key keyRoot = Key('Root');
const Key keyA = Key('A'); const Key keyA = Key('A');
...@@ -1775,7 +1777,7 @@ void main() { ...@@ -1775,7 +1777,7 @@ void main() {
expect(find.byKey(keyAB), findsNothing); expect(find.byKey(keyAB), findsNothing);
}); });
testWidgets("Popping immediately after pushing doesn't crash", (WidgetTester tester) async { testWidgetsWithLeakTracking("Popping immediately after pushing doesn't crash", (WidgetTester tester) async {
// Added this test to protect against regression of https://github.com/flutter/flutter/issues/45539 // Added this test to protect against regression of https://github.com/flutter/flutter/issues/45539
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/' : (BuildContext context) => OnTapPage(id: '/', onTap: () { '/' : (BuildContext context) => OnTapPage(id: '/', onTap: () {
...@@ -1824,7 +1826,7 @@ void main() { ...@@ -1824,7 +1826,7 @@ void main() {
}); });
group('error control test', () { group('error control test', () {
testWidgets('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async { testWidgetsWithLeakTracking('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(Navigator( await tester.pumpWidget(Navigator(
key: navigatorKey, key: navigatorKey,
...@@ -1850,7 +1852,7 @@ void main() { ...@@ -1850,7 +1852,7 @@ void main() {
); );
}); });
testWidgets('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async { testWidgetsWithLeakTracking('onUnknownRoute null and onGenerateRoute returns null', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(Navigator( await tester.pumpWidget(Navigator(
key: navigatorKey, key: navigatorKey,
...@@ -1877,7 +1879,7 @@ void main() { ...@@ -1877,7 +1879,7 @@ void main() {
}); });
}); });
testWidgets('OverlayEntry of topmost initial route is marked as opaque', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntry of topmost initial route is marked as opaque', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038. // Regression test for https://github.com/flutter/flutter/issues/38038.
final Key root = UniqueKey(); final Key root = UniqueKey();
...@@ -1901,7 +1903,7 @@ void main() { ...@@ -1901,7 +1903,7 @@ void main() {
expect(find.byKey(topmost), findsOneWidget); expect(find.byKey(topmost), findsOneWidget);
}); });
testWidgets('OverlayEntry of topmost route is set to opaque after Push', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntry of topmost route is set to opaque after Push', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038. // Regression test for https://github.com/flutter/flutter/issues/38038.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
...@@ -1928,7 +1930,7 @@ void main() { ...@@ -1928,7 +1930,7 @@ void main() {
expect(find.byKey(const ValueKey<String>('/A')), findsOneWidget); expect(find.byKey(const ValueKey<String>('/A')), findsOneWidget);
}); });
testWidgets('OverlayEntry of topmost route is set to opaque after Replace', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntry of topmost route is set to opaque after Replace', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/38038. // Regression test for https://github.com/flutter/flutter/issues/38038.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
...@@ -1973,7 +1975,7 @@ void main() { ...@@ -1973,7 +1975,7 @@ void main() {
expect(find.byKey(const ValueKey<String>('/C')), findsOneWidget); expect(find.byKey(const ValueKey<String>('/C')), findsOneWidget);
}); });
testWidgets('Pushing opaque Route does not rebuild routes below', (WidgetTester tester) async { testWidgetsWithLeakTracking('Pushing opaque Route does not rebuild routes below', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797. // Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
...@@ -2007,7 +2009,7 @@ void main() { ...@@ -2007,7 +2009,7 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(topRoute)).rebuildCount, 1); expect(tester.state<StatefulTestState>(find.byKey(topRoute)).rebuildCount, 1);
}); });
testWidgets('initial routes below opaque route are offstage', (WidgetTester tester) async { testWidgetsWithLeakTracking('initial routes below opaque route are offstage', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
TestDependencies( TestDependencies(
...@@ -2048,7 +2050,7 @@ void main() { ...@@ -2048,7 +2050,7 @@ void main() {
expect(find.text('+/a/b+'), findsNothing); expect(find.text('+/a/b+'), findsNothing);
}); });
testWidgets('Can provide custom onGenerateInitialRoutes', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can provide custom onGenerateInitialRoutes', (WidgetTester tester) async {
bool onGenerateInitialRoutesCalled = false; bool onGenerateInitialRoutesCalled = false;
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2081,7 +2083,7 @@ void main() { ...@@ -2081,7 +2083,7 @@ void main() {
expect(find.text('World'), findsNothing); expect(find.text('World'), findsNothing);
}); });
testWidgets('Navigator.of able to handle input context is a navigator context', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.of able to handle input context is a navigator context', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -2094,7 +2096,7 @@ void main() { ...@@ -2094,7 +2096,7 @@ void main() {
expect(state, testKey.currentState); expect(state, testKey.currentState);
}); });
testWidgets('Navigator.of able to handle input context is a navigator context - root navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.of able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2116,7 +2118,7 @@ void main() { ...@@ -2116,7 +2118,7 @@ void main() {
expect(state, root.currentState); expect(state, root.currentState);
}); });
testWidgets('Navigator.maybeOf throws when there is no navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.maybeOf throws when there is no navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(SizedBox(key: testKey)); await tester.pumpWidget(SizedBox(key: testKey));
...@@ -2125,7 +2127,7 @@ void main() { ...@@ -2125,7 +2127,7 @@ void main() {
}, throwsFlutterError); }, throwsFlutterError);
}); });
testWidgets('Navigator.maybeOf works when there is no navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.maybeOf works when there is no navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget(SizedBox(key: testKey)); await tester.pumpWidget(SizedBox(key: testKey));
...@@ -2133,7 +2135,7 @@ void main() { ...@@ -2133,7 +2135,7 @@ void main() {
expect(state, isNull); expect(state, isNull);
}); });
testWidgets('Navigator.maybeOf able to handle input context is a navigator context', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.maybeOf able to handle input context is a navigator context', (WidgetTester tester) async {
final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> testKey = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -2147,7 +2149,7 @@ void main() { ...@@ -2147,7 +2149,7 @@ void main() {
expect(state, testKey.currentState); expect(state, testKey.currentState);
}); });
testWidgets('Navigator.maybeOf able to handle input context is a navigator context - root navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator.maybeOf able to handle input context is a navigator context - root navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> root = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2170,7 +2172,7 @@ void main() { ...@@ -2170,7 +2172,7 @@ void main() {
expect(state, root.currentState); expect(state, root.currentState);
}); });
testWidgets('pushAndRemove until animates the push', (WidgetTester tester) async { testWidgetsWithLeakTracking('pushAndRemove until animates the push', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/25080. // Regression test for https://github.com/flutter/flutter/issues/25080.
const Duration kFourTenthsOfTheTransitionDuration = Duration(milliseconds: 120); const Duration kFourTenthsOfTheTransitionDuration = Duration(milliseconds: 120);
...@@ -2252,7 +2254,7 @@ void main() { ...@@ -2252,7 +2254,7 @@ void main() {
expect(find.text('Route: 4', skipOffstage: false), findsNothing); expect(find.text('Route: 4', skipOffstage: false), findsNothing);
}); });
testWidgets('Wrapping TickerMode can turn off ticking in routes', (WidgetTester tester) async { testWidgetsWithLeakTracking('Wrapping TickerMode can turn off ticking in routes', (WidgetTester tester) async {
int tickCount = 0; int tickCount = 0;
Widget widgetUnderTest({required bool enabled}) { Widget widgetUnderTest({required bool enabled}) {
return TickerMode( return TickerMode(
...@@ -2345,7 +2347,7 @@ void main() { ...@@ -2345,7 +2347,7 @@ void main() {
expect(popNextOfFirst, secondRoute); expect(popNextOfFirst, secondRoute);
}); });
testWidgets('hero controller scope works', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller scope works', (WidgetTester tester) async {
final GlobalKey<NavigatorState> top = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> top = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> sub = GlobalKey<NavigatorState>();
...@@ -2415,7 +2417,7 @@ void main() { ...@@ -2415,7 +2417,7 @@ void main() {
expect(observations[1].previous, 'top1'); expect(observations[1].previous, 'top1');
}); });
testWidgets('hero controller can correctly transfer subscription - replacing navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller can correctly transfer subscription - replacing navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>();
...@@ -2484,7 +2486,7 @@ void main() { ...@@ -2484,7 +2486,7 @@ void main() {
expect(observations[0].previous, 'navigator2'); expect(observations[0].previous, 'navigator2');
}); });
testWidgets('hero controller can correctly transfer subscription - swapping navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller can correctly transfer subscription - swapping navigator', (WidgetTester tester) async {
final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>();
...@@ -2624,7 +2626,7 @@ void main() { ...@@ -2624,7 +2626,7 @@ void main() {
expect(observations2[1].previous, 'navigator1'); expect(observations2[1].previous, 'navigator1');
}); });
testWidgets('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller subscribes to multiple navigators does throw', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy(); final HeroControllerSpy spy = HeroControllerSpy();
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
...@@ -2662,7 +2664,7 @@ void main() { ...@@ -2662,7 +2664,7 @@ void main() {
expect(tester.takeException(), isAssertionError); expect(tester.takeException(), isAssertionError);
}); });
testWidgets('hero controller throws has correct error message', (WidgetTester tester) async { testWidgetsWithLeakTracking('hero controller throws has correct error message', (WidgetTester tester) async {
final HeroControllerSpy spy = HeroControllerSpy(); final HeroControllerSpy spy = HeroControllerSpy();
await tester.pumpWidget( await tester.pumpWidget(
HeroControllerScope( HeroControllerScope(
...@@ -2746,7 +2748,7 @@ void main() { ...@@ -2746,7 +2748,7 @@ void main() {
); );
} }
testWidgets('can initialize with pages list', (WidgetTester tester) async { testWidgetsWithLeakTracking('can initialize with pages list', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final List<TestPage> myPages = <TestPage>[ final List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'), const TestPage(key: ValueKey<String>('1'), name:'initial'),
...@@ -2784,7 +2786,7 @@ void main() { ...@@ -2784,7 +2786,7 @@ void main() {
expect(find.text('initial'), findsOneWidget); expect(find.text('initial'), findsOneWidget);
}); });
testWidgets('can handle duplicate page key if update before transition finishes', (WidgetTester tester) async { testWidgetsWithLeakTracking('can handle duplicate page key if update before transition finishes', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/97363. // Regression test for https://github.com/flutter/flutter/issues/97363.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final List<TestPage> myPages1 = <TestPage>[ final List<TestPage> myPages1 = <TestPage>[
...@@ -2840,7 +2842,7 @@ void main() { ...@@ -2840,7 +2842,7 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('throw if onPopPage callback is not provided', (WidgetTester tester) async { testWidgetsWithLeakTracking('throw if onPopPage callback is not provided', (WidgetTester tester) async {
final List<TestPage> myPages = <TestPage>[ final List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'), const TestPage(key: ValueKey<String>('1'), name:'initial'),
const TestPage(key: ValueKey<String>('2'), name:'second'), const TestPage(key: ValueKey<String>('2'), name:'second'),
...@@ -2910,7 +2912,7 @@ void main() { ...@@ -2910,7 +2912,7 @@ void main() {
); );
}); });
testWidgets('Can pop route with local history entries using page api', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can pop route with local history entries using page api', (WidgetTester tester) async {
List<Page<void>> myPages = const <Page<void>>[ List<Page<void>> myPages = const <Page<void>>[
MaterialPage<void>(child: Text('page1')), MaterialPage<void>(child: Text('page1')),
MaterialPage<void>(child: Text('page2')), MaterialPage<void>(child: Text('page2')),
...@@ -2961,7 +2963,7 @@ void main() { ...@@ -2961,7 +2963,7 @@ void main() {
expect(entryRemoved, isTrue); expect(entryRemoved, isTrue);
}); });
testWidgets('ModalRoute must comply with willHandlePopInternally when there is a PopScope', (WidgetTester tester) async { testWidgetsWithLeakTracking('ModalRoute must comply with willHandlePopInternally when there is a PopScope', (WidgetTester tester) async {
const List<Page<void>> myPages = <Page<void>>[ const List<Page<void>> myPages = <Page<void>>[
MaterialPage<void>(child: Text('page1')), MaterialPage<void>(child: Text('page1')),
MaterialPage<void>( MaterialPage<void>(
...@@ -2994,7 +2996,7 @@ void main() { ...@@ -2994,7 +2996,7 @@ void main() {
expect(route.didPop(null), true); expect(route.didPop(null), true);
}); });
testWidgets('can push and pop pages using page api', (WidgetTester tester) async { testWidgetsWithLeakTracking('can push and pop pages using page api', (WidgetTester tester) async {
late Animation<double> secondaryAnimationOfRouteOne; late Animation<double> secondaryAnimationOfRouteOne;
late Animation<double> primaryAnimationOfRouteOne; late Animation<double> primaryAnimationOfRouteOne;
late Animation<double> secondaryAnimationOfRouteTwo; late Animation<double> secondaryAnimationOfRouteTwo;
...@@ -3143,7 +3145,7 @@ void main() { ...@@ -3143,7 +3145,7 @@ void main() {
expect(primaryAnimationOfRouteThree.status, AnimationStatus.dismissed); expect(primaryAnimationOfRouteThree.status, AnimationStatus.dismissed);
}); });
testWidgets('can modify routes history and secondary animation still works', (WidgetTester tester) async { testWidgetsWithLeakTracking('can modify routes history and secondary animation still works', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
late Animation<double> secondaryAnimationOfRouteOne; late Animation<double> secondaryAnimationOfRouteOne;
late Animation<double> primaryAnimationOfRouteOne; late Animation<double> primaryAnimationOfRouteOne;
...@@ -3254,7 +3256,7 @@ void main() { ...@@ -3254,7 +3256,7 @@ void main() {
expect(primaryAnimationOfRouteOne.status, AnimationStatus.dismissed); expect(primaryAnimationOfRouteOne.status, AnimationStatus.dismissed);
}); });
testWidgets('Pop no animation page does not crash', (WidgetTester tester) async { testWidgetsWithLeakTracking('Pop no animation page does not crash', (WidgetTester tester) async {
// Regression Test for https://github.com/flutter/flutter/issues/86604. // Regression Test for https://github.com/flutter/flutter/issues/86604.
Widget buildNavigator(bool secondPage) { Widget buildNavigator(bool secondPage) {
return TestDependencies( return TestDependencies(
...@@ -3279,7 +3281,7 @@ void main() { ...@@ -3279,7 +3281,7 @@ void main() {
expect(find.text('page1'), findsOneWidget); expect(find.text('page1'), findsOneWidget);
}); });
testWidgets('can work with pageless route', (WidgetTester tester) async { testWidgetsWithLeakTracking('can work with pageless route', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[ List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'), const TestPage(key: ValueKey<String>('1'), name:'initial'),
...@@ -3427,7 +3429,7 @@ void main() { ...@@ -3427,7 +3429,7 @@ void main() {
expect(myPages.length, 1); expect(myPages.length, 1);
}); });
testWidgets('complex case 1', (WidgetTester tester) async { testWidgetsWithLeakTracking('complex case 1', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[ List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'), const TestPage(key: ValueKey<String>('1'), name: 'initial'),
...@@ -3564,7 +3566,7 @@ void main() { ...@@ -3564,7 +3566,7 @@ void main() {
}); });
//Regression test for https://github.com/flutter/flutter/issues/115887 //Regression test for https://github.com/flutter/flutter/issues/115887
testWidgets('Complex case 2', (WidgetTester tester) async { testWidgetsWithLeakTracking('Complex case 2', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[ List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'initial'), const TestPage(key: ValueKey<String>('1'), name:'initial'),
...@@ -3628,7 +3630,7 @@ void main() { ...@@ -3628,7 +3630,7 @@ void main() {
expect(find.text('second-pageless1'), findsNothing); expect(find.text('second-pageless1'), findsNothing);
}); });
testWidgets('complex case 1 - with always remove transition delegate', (WidgetTester tester) async { testWidgetsWithLeakTracking('complex case 1 - with always remove transition delegate', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
final AlwaysRemoveTransitionDelegate transitionDelegate = AlwaysRemoveTransitionDelegate(); final AlwaysRemoveTransitionDelegate transitionDelegate = AlwaysRemoveTransitionDelegate();
List<TestPage> myPages = <TestPage>[ List<TestPage> myPages = <TestPage>[
...@@ -3773,7 +3775,7 @@ void main() { ...@@ -3773,7 +3775,7 @@ void main() {
expect(find.text('forth'), findsOneWidget); expect(find.text('forth'), findsOneWidget);
}); });
testWidgets('can repush a page that was previously popped before it has finished popping', (WidgetTester tester) async { testWidgetsWithLeakTracking('can repush a page that was previously popped before it has finished popping', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[ List<Page<dynamic>> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'), const TestPage(key: ValueKey<String>('1'), name: 'initial'),
...@@ -3825,7 +3827,7 @@ void main() { ...@@ -3825,7 +3827,7 @@ void main() {
expect(find.text('second'), findsOneWidget); expect(find.text('second'), findsOneWidget);
}); });
testWidgets('can update pages before a route has finished popping', (WidgetTester tester) async { testWidgetsWithLeakTracking('can update pages before a route has finished popping', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[ List<Page<dynamic>> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name: 'initial'), const TestPage(key: ValueKey<String>('1'), name: 'initial'),
...@@ -3876,7 +3878,7 @@ void main() { ...@@ -3876,7 +3878,7 @@ void main() {
expect(find.text('initial'), findsOneWidget); expect(find.text('initial'), findsOneWidget);
}); });
testWidgets('can update pages before a pageless route has finished popping', (WidgetTester tester) async { testWidgetsWithLeakTracking('can update pages before a pageless route has finished popping', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/68162. // Regression test for https://github.com/flutter/flutter/issues/68162.
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<Page<dynamic>> myPages = <TestPage>[ List<Page<dynamic>> myPages = <TestPage>[
...@@ -3923,7 +3925,7 @@ void main() { ...@@ -3923,7 +3925,7 @@ void main() {
expect(find.text('initial'), findsOneWidget); expect(find.text('initial'), findsOneWidget);
}); });
testWidgets('pages remove and add trigger observer in the right order', (WidgetTester tester) async { testWidgetsWithLeakTracking('pages remove and add trigger observer in the right order', (WidgetTester tester) async {
final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
List<TestPage> myPages = <TestPage>[ List<TestPage> myPages = <TestPage>[
const TestPage(key: ValueKey<String>('1'), name:'first'), const TestPage(key: ValueKey<String>('1'), name:'first'),
...@@ -4016,7 +4018,7 @@ void main() { ...@@ -4016,7 +4018,7 @@ void main() {
}); });
}); });
testWidgets('Can reuse NavigatorObserver in rebuilt tree', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can reuse NavigatorObserver in rebuilt tree', (WidgetTester tester) async {
final NavigatorObserver observer = NavigatorObserver(); final NavigatorObserver observer = NavigatorObserver();
Widget build([Key? key]) { Widget build([Key? key]) {
return TestDependencies( return TestDependencies(
...@@ -4051,7 +4053,7 @@ void main() { ...@@ -4051,7 +4053,7 @@ void main() {
expect(observer.navigator, tester.state<NavigatorState>(find.byType(Navigator))); expect(observer.navigator, tester.state<NavigatorState>(find.byType(Navigator)));
}); });
testWidgets('Navigator requests focus if requestFocus is true', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator requests focus if requestFocus is true', (WidgetTester tester) async {
final GlobalKey navigatorKey = GlobalKey(); final GlobalKey navigatorKey = GlobalKey();
final GlobalKey innerKey = GlobalKey(); final GlobalKey innerKey = GlobalKey();
final Map<String, Widget> routes = <String, Widget>{ final Map<String, Widget> routes = <String, Widget>{
...@@ -4060,6 +4062,7 @@ void main() { ...@@ -4060,6 +4062,7 @@ void main() {
}; };
late final NavigatorState navigator = navigatorKey.currentState! as NavigatorState; late final NavigatorState navigator = navigatorKey.currentState! as NavigatorState;
final FocusScopeNode focusNode = FocusScopeNode(); final FocusScopeNode focusNode = FocusScopeNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(Column( await tester.pumpWidget(Column(
children: <Widget>[ children: <Widget>[
...@@ -4126,7 +4129,7 @@ void main() { ...@@ -4126,7 +4129,7 @@ void main() {
expect(focusNode.hasFocus, true); expect(focusNode.hasFocus, true);
}); });
testWidgets('Navigator does not request focus if requestFocus is false', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator does not request focus if requestFocus is false', (WidgetTester tester) async {
final GlobalKey navigatorKey = GlobalKey(); final GlobalKey navigatorKey = GlobalKey();
final GlobalKey innerKey = GlobalKey(); final GlobalKey innerKey = GlobalKey();
final Map<String, Widget> routes = <String, Widget>{ final Map<String, Widget> routes = <String, Widget>{
...@@ -4136,6 +4139,7 @@ void main() { ...@@ -4136,6 +4139,7 @@ void main() {
late final NavigatorState navigator = late final NavigatorState navigator =
navigatorKey.currentState! as NavigatorState; navigatorKey.currentState! as NavigatorState;
final FocusScopeNode focusNode = FocusScopeNode(); final FocusScopeNode focusNode = FocusScopeNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(Column( await tester.pumpWidget(Column(
children: <Widget>[ children: <Widget>[
...@@ -4192,7 +4196,7 @@ void main() { ...@@ -4192,7 +4196,7 @@ void main() {
expect(focusNode.hasFocus, true); expect(focusNode.hasFocus, true);
}); });
testWidgets('class implementing NavigatorObserver can be used without problems', (WidgetTester tester) async { testWidgetsWithLeakTracking('class implementing NavigatorObserver can be used without problems', (WidgetTester tester) async {
final _MockNavigatorObserver observer = _MockNavigatorObserver(); final _MockNavigatorObserver observer = _MockNavigatorObserver();
Widget build([Key? key]) { Widget build([Key? key]) {
return TestDependencies( return TestDependencies(
...@@ -4224,7 +4228,7 @@ void main() { ...@@ -4224,7 +4228,7 @@ void main() {
observer._checkInvocations(<Symbol>[#navigator, #navigator]); observer._checkInvocations(<Symbol>[#navigator, #navigator]);
}); });
testWidgets("Navigator doesn't override FocusTraversalPolicy of ancestors", (WidgetTester tester) async { testWidgetsWithLeakTracking("Navigator doesn't override FocusTraversalPolicy of ancestors", (WidgetTester tester) async {
FocusTraversalPolicy? policy; FocusTraversalPolicy? policy;
await tester.pumpWidget( await tester.pumpWidget(
TestDependencies( TestDependencies(
...@@ -4247,7 +4251,7 @@ void main() { ...@@ -4247,7 +4251,7 @@ void main() {
expect(policy, isA<WidgetOrderTraversalPolicy>()); expect(policy, isA<WidgetOrderTraversalPolicy>());
}); });
testWidgets('Navigator inserts ReadingOrderTraversalPolicy if no ancestor has a policy', (WidgetTester tester) async { testWidgetsWithLeakTracking('Navigator inserts ReadingOrderTraversalPolicy if no ancestor has a policy', (WidgetTester tester) async {
FocusTraversalPolicy? policy; FocusTraversalPolicy? policy;
await tester.pumpWidget( await tester.pumpWidget(
TestDependencies( TestDependencies(
...@@ -4302,7 +4306,7 @@ void main() { ...@@ -4302,7 +4306,7 @@ void main() {
.setMockMethodCallHandler(SystemChannels.platform, null); .setMockMethodCallHandler(SystemChannels.platform, null);
}); });
testWidgets('a single route is already defaulted to false', (WidgetTester tester) async { testWidgetsWithLeakTracking('a single route is already defaulted to false', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -4317,7 +4321,7 @@ void main() { ...@@ -4317,7 +4321,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back. skip: isBrowser, // [intended] only non-web Android supports predictive back.
); );
testWidgets('navigating around a single Navigator with .pop', (WidgetTester tester) async { testWidgetsWithLeakTracking('navigating around a single Navigator with .pop', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
initialRoute: '/', initialRoute: '/',
...@@ -4394,7 +4398,7 @@ void main() { ...@@ -4394,7 +4398,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back. skip: isBrowser, // [intended] only non-web Android supports predictive back.
); );
testWidgets('navigating around a single Navigator with system back', (WidgetTester tester) async { testWidgetsWithLeakTracking('navigating around a single Navigator with system back', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
initialRoute: '/', initialRoute: '/',
...@@ -4471,7 +4475,7 @@ void main() { ...@@ -4471,7 +4475,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back. skip: isBrowser, // [intended] only non-web Android supports predictive back.
); );
testWidgets('a single Navigator with a PopScope that defaults to enabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('a single Navigator with a PopScope that defaults to enabled', (WidgetTester tester) async {
bool canPop = true; bool canPop = true;
late StateSetter setState; late StateSetter setState;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -4511,7 +4515,7 @@ void main() { ...@@ -4511,7 +4515,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back. skip: isBrowser, // [intended] only non-web Android supports predictive back.
); );
testWidgets('a single Navigator with a PopScope that defaults to disabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('a single Navigator with a PopScope that defaults to disabled', (WidgetTester tester) async {
bool canPop = false; bool canPop = false;
late StateSetter setState; late StateSetter setState;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -4553,7 +4557,7 @@ void main() { ...@@ -4553,7 +4557,7 @@ void main() {
// Test both system back gestures and Navigator.pop. // Test both system back gestures and Navigator.pop.
for (final _BackType backType in _BackType.values) { for (final _BackType backType in _BackType.values) {
testWidgets('navigating around nested Navigators', (WidgetTester tester) async { testWidgetsWithLeakTracking('navigating around nested Navigators', (WidgetTester tester) async {
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> nestedNav = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> nestedNav = GlobalKey<NavigatorState>();
Future<void> goBack() async { Future<void> goBack() async {
...@@ -4653,7 +4657,7 @@ void main() { ...@@ -4653,7 +4657,7 @@ void main() {
); );
} }
testWidgets('nested Navigators with a nested PopScope', (WidgetTester tester) async { testWidgetsWithLeakTracking('nested Navigators with a nested PopScope', (WidgetTester tester) async {
bool canPop = true; bool canPop = true;
late StateSetter setState; late StateSetter setState;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -4780,7 +4784,7 @@ void main() { ...@@ -4780,7 +4784,7 @@ void main() {
); );
group('Navigator page API', () { group('Navigator page API', () {
testWidgets('starting with one route as usual', (WidgetTester tester) async { testWidgetsWithLeakTracking('starting with one route as usual', (WidgetTester tester) async {
late StateSetter builderSetState; late StateSetter builderSetState;
final List<_Page> pages = <_Page>[_Page.home]; final List<_Page> pages = <_Page>[_Page.home];
bool canPop() => pages.length <= 1; bool canPop() => pages.length <= 1;
...@@ -4898,7 +4902,7 @@ void main() { ...@@ -4898,7 +4902,7 @@ void main() {
skip: isBrowser, // [intended] only non-web Android supports predictive back. skip: isBrowser, // [intended] only non-web Android supports predictive back.
); );
testWidgets('starting with existing route history', (WidgetTester tester) async { testWidgetsWithLeakTracking('starting with existing route history', (WidgetTester tester) async {
final List<_Page> pages = <_Page>[_Page.home, _Page.one]; final List<_Page> pages = <_Page>[_Page.home, _Page.one];
bool canPop() => pages.length <= 1; bool canPop() => pages.length <= 1;
......
...@@ -5,20 +5,26 @@ ...@@ -5,20 +5,26 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'semantics_tester.dart'; import 'semantics_tester.dart';
void main() { void main() {
testWidgets('OverflowEntries context contains Overlay', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverflowEntries context contains Overlay', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
bool didBuild = false; bool didBuild = false;
late final OverlayEntry overlayEntry1;
addTearDown(() => overlayEntry1..remove()..dispose());
late final OverlayEntry overlayEntry2;
addTearDown(() => overlayEntry2..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( overlayEntry1 = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
didBuild = true; didBuild = true;
final Overlay overlay = context.findAncestorWidgetOfExactType<Overlay>()!; final Overlay overlay = context.findAncestorWidgetOfExactType<Overlay>()!;
...@@ -26,7 +32,7 @@ void main() { ...@@ -26,7 +32,7 @@ void main() {
return Container(); return Container();
}, },
), ),
OverlayEntry( overlayEntry2 = OverlayEntry(
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
), ),
], ],
...@@ -80,25 +86,32 @@ void main() { ...@@ -80,25 +86,32 @@ void main() {
); );
}); });
testWidgets('Offstage overlay', (WidgetTester tester) async { testWidgetsWithLeakTracking('Offstage overlay', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
late final OverlayEntry overlayEntry1;
addTearDown(() => overlayEntry1..remove()..dispose());
late final OverlayEntry overlayEntry2;
addTearDown(() => overlayEntry2..remove()..dispose());
late final OverlayEntry overlayEntry3;
addTearDown(() => overlayEntry3..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( overlayEntry1 = OverlayEntry(
opaque: true, opaque: true,
maintainState: true, maintainState: true,
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
), ),
OverlayEntry( overlayEntry2 = OverlayEntry(
opaque: true, opaque: true,
maintainState: true, maintainState: true,
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
), ),
OverlayEntry( overlayEntry3 = OverlayEntry(
opaque: true, opaque: true,
maintainState: true, maintainState: true,
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
...@@ -163,16 +176,19 @@ void main() { ...@@ -163,16 +176,19 @@ void main() {
); );
}); });
testWidgets('insert top', (WidgetTester tester) async { testWidgetsWithLeakTracking('insert top', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
...@@ -187,8 +203,10 @@ void main() { ...@@ -187,8 +203,10 @@ void main() {
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert( overlay.insert(
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('New'); buildOrder.add('New');
return Container(); return Container();
...@@ -200,17 +218,19 @@ void main() { ...@@ -200,17 +218,19 @@ void main() {
expect(buildOrder, <String>['Base', 'New']); expect(buildOrder, <String>['Base', 'New']);
}); });
testWidgets('insert below', (WidgetTester tester) async { testWidgetsWithLeakTracking('insert below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
OverlayEntry base; late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
base = OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
...@@ -225,37 +245,43 @@ void main() { ...@@ -225,37 +245,43 @@ void main() {
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert( overlay.insert(
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('New'); buildOrder.add('New');
return Container(); return Container();
}, },
), ),
below: base, below: baseEntry,
); );
await tester.pump(); await tester.pump();
expect(buildOrder, <String>['New', 'Base']); expect(buildOrder, <String>['New', 'Base']);
}); });
testWidgets('insert above', (WidgetTester tester) async { testWidgetsWithLeakTracking('insert above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
OverlayEntry base; late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
base = OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
}, },
), ),
OverlayEntry( topEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Top'); buildOrder.add('Top');
return Container(); return Container();
...@@ -270,30 +296,35 @@ void main() { ...@@ -270,30 +296,35 @@ void main() {
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
overlay.insert( overlay.insert(
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('New'); buildOrder.add('New');
return Container(); return Container();
}, },
), ),
above: base, above: baseEntry,
); );
await tester.pump(); await tester.pump();
expect(buildOrder, <String>['Base', 'New', 'Top']); expect(buildOrder, <String>['Base', 'New', 'Top']);
}); });
testWidgets('insertAll top', (WidgetTester tester) async { testWidgetsWithLeakTracking('insertAll top', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
...@@ -320,6 +351,11 @@ void main() { ...@@ -320,6 +351,11 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
...@@ -329,17 +365,19 @@ void main() { ...@@ -329,17 +365,19 @@ void main() {
expect(buildOrder, <String>['Base', 'New1', 'New2']); expect(buildOrder, <String>['Base', 'New1', 'New2']);
}); });
testWidgets('insertAll below', (WidgetTester tester) async { testWidgetsWithLeakTracking('insertAll below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
OverlayEntry base; late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
base = OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
...@@ -366,32 +404,41 @@ void main() { ...@@ -366,32 +404,41 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
overlay.insertAll(entries, below: base); overlay.insertAll(entries, below: baseEntry);
await tester.pump(); await tester.pump();
expect(buildOrder, <String>['New1', 'New2','Base']); expect(buildOrder, <String>['New1', 'New2','Base']);
}); });
testWidgets('insertAll above', (WidgetTester tester) async { testWidgetsWithLeakTracking('insertAll above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<String> buildOrder = <String>[]; final List<String> buildOrder = <String>[];
OverlayEntry base; late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
base = OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Base'); buildOrder.add('Base');
return Container(); return Container();
}, },
), ),
OverlayEntry( topEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add('Top'); buildOrder.add('Top');
return Container(); return Container();
...@@ -418,16 +465,21 @@ void main() { ...@@ -418,16 +465,21 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in entries) {
entry..remove()..dispose();
}
});
buildOrder.clear(); buildOrder.clear();
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
overlay.insertAll(entries, above: base); overlay.insertAll(entries, above: baseEntry);
await tester.pump(); await tester.pump();
expect(buildOrder, <String>['Base', 'New1', 'New2', 'Top']); expect(buildOrder, <String>['Base', 'New1', 'New2', 'Top']);
}); });
testWidgets('rearrange', (WidgetTester tester) async { testWidgetsWithLeakTracking('rearrange', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[]; final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[ final List<OverlayEntry> initialEntries = <OverlayEntry>[
...@@ -456,6 +508,11 @@ void main() { ...@@ -456,6 +508,11 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -469,9 +526,11 @@ void main() { ...@@ -469,9 +526,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]); expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[ final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3], initialEntries[3],
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add(4); buildOrder.add(4);
return Container(); return Container();
...@@ -490,9 +549,10 @@ void main() { ...@@ -490,9 +549,10 @@ void main() {
expect(buildOrder, <int>[3, 4, 2, 0, 1]); expect(buildOrder, <int>[3, 4, 2, 0, 1]);
}); });
testWidgets('rearrange above', (WidgetTester tester) async { testWidgetsWithLeakTracking('rearrange above', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[]; final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[ final List<OverlayEntry> initialEntries = <OverlayEntry>[
OverlayEntry( OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
...@@ -519,6 +579,11 @@ void main() { ...@@ -519,6 +579,11 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -532,9 +597,11 @@ void main() { ...@@ -532,9 +597,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]); expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[ final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3], initialEntries[3],
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add(4); buildOrder.add(4);
return Container(); return Container();
...@@ -553,7 +620,7 @@ void main() { ...@@ -553,7 +620,7 @@ void main() {
expect(buildOrder, <int>[3, 4, 2, 1, 0]); expect(buildOrder, <int>[3, 4, 2, 1, 0]);
}); });
testWidgets('rearrange below', (WidgetTester tester) async { testWidgetsWithLeakTracking('rearrange below', (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
final List<int> buildOrder = <int>[]; final List<int> buildOrder = <int>[];
final List<OverlayEntry> initialEntries = <OverlayEntry>[ final List<OverlayEntry> initialEntries = <OverlayEntry>[
...@@ -582,6 +649,11 @@ void main() { ...@@ -582,6 +649,11 @@ void main() {
}, },
), ),
]; ];
addTearDown(() {
for (final OverlayEntry entry in initialEntries) {
entry..remove()..dispose();
}
});
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -595,9 +667,11 @@ void main() { ...@@ -595,9 +667,11 @@ void main() {
expect(buildOrder, <int>[0, 1, 2, 3]); expect(buildOrder, <int>[0, 1, 2, 3]);
late final OverlayEntry newEntry;
addTearDown(() => newEntry..remove()..dispose());
final List<OverlayEntry> rearranged = <OverlayEntry>[ final List<OverlayEntry> rearranged = <OverlayEntry>[
initialEntries[3], initialEntries[3],
OverlayEntry( newEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
buildOrder.add(4); buildOrder.add(4);
return Container(); return Container();
...@@ -694,7 +768,7 @@ void main() { ...@@ -694,7 +768,7 @@ void main() {
await tester.pump(); await tester.pump();
}); });
testWidgets('OverlayState.of() throws when called if an Overlay does not exist', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayState.of() throws when called if an Overlay does not exist', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -740,9 +814,11 @@ void main() { ...@@ -740,9 +814,11 @@ void main() {
); );
}); });
testWidgets("OverlayState.maybeOf() works when an Overlay does and doesn't exist", (WidgetTester tester) async { testWidgetsWithLeakTracking("OverlayState.maybeOf() works when an Overlay does and doesn't exist", (WidgetTester tester) async {
final GlobalKey overlayKey = GlobalKey(); final GlobalKey overlayKey = GlobalKey();
OverlayState? foundState; OverlayState? foundState;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -750,7 +826,7 @@ void main() { ...@@ -750,7 +826,7 @@ void main() {
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
foundState = Overlay.maybeOf(context); foundState = Overlay.maybeOf(context);
return Container(); return Container();
...@@ -781,7 +857,7 @@ void main() { ...@@ -781,7 +857,7 @@ void main() {
expect(foundState, isNull); expect(foundState, isNull);
}); });
testWidgets('OverlayEntry.opaque can be changed when OverlayEntry is not part of an Overlay (yet)', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntry.opaque can be changed when OverlayEntry is not part of an Overlay (yet)', (WidgetTester tester) async {
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>(); final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
final Key root = UniqueKey(); final Key root = UniqueKey();
final Key top = UniqueKey(); final Key top = UniqueKey();
...@@ -790,6 +866,7 @@ void main() { ...@@ -790,6 +866,7 @@ void main() {
return Container(key: root); return Container(key: root);
}, },
); );
addTearDown(() => rootEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -810,6 +887,7 @@ void main() { ...@@ -810,6 +887,7 @@ void main() {
return Container(key: top); return Container(key: top);
}, },
); );
addTearDown(() => newEntry..remove()..dispose());
expect(newEntry.opaque, isFalse); expect(newEntry.opaque, isFalse);
newEntry.opaque = true; // Does neither trigger an assert nor throw. newEntry.opaque = true; // Does neither trigger an assert nor throw.
expect(newEntry.opaque, isTrue); expect(newEntry.opaque, isTrue);
...@@ -822,7 +900,7 @@ void main() { ...@@ -822,7 +900,7 @@ void main() {
expect(find.byKey(top), findsOneWidget); expect(find.byKey(top), findsOneWidget);
}); });
testWidgets('OverlayEntries do not rebuild when opaqueness changes', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntries do not rebuild when opaqueness changes', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797. // Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>(); final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
...@@ -839,18 +917,21 @@ void main() { ...@@ -839,18 +917,21 @@ void main() {
return bottomWidget; return bottomWidget;
}, },
); );
addTearDown(() => bottomEntry..remove()..dispose());
final OverlayEntry middleEntry = OverlayEntry( final OverlayEntry middleEntry = OverlayEntry(
maintainState: true, maintainState: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return middleWidget; return middleWidget;
}, },
); );
addTearDown(() => middleEntry..remove()..dispose());
final OverlayEntry topEntry = OverlayEntry( final OverlayEntry topEntry = OverlayEntry(
maintainState: true, maintainState: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return topWidget; return topWidget;
}, },
); );
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -881,7 +962,7 @@ void main() { ...@@ -881,7 +962,7 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1); expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
}); });
testWidgets('OverlayEntries do not rebuild when opaque entry is added', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntries do not rebuild when opaque entry is added', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/45797. // Regression test for https://github.com/flutter/flutter/issues/45797.
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>(); final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
...@@ -898,6 +979,7 @@ void main() { ...@@ -898,6 +979,7 @@ void main() {
return bottomWidget; return bottomWidget;
}, },
); );
addTearDown(() => bottomEntry..remove()..dispose());
final OverlayEntry middleEntry = OverlayEntry( final OverlayEntry middleEntry = OverlayEntry(
opaque: true, opaque: true,
maintainState: true, maintainState: true,
...@@ -905,12 +987,14 @@ void main() { ...@@ -905,12 +987,14 @@ void main() {
return middleWidget; return middleWidget;
}, },
); );
addTearDown(() => middleEntry..remove()..dispose());
final OverlayEntry topEntry = OverlayEntry( final OverlayEntry topEntry = OverlayEntry(
maintainState: true, maintainState: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return topWidget; return topWidget;
}, },
); );
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -941,16 +1025,19 @@ void main() { ...@@ -941,16 +1025,19 @@ void main() {
expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1); expect(tester.state<StatefulTestState>(find.byKey(top)).rebuildCount, 1);
}); });
testWidgets('entries below opaque entries are ignored for hit testing', (WidgetTester tester) async { testWidgetsWithLeakTracking('entries below opaque entries are ignored for hit testing', (WidgetTester tester) async {
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>(); final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
int bottomTapCount = 0; int bottomTapCount = 0;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
maintainState: true, maintainState: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
...@@ -969,13 +1056,17 @@ void main() { ...@@ -969,13 +1056,17 @@ void main() {
await tester.tap(find.byKey(overlayKey), warnIfMissed: false); // gesture detector is translucent; no hit is registered between it and the render view await tester.tap(find.byKey(overlayKey), warnIfMissed: false); // gesture detector is translucent; no hit is registered between it and the render view
expect(bottomTapCount, 1); expect(bottomTapCount, 1);
overlayKey.currentState!.insert(OverlayEntry( late final OverlayEntry newEntry1;
addTearDown(() => newEntry1..remove()..dispose());
overlayKey.currentState!.insert(
newEntry1 = OverlayEntry(
maintainState: true, maintainState: true,
opaque: true, opaque: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return Container(); return Container();
}, },
)); ),
);
await tester.pump(); await tester.pump();
// Bottom is offstage and does not receive tap events. // Bottom is offstage and does not receive tap events.
...@@ -985,7 +1076,10 @@ void main() { ...@@ -985,7 +1076,10 @@ void main() {
expect(bottomTapCount, 1); expect(bottomTapCount, 1);
int topTapCount = 0; int topTapCount = 0;
overlayKey.currentState!.insert(OverlayEntry( late final OverlayEntry newEntry2;
addTearDown(() => newEntry2..remove()..dispose());
overlayKey.currentState!.insert(
newEntry2 = OverlayEntry(
maintainState: true, maintainState: true,
opaque: true, opaque: true,
builder: (BuildContext context) { builder: (BuildContext context) {
...@@ -995,7 +1089,8 @@ void main() { ...@@ -995,7 +1089,8 @@ void main() {
}, },
); );
}, },
)); ),
);
await tester.pump(); await tester.pump();
expect(topTapCount, 0); expect(topTapCount, 0);
...@@ -1004,22 +1099,27 @@ void main() { ...@@ -1004,22 +1099,27 @@ void main() {
expect(bottomTapCount, 1); expect(bottomTapCount, 1);
}); });
testWidgets('Semantics of entries below opaque entries are ignored', (WidgetTester tester) async { testWidgetsWithLeakTracking('Semantics of entries below opaque entries are ignored', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>(); final GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
late final OverlayEntry bottomEntry;
addTearDown(() => bottomEntry..remove()..dispose());
late final OverlayEntry topEntry;
addTearDown(() => topEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
key: overlayKey, key: overlayKey,
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( bottomEntry = OverlayEntry(
maintainState: true, maintainState: true,
builder: (BuildContext context) { builder: (BuildContext context) {
return const Text('bottom'); return const Text('bottom');
}, },
), ),
OverlayEntry( topEntry = OverlayEntry(
maintainState: true, maintainState: true,
opaque: true, opaque: true,
builder: (BuildContext context) { builder: (BuildContext context) {
...@@ -1039,13 +1139,16 @@ void main() { ...@@ -1039,13 +1139,16 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Can use Positioned within OverlayEntry', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can use Positioned within OverlayEntry', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return const Positioned( return const Positioned(
left: 145, left: 145,
...@@ -1117,13 +1220,16 @@ void main() { ...@@ -1117,13 +1220,16 @@ void main() {
} }
}); });
testWidgets('Overlay always applies clip', (WidgetTester tester) async { testWidgetsWithLeakTracking('Overlay always applies clip', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) => Positioned(left: 10, right: 10, child: Container()), builder: (BuildContext context) => Positioned(left: 10, right: 10, child: Container()),
), ),
], ],
...@@ -1139,7 +1245,7 @@ void main() { ...@@ -1139,7 +1245,7 @@ void main() {
); );
}); });
testWidgets('OverlayEntry throws if inserted to an invalid Overlay', (WidgetTester tester) async { testWidgetsWithLeakTracking('OverlayEntry throws if inserted to an invalid Overlay', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const Directionality( const Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -1148,6 +1254,7 @@ void main() { ...@@ -1148,6 +1254,7 @@ void main() {
); );
final OverlayState overlay = tester.state(find.byType(Overlay)); final OverlayState overlay = tester.state(find.byType(Overlay));
final OverlayEntry entry = OverlayEntry(builder: (BuildContext context) => const SizedBox()); final OverlayEntry entry = OverlayEntry(builder: (BuildContext context) => const SizedBox());
addTearDown(() => entry..remove()..dispose());
expect( expect(
() => overlay.insert(entry), () => overlay.insert(entry),
returnsNormally, returnsNormally,
...@@ -1212,13 +1319,14 @@ void main() { ...@@ -1212,13 +1319,14 @@ void main() {
child: Overlay(key: overlayKey), child: Overlay(key: overlayKey),
); );
testWidgets('mounted state can be listened', (WidgetTester tester) async { testWidgetsWithLeakTracking('mounted state can be listened', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay); await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
final List<bool> mountedLog = <bool>[]; final List<bool> mountedLog = <bool>[];
final OverlayEntry entry = OverlayEntry( final OverlayEntry entry = OverlayEntry(
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
); );
addTearDown(entry.dispose);
entry.addListener(() { entry.addListener(() {
mountedLog.add(entry.mounted); mountedLog.add(entry.mounted);
...@@ -1245,12 +1353,13 @@ void main() { ...@@ -1245,12 +1353,13 @@ void main() {
expect(mountedLog, <bool>[true, false, true, false]); expect(mountedLog, <bool>[true, false, true, false]);
}); });
testWidgets('throw if disposed before removal', (WidgetTester tester) async { testWidgetsWithLeakTracking('throw if disposed before removal', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay); await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
final OverlayEntry entry = OverlayEntry( final OverlayEntry entry = OverlayEntry(
builder: (BuildContext context) => Container(), builder: (BuildContext context) => Container(),
); );
addTearDown(() => entry..remove()..dispose());
overlay.insert(entry); overlay.insert(entry);
Object? error; Object? error;
...@@ -1279,7 +1388,7 @@ void main() { ...@@ -1279,7 +1388,7 @@ void main() {
expect(error, isAssertionError); expect(error, isAssertionError);
}); });
testWidgets('delayed dispose', (WidgetTester tester) async { testWidgetsWithLeakTracking('delayed dispose', (WidgetTester tester) async {
await tester.pumpWidget(emptyOverlay); await tester.pumpWidget(emptyOverlay);
final OverlayState overlay = overlayKey.currentState! as OverlayState; final OverlayState overlay = overlayKey.currentState! as OverlayState;
final List<bool> mountedLog = <bool>[]; final List<bool> mountedLog = <bool>[];
...@@ -1315,15 +1424,17 @@ void main() { ...@@ -1315,15 +1424,17 @@ void main() {
}); });
group('LookupBoundary', () { group('LookupBoundary', () {
testWidgets('hides Overlay from Overlay.maybeOf', (WidgetTester tester) async { testWidgetsWithLeakTracking('hides Overlay from Overlay.maybeOf', (WidgetTester tester) async {
OverlayState? overlay; OverlayState? overlay;
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return LookupBoundary( return LookupBoundary(
child: Builder( child: Builder(
...@@ -1343,13 +1454,16 @@ void main() { ...@@ -1343,13 +1454,16 @@ void main() {
expect(overlay, isNull); expect(overlay, isNull);
}); });
testWidgets('hides Overlay from Overlay.of', (WidgetTester tester) async { testWidgetsWithLeakTracking('hides Overlay from Overlay.of', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return LookupBoundary( return LookupBoundary(
child: Builder( child: Builder(
...@@ -1386,13 +1500,16 @@ void main() { ...@@ -1386,13 +1500,16 @@ void main() {
); );
}); });
testWidgets('hides Overlay from debugCheckHasOverlay', (WidgetTester tester) async { testWidgetsWithLeakTracking('hides Overlay from debugCheckHasOverlay', (WidgetTester tester) async {
late final OverlayEntry baseEntry;
addTearDown(() => baseEntry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( baseEntry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return LookupBoundary( return LookupBoundary(
child: Builder( child: Builder(
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() { void main() {
testWidgets('Router state restoration without RouteInformationProvider', (WidgetTester tester) async { testWidgetsWithLeakTracking('Router state restoration without RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey(); final UniqueKey router = UniqueKey();
_TestRouterDelegate delegate() => tester.widget<Router<Object?>>(find.byKey(router)).routerDelegate as _TestRouterDelegate; _TestRouterDelegate delegate() => tester.widget<Router<Object?>>(find.byKey(router)).routerDelegate as _TestRouterDelegate;
...@@ -39,7 +40,12 @@ void main() { ...@@ -39,7 +40,12 @@ void main() {
expect(find.text('Current config: /foo'), findsOneWidget); expect(find.text('Current config: /foo'), findsOneWidget);
expect(delegate().newRoutePaths, isEmpty); expect(delegate().newRoutePaths, isEmpty);
expect(delegate().restoredRoutePaths, <String>['/foo', '/foo']); expect(delegate().restoredRoutePaths, <String>['/foo', '/foo']);
}); },
leakTrackingTestConfig: const LeakTrackingTestConfig(
// TODO(ksokolovskyi): remove after fixing
// https://github.com/flutter/flutter/issues/134205
notDisposedAllowList: <String, int?> {'_RestorableRouteInformation': 2},
));
testWidgets('Router state restoration with RouteInformationProvider', (WidgetTester tester) async { testWidgets('Router state restoration with RouteInformationProvider', (WidgetTester tester) async {
final UniqueKey router = UniqueKey(); final UniqueKey router = UniqueKey();
...@@ -152,22 +158,37 @@ class _TestRouteInformationProvider extends RouteInformationProvider with Change ...@@ -152,22 +158,37 @@ class _TestRouteInformationProvider extends RouteInformationProvider with Change
} }
} }
class _TestWidget extends StatelessWidget { class _TestWidget extends StatefulWidget {
const _TestWidget({this.withInformationProvider = false, this.routerKey}); const _TestWidget({this.withInformationProvider = false, this.routerKey});
final bool withInformationProvider; final bool withInformationProvider;
final Key? routerKey; final Key? routerKey;
@override
State<_TestWidget> createState() => _TestWidgetState();
}
class _TestWidgetState extends State<_TestWidget> {
final _TestRouterDelegate _delegate = _TestRouterDelegate();
final _TestRouteInformationProvider _routeInformationProvider = _TestRouteInformationProvider();
@override
void dispose() {
_delegate.dispose();
_routeInformationProvider.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RootRestorationScope( return RootRestorationScope(
restorationId: 'root', restorationId: 'root',
child: Router<String>( child: Router<String>(
key: routerKey, key: widget.routerKey,
restorationScopeId: 'router', restorationScopeId: 'router',
routerDelegate: _TestRouterDelegate(), routerDelegate: _delegate,
routeInformationParser: _TestRouteInformationParser(), routeInformationParser: _TestRouteInformationParser(),
routeInformationProvider: withInformationProvider ? _TestRouteInformationProvider() : null, routeInformationProvider: widget.withInformationProvider ? _routeInformationProvider : null,
), ),
); );
} }
......
...@@ -861,12 +861,13 @@ void main() { ...@@ -861,12 +861,13 @@ void main() {
expect(targetMidLeftPage1, findsOneWidget); 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 TickerProvider vsync = TestTickerProvider();
final TabController controller = TabController( final TabController controller = TabController(
length: 3, length: 3,
vsync: vsync, vsync: vsync,
); );
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
......
...@@ -13,10 +13,11 @@ import 'dart:ui' as ui; ...@@ -13,10 +13,11 @@ import 'dart:ui' as ui;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
void main() { void main() {
testWidgets('Transform origin', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform origin', (WidgetTester tester) async {
bool didReceiveTap = false; bool didReceiveTap = false;
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -64,7 +65,7 @@ void main() { ...@@ -64,7 +65,7 @@ void main() {
expect(didReceiveTap, isTrue); expect(didReceiveTap, isTrue);
}); });
testWidgets('Transform alignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform alignment', (WidgetTester tester) async {
bool didReceiveTap = false; bool didReceiveTap = false;
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -112,7 +113,7 @@ void main() { ...@@ -112,7 +113,7 @@ void main() {
expect(didReceiveTap, isTrue); expect(didReceiveTap, isTrue);
}); });
testWidgets('Transform AlignmentDirectional alignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform AlignmentDirectional alignment', (WidgetTester tester) async {
bool didReceiveTap = false; bool didReceiveTap = false;
Widget buildFrame(TextDirection textDirection, AlignmentGeometry alignment) { Widget buildFrame(TextDirection textDirection, AlignmentGeometry alignment) {
...@@ -183,7 +184,7 @@ void main() { ...@@ -183,7 +184,7 @@ void main() {
expect(didReceiveTap, isTrue); expect(didReceiveTap, isTrue);
}); });
testWidgets('Transform offset + alignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform offset + alignment', (WidgetTester tester) async {
bool didReceiveTap = false; bool didReceiveTap = false;
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
...@@ -232,7 +233,7 @@ void main() { ...@@ -232,7 +233,7 @@ void main() {
expect(didReceiveTap, isTrue); expect(didReceiveTap, isTrue);
}); });
testWidgets('Composited transform offset', (WidgetTester tester) async { testWidgetsWithLeakTracking('Composited transform offset', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Center( Center(
child: SizedBox( child: SizedBox(
...@@ -261,7 +262,7 @@ void main() { ...@@ -261,7 +262,7 @@ void main() {
expect(transform.getTranslation(), equals(Vector3(100.0, 75.0, 0.0))); expect(transform.getTranslation(), equals(Vector3(100.0, 75.0, 0.0)));
}); });
testWidgets('Transform.rotate', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.rotate', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.rotate( Transform.rotate(
angle: math.pi / 2.0, angle: math.pi / 2.0,
...@@ -283,7 +284,7 @@ void main() { ...@@ -283,7 +284,7 @@ void main() {
]); ]);
}); });
testWidgets('applyPaintTransform of Transform in Padding', (WidgetTester tester) async { testWidgetsWithLeakTracking('applyPaintTransform of Transform in Padding', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
...@@ -301,7 +302,7 @@ void main() { ...@@ -301,7 +302,7 @@ void main() {
expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(30.0, 20.0)); expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(30.0, 20.0));
}); });
testWidgets('Transform.translate', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.translate', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.translate( Transform.translate(
offset: const Offset(100.0, 50.0), offset: const Offset(100.0, 50.0),
...@@ -316,7 +317,7 @@ void main() { ...@@ -316,7 +317,7 @@ void main() {
expect(tester.getTopLeft(find.byType(Container)), const Offset(100.0, 50.0)); expect(tester.getTopLeft(find.byType(Container)), const Offset(100.0, 50.0));
}); });
testWidgets('Transform.scale', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.scale', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.scale( Transform.scale(
scale: 2.0, scale: 2.0,
...@@ -339,7 +340,7 @@ void main() { ...@@ -339,7 +340,7 @@ void main() {
]); ]);
}); });
testWidgets('Transform with nan value short-circuits rendering', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform with nan value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform( Transform(
transform: Matrix4.identity() transform: Matrix4.identity()
...@@ -351,7 +352,7 @@ void main() { ...@@ -351,7 +352,7 @@ void main() {
expect(tester.layers, hasLength(1)); expect(tester.layers, hasLength(1));
}); });
testWidgets('Transform with inf value short-circuits rendering', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform with inf value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform( Transform(
transform: Matrix4.identity() transform: Matrix4.identity()
...@@ -363,7 +364,7 @@ void main() { ...@@ -363,7 +364,7 @@ void main() {
expect(tester.layers, hasLength(1)); expect(tester.layers, hasLength(1));
}); });
testWidgets('Transform with -inf value short-circuits rendering', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform with -inf value short-circuits rendering', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform( Transform(
transform: Matrix4.identity() transform: Matrix4.identity()
...@@ -375,7 +376,7 @@ void main() { ...@@ -375,7 +376,7 @@ void main() {
expect(tester.layers, hasLength(1)); expect(tester.layers, hasLength(1));
}); });
testWidgets('Transform.rotate does not remove layers due to singular short-circuit', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.rotate does not remove layers due to singular short-circuit', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.rotate( Transform.rotate(
angle: math.pi / 2, angle: math.pi / 2,
...@@ -386,7 +387,7 @@ void main() { ...@@ -386,7 +387,7 @@ void main() {
expect(tester.layers, hasLength(3)); expect(tester.layers, hasLength(3));
}); });
testWidgets('Transform.rotate creates nice rotation matrices for 0, 90, 180, 270 degrees', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.rotate creates nice rotation matrices for 0, 90, 180, 270 degrees', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.rotate( Transform.rotate(
angle: math.pi / 2, angle: math.pi / 2,
...@@ -447,7 +448,7 @@ void main() { ...@@ -447,7 +448,7 @@ void main() {
expect(tester.layers, hasLength(2)); expect(tester.layers, hasLength(2));
}); });
testWidgets('Transform.scale with 0.0 does not paint child layers', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.scale with 0.0 does not paint child layers', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.scale( Transform.scale(
scale: 0.0, scale: 0.0,
...@@ -486,7 +487,7 @@ void main() { ...@@ -486,7 +487,7 @@ void main() {
}); });
testWidgets('Translated child into translated box - hit test', (WidgetTester tester) async { testWidgetsWithLeakTracking('Translated child into translated box - hit test', (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey(); final GlobalKey key1 = GlobalKey();
bool pointerDown = false; bool pointerDown = false;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -524,7 +525,7 @@ void main() { ...@@ -524,7 +525,7 @@ void main() {
); );
} }
testWidgets( testWidgetsWithLeakTracking(
'3D transform renders the same with or without needsCompositing', '3D transform renders the same with or without needsCompositing',
(WidgetTester tester) async { (WidgetTester tester) async {
for (double angle = 0; angle <= math.pi/4; angle += 0.01) { for (double angle = 0; angle <= math.pi/4; angle += 0.01) {
...@@ -532,6 +533,7 @@ void main() { ...@@ -532,6 +533,7 @@ void main() {
final RenderBox renderBox = tester.binding.renderView.child!; final RenderBox renderBox = tester.binding.renderView.child!;
final OffsetLayer layer = renderBox.debugLayer! as OffsetLayer; final OffsetLayer layer = renderBox.debugLayer! as OffsetLayer;
final ui.Image imageWithCompositing = await layer.toImage(renderBox.paintBounds); final ui.Image imageWithCompositing = await layer.toImage(renderBox.paintBounds);
addTearDown(imageWithCompositing.dispose);
await tester.pumpWidget(RepaintBoundary(child: generateTransform(false, angle))); await tester.pumpWidget(RepaintBoundary(child: generateTransform(false, angle)));
await expectLater(find.byType(RepaintBoundary).first, matchesReferenceImage(imageWithCompositing)); await expectLater(find.byType(RepaintBoundary).first, matchesReferenceImage(imageWithCompositing));
...@@ -545,7 +547,7 @@ void main() { ...@@ -545,7 +547,7 @@ void main() {
return numbers.map<double>((String str) => double.parse(str.trim())).toList(); return numbers.map<double>((String str) => double.parse(str.trim())).toList();
} }
testWidgets('Transform.translate with FilterQuality produces filter layer', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.translate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.translate( Transform.translate(
offset: const Offset(25.0, 25.0), offset: const Offset(25.0, 25.0),
...@@ -563,7 +565,7 @@ void main() { ...@@ -563,7 +565,7 @@ void main() {
]); ]);
}); });
testWidgets('Transform.scale with FilterQuality produces filter layer', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.scale with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.scale( Transform.scale(
scale: 3.14159, scale: 3.14159,
...@@ -581,7 +583,7 @@ void main() { ...@@ -581,7 +583,7 @@ void main() {
]); ]);
}); });
testWidgets('Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.rotate( Transform.rotate(
angle: math.pi / 4, angle: math.pi / 4,
...@@ -599,7 +601,7 @@ void main() { ...@@ -599,7 +601,7 @@ void main() {
]); ]);
}); });
testWidgets('Offset Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async { testWidgetsWithLeakTracking('Offset Transform.rotate with FilterQuality produces filter layer', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
SizedBox(width: 400, height: 400, SizedBox(width: 400, height: 400,
child: Center( child: Center(
...@@ -621,7 +623,7 @@ void main() { ...@@ -621,7 +623,7 @@ void main() {
]); ]);
}); });
testWidgets('Transform layers update to match child and filterQuality', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform layers update to match child and filterQuality', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Transform.rotate( Transform.rotate(
angle: math.pi / 4, angle: math.pi / 4,
...@@ -657,7 +659,7 @@ void main() { ...@@ -657,7 +659,7 @@ void main() {
expect(tester.layers.whereType<ImageFilterLayer>(), hasLength(1)); expect(tester.layers.whereType<ImageFilterLayer>(), hasLength(1));
}); });
testWidgets('Transform layers with filterQuality golden', (WidgetTester tester) async { testWidgetsWithLeakTracking('Transform layers with filterQuality golden', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -701,7 +703,7 @@ void main() { ...@@ -701,7 +703,7 @@ void main() {
); );
}); });
testWidgets("Transform.scale() does not accept all three 'scale', 'scaleX' and 'scaleY' parameters to be non-null", (WidgetTester tester) async { testWidgetsWithLeakTracking("Transform.scale() does not accept all three 'scale', 'scaleX' and 'scaleY' parameters to be non-null", (WidgetTester tester) async {
await expectLater(() { await expectLater(() {
tester.pumpWidget(Directionality( tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -719,7 +721,7 @@ void main() { ...@@ -719,7 +721,7 @@ void main() {
}, throwsAssertionError); }, throwsAssertionError);
}); });
testWidgets("Transform.scale() needs at least one of 'scale', 'scaleX' and 'scaleY' to be non-null, otherwise throws AssertionError", (WidgetTester tester) async { testWidgetsWithLeakTracking("Transform.scale() needs at least one of 'scale', 'scaleX' and 'scaleY' to be non-null, otherwise throws AssertionError", (WidgetTester tester) async {
await expectLater(() { await expectLater(() {
tester.pumpWidget(Directionality( tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
...@@ -734,7 +736,7 @@ void main() { ...@@ -734,7 +736,7 @@ void main() {
}, throwsAssertionError); }, throwsAssertionError);
}); });
testWidgets("Transform.scale() scales widget uniformly with 'scale' parameter", (WidgetTester tester) async { testWidgetsWithLeakTracking("Transform.scale() scales widget uniformly with 'scale' parameter", (WidgetTester tester) async {
const double scale = 1.5; const double scale = 1.5;
const double height = 100; const double height = 100;
const double width = 150; const double width = 150;
...@@ -760,7 +762,7 @@ void main() { ...@@ -760,7 +762,7 @@ void main() {
expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container)))); expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container))));
}); });
testWidgets("Transform.scale() scales widget according to 'scaleX' and 'scaleY'", (WidgetTester tester) async { testWidgetsWithLeakTracking("Transform.scale() scales widget according to 'scaleX' and 'scaleY'", (WidgetTester tester) async {
const double scaleX = 1.5; const double scaleX = 1.5;
const double scaleY = 1.2; const double scaleY = 1.2;
const double height = 100; const double height = 100;
...@@ -788,7 +790,7 @@ void main() { ...@@ -788,7 +790,7 @@ void main() {
expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container)))); expect(tester.getBottomRight(find.byType(Container)), target.bottomRight(tester.getTopLeft(find.byType(Container))));
}); });
testWidgets( testWidgetsWithLeakTracking(
'Transform.flip does flip child correctly', 'Transform.flip does flip child correctly',
(WidgetTester tester) async { (WidgetTester tester) async {
const Offset topRight = Offset(60, 20); const Offset topRight = Offset(60, 20);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment