Unverified Commit 98ebab58 authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

Cover more test/widgets tests with leak tracking #8 (#135045)

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