Unverified Commit 5064f782 authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

Cover more test/material tests with leak tracking. (#136093)

parent 83134ac7
...@@ -876,7 +876,7 @@ void main() { ...@@ -876,7 +876,7 @@ void main() {
expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0))); expect(tester.getSize(find.byKey(keyA)), equals(const Size(20.0, 20.0)));
}); });
testWidgets('Chip padding - LTR', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip padding - LTR', (WidgetTester tester) async {
final GlobalKey keyA = GlobalKey(); final GlobalKey keyA = GlobalKey();
final GlobalKey keyB = GlobalKey(); final GlobalKey keyB = GlobalKey();
...@@ -915,7 +915,7 @@ void main() { ...@@ -915,7 +915,7 @@ void main() {
expect(tester.getBottomRight(find.byType(Icon)), const Offset(457.0, 309.0)); expect(tester.getBottomRight(find.byType(Icon)), const Offset(457.0, 309.0));
}); });
testWidgets('Chip padding - RTL', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip padding - RTL', (WidgetTester tester) async {
final GlobalKey keyA = GlobalKey(); final GlobalKey keyA = GlobalKey();
final GlobalKey keyB = GlobalKey(); final GlobalKey keyB = GlobalKey();
...@@ -1849,7 +1849,7 @@ void main() { ...@@ -1849,7 +1849,7 @@ void main() {
expect(tester.getSize(find.byKey(key2)), const Size(80.0, 32.0)); expect(tester.getSize(find.byKey(key2)), const Size(80.0, 32.0));
}); });
testWidgets('Chip uses the right theme colors for the right components', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses the right theme colors for the right components', (WidgetTester tester) async {
final ThemeData themeData = ThemeData( final ThemeData themeData = ThemeData(
platform: TargetPlatform.android, platform: TargetPlatform.android,
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
...@@ -2596,7 +2596,7 @@ void main() { ...@@ -2596,7 +2596,7 @@ void main() {
checkChipMaterialClipBehavior(tester, Clip.antiAlias); checkChipMaterialClipBehavior(tester, Clip.antiAlias);
}); });
testWidgets('selected chip and avatar draw darkened layer within avatar circle', (WidgetTester tester) async { testWidgetsWithLeakTracking('selected chip and avatar draw darkened layer within avatar circle', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
useMaterial3: false, useMaterial3: false,
...@@ -2639,8 +2639,9 @@ void main() { ...@@ -2639,8 +2639,9 @@ void main() {
expect(find.byType(InkWell), findsOneWidget); expect(find.byType(InkWell), findsOneWidget);
}); });
testWidgets('Chip uses stateful color for text color in different states', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses stateful color for text color in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001); const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002); const Color hoverColor = Color(0x00000002);
...@@ -2727,8 +2728,9 @@ void main() { ...@@ -2727,8 +2728,9 @@ void main() {
expect(textColor(), disabledColor); expect(textColor(), disabledColor);
}); });
testWidgets('Chip uses stateful border side color in different states', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses stateful border side color in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001); const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002); const Color hoverColor = Color(0x00000002);
...@@ -2807,8 +2809,9 @@ void main() { ...@@ -2807,8 +2809,9 @@ void main() {
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor)); expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
}); });
testWidgets('Chip uses stateful border side color from resolveWith', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses stateful border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001); const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002); const Color hoverColor = Color(0x00000002);
...@@ -2888,8 +2891,9 @@ void main() { ...@@ -2888,8 +2891,9 @@ void main() {
}); });
testWidgets('Chip uses stateful nullable border side color from resolveWith', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses stateful nullable border side color from resolveWith', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
const Color pressedColor = Color(0x00000001); const Color pressedColor = Color(0x00000001);
const Color hoverColor = Color(0x00000002); const Color hoverColor = Color(0x00000002);
...@@ -2977,8 +2981,9 @@ void main() { ...@@ -2977,8 +2981,9 @@ void main() {
expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor)); expect(find.byType(RawChip), paints..rrect()..rrect(color: disabledColor));
}); });
testWidgets('Chip uses stateful shape in different states', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses stateful shape in different states', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
OutlinedBorder? getShape(Set<MaterialState> states) { OutlinedBorder? getShape(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) { if (states.contains(MaterialState.disabled)) {
...@@ -3320,8 +3325,9 @@ void main() { ...@@ -3320,8 +3325,9 @@ void main() {
expect(decoration.shape, shape); expect(decoration.shape, shape);
}); });
testWidgets('Chip highlight color is drawn on top of the backgroundColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip highlight color is drawn on top of the backgroundColor', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'RawChip'); final FocusNode focusNode = FocusNode(debugLabel: 'RawChip');
addTearDown(focusNode.dispose);
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color backgroundColor = Color(0xff00ff00); const Color backgroundColor = Color(0xff00ff00);
...@@ -3443,7 +3449,7 @@ void main() { ...@@ -3443,7 +3449,7 @@ void main() {
expect(getMaterialBox(tester), paints..rrect(color: selectedColor)); expect(getMaterialBox(tester), paints..rrect(color: selectedColor));
}); });
testWidgets('Delete button tap target area does not include label', (WidgetTester tester) async { testWidgetsWithLeakTracking('Delete button tap target area does not include label', (WidgetTester tester) async {
bool calledDelete = false; bool calledDelete = false;
await tester.pumpWidget( await tester.pumpWidget(
wrapForChip( wrapForChip(
...@@ -3486,7 +3492,7 @@ void main() { ...@@ -3486,7 +3492,7 @@ void main() {
}); });
// This is a regression test for https://github.com/flutter/flutter/pull/133615. // This is a regression test for https://github.com/flutter/flutter/pull/133615.
testWidgets('Material3 - Custom shape without provided side uses default side', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - Custom shape without provided side uses default side', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true); final ThemeData theme = ThemeData(useMaterial3: true);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -3510,7 +3516,7 @@ void main() { ...@@ -3510,7 +3516,7 @@ void main() {
); );
}); });
testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async { testWidgetsWithLeakTracking("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async {
Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) { Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) {
return MaterialApp( return MaterialApp(
theme: ThemeData(useMaterial3: true), theme: ThemeData(useMaterial3: true),
...@@ -3569,7 +3575,7 @@ void main() { ...@@ -3569,7 +3575,7 @@ void main() {
// support is deprecated and the APIs are removed, these tests // support is deprecated and the APIs are removed, these tests
// can be deleted. // can be deleted.
testWidgets('M2 Chip defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('M2 Chip defaults', (WidgetTester tester) async {
late TextTheme textTheme; late TextTheme textTheme;
Widget buildFrame(Brightness brightness) { Widget buildFrame(Brightness brightness) {
...@@ -3646,7 +3652,7 @@ void main() { ...@@ -3646,7 +3652,7 @@ void main() {
expect(labelStyle.wordSpacing, textTheme.bodyLarge?.wordSpacing); expect(labelStyle.wordSpacing, textTheme.bodyLarge?.wordSpacing);
}); });
testWidgets('Chip uses the right theme colors for the right components', (WidgetTester tester) async { testWidgetsWithLeakTracking('Chip uses the right theme colors for the right components', (WidgetTester tester) async {
final ThemeData themeData = ThemeData( final ThemeData themeData = ThemeData(
platform: TargetPlatform.android, platform: TargetPlatform.android,
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
......
...@@ -929,7 +929,7 @@ void main() { ...@@ -929,7 +929,7 @@ void main() {
); );
}); });
testWidgets("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async { testWidgetsWithLeakTracking("Material3 - RawChip.shape's side is used when provided", (WidgetTester tester) async {
Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) { Widget buildChip({ OutlinedBorder? shape, BorderSide? side }) {
return MaterialApp( return MaterialApp(
theme: ThemeData( theme: ThemeData(
......
...@@ -698,7 +698,7 @@ void main() { ...@@ -698,7 +698,7 @@ void main() {
expect(find.text('disabled'), findsOneWidget); expect(find.text('disabled'), findsOneWidget);
}); });
testWidgets( testWidgetsWithLeakTracking(
'DropdownButtonFormField - hint displays when the items list is ' 'DropdownButtonFormField - hint displays when the items list is '
'empty, items is null, and disabledHint is null', 'empty, items is null, and disabledHint is null',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1232,7 +1232,7 @@ void main() { ...@@ -1232,7 +1232,7 @@ void main() {
expect(inkWell.borderRadius, errorBorderRadius); expect(inkWell.borderRadius, errorBorderRadius);
}); });
testWidgets('DropdownButtonFormField onChanged is called when the form is reset', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButtonFormField onChanged is called when the form is reset', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/123009. // Regression test for https://github.com/flutter/flutter/issues/123009.
final GlobalKey<FormFieldState<String>> stateKey = GlobalKey<FormFieldState<String>>(); final GlobalKey<FormFieldState<String>> stateKey = GlobalKey<FormFieldState<String>>();
final GlobalKey<FormState> formKey = GlobalKey<FormState>(); final GlobalKey<FormState> formKey = GlobalKey<FormState>();
......
...@@ -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';
void main() { void main() {
const String longText = 'one two three four five six seven eight nine ten eleven twelve'; const String longText = 'one two three four five six seven eight nine ten eleven twelve';
...@@ -34,7 +35,7 @@ void main() { ...@@ -34,7 +35,7 @@ void main() {
); );
} }
testWidgets('DropdownMenu defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu defaults', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(buildTest(themeData, menuChildren)); await tester.pumpWidget(buildTest(themeData, menuChildren));
...@@ -82,7 +83,7 @@ void main() { ...@@ -82,7 +83,7 @@ void main() {
expect(material.textStyle?.height, 1.43); expect(material.textStyle?.height, 1.43);
}); });
testWidgets('DropdownMenu can be disabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu can be disabled', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -115,7 +116,7 @@ void main() { ...@@ -115,7 +116,7 @@ void main() {
expect(updatedMenuMaterial, findsNothing); expect(updatedMenuMaterial, findsNothing);
}); });
testWidgets('Material2 - The width of the text field should always be the same as the menu view', testWidgetsWithLeakTracking('Material2 - The width of the text field should always be the same as the menu view',
(WidgetTester tester) async { (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: false); final ThemeData themeData = ThemeData(useMaterial3: false);
...@@ -165,7 +166,7 @@ void main() { ...@@ -165,7 +166,7 @@ void main() {
expect(updatedMenuWidth, 200.0); expect(updatedMenuWidth, 200.0);
}); });
testWidgets('Material3 - The width of the text field should always be the same as the menu view', testWidgetsWithLeakTracking('Material3 - The width of the text field should always be the same as the menu view',
(WidgetTester tester) async { (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true); final ThemeData themeData = ThemeData(useMaterial3: true);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -214,7 +215,7 @@ void main() { ...@@ -214,7 +215,7 @@ void main() {
expect(updatedMenuWidth, 200.0); expect(updatedMenuWidth, 200.0);
}); });
testWidgets('The width property can customize the width of the dropdown menu', (WidgetTester tester) async { testWidgetsWithLeakTracking('The width property can customize the width of the dropdown menu', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[]; final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[];
...@@ -248,7 +249,7 @@ void main() { ...@@ -248,7 +249,7 @@ void main() {
expect(buttonSize.width, customSmallWidth); expect(buttonSize.width, customSmallWidth);
}); });
testWidgets('The width property update test', (WidgetTester tester) async { testWidgetsWithLeakTracking('The width property update test', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/120567 // Regression test for https://github.com/flutter/flutter/issues/120567
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[]; final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[];
...@@ -270,7 +271,7 @@ void main() { ...@@ -270,7 +271,7 @@ void main() {
expect(box.size.width, customWidth); expect(box.size.width, customWidth);
}); });
testWidgets('The width of MenuAnchor respects MenuAnchor.expandedInsets', (WidgetTester tester) async { testWidgetsWithLeakTracking('The width of MenuAnchor respects MenuAnchor.expandedInsets', (WidgetTester tester) async {
const double parentWidth = 500.0; const double parentWidth = 500.0;
final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[]; final List<DropdownMenuEntry<ShortMenu>> shortMenuItems = <DropdownMenuEntry<ShortMenu>>[];
for (final ShortMenu value in ShortMenu.values) { for (final ShortMenu value in ShortMenu.values) {
...@@ -334,7 +335,7 @@ void main() { ...@@ -334,7 +335,7 @@ void main() {
expect(buttonSize.width, parentWidth - 35.0 - 20.0); expect(buttonSize.width, parentWidth - 35.0 - 20.0);
}); });
testWidgets('Material2 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', testWidgetsWithLeakTracking('Material2 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list',
(WidgetTester tester) async { (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: false); final ThemeData themeData = ThemeData(useMaterial3: false);
await tester.pumpWidget(buildTest(themeData, menuChildren)); await tester.pumpWidget(buildTest(themeData, menuChildren));
...@@ -375,7 +376,7 @@ void main() { ...@@ -375,7 +376,7 @@ void main() {
expect(updatedMenuSize, const Size(180.0, 100.0)); expect(updatedMenuSize, const Size(180.0, 100.0));
}); });
testWidgets('Material3 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list', testWidgetsWithLeakTracking('Material3 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list',
(WidgetTester tester) async { (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true); final ThemeData themeData = ThemeData(useMaterial3: true);
await tester.pumpWidget(buildTest(themeData, menuChildren)); await tester.pumpWidget(buildTest(themeData, menuChildren));
...@@ -418,7 +419,7 @@ void main() { ...@@ -418,7 +419,7 @@ void main() {
expect(updatedMenuSize.height, equals(100.0)); expect(updatedMenuSize.height, equals(100.0));
}); });
testWidgets('The text in the menu button should be aligned with the text of ' testWidgetsWithLeakTracking('The text in the menu button should be aligned with the text of '
'the text field - LTR', (WidgetTester tester) async { 'the text field - LTR', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
// Default text field (without leading icon). // Default text field (without leading icon).
...@@ -479,7 +480,7 @@ void main() { ...@@ -479,7 +480,7 @@ void main() {
expect(updatedLabelTopLeft1.dx, equals(largeIconWidth)); expect(updatedLabelTopLeft1.dx, equals(largeIconWidth));
}); });
testWidgets('The text in the menu button should be aligned with the text of ' testWidgetsWithLeakTracking('The text in the menu button should be aligned with the text of '
'the text field - RTL', (WidgetTester tester) async { 'the text field - RTL', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
// Default text field (without leading icon). // Default text field (without leading icon).
...@@ -571,7 +572,7 @@ void main() { ...@@ -571,7 +572,7 @@ void main() {
expect(updatedLabelTopRight1.dx, equals(updatedDropdownMenuTopRight.dx - largeIconWidth)); expect(updatedLabelTopRight1.dx, equals(updatedDropdownMenuTopRight.dx - largeIconWidth));
}); });
testWidgets('DropdownMenu has default trailing icon button', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu has default trailing icon button', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(buildTest(themeData, menuChildren)); await tester.pumpWidget(buildTest(themeData, menuChildren));
await tester.pump(); await tester.pump();
...@@ -589,7 +590,7 @@ void main() { ...@@ -589,7 +590,7 @@ void main() {
expect(menuMaterial, findsOneWidget); expect(menuMaterial, findsOneWidget);
}); });
testWidgets('Leading IconButton status test', (WidgetTester tester) async { testWidgetsWithLeakTracking('Leading IconButton status test', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true); final ThemeData themeData = ThemeData(useMaterial3: true);
await tester.pumpWidget(buildTest(themeData, menuChildren, width: 100.0, menuHeight: 100.0)); await tester.pumpWidget(buildTest(themeData, menuChildren, width: 100.0, menuHeight: 100.0));
await tester.pump(); await tester.pump();
...@@ -617,7 +618,7 @@ void main() { ...@@ -617,7 +618,7 @@ void main() {
expect(iconButton, findsOneWidget); expect(iconButton, findsOneWidget);
}); });
testWidgets('Do not crash when resize window during menu opening', (WidgetTester tester) async { testWidgetsWithLeakTracking('Do not crash when resize window during menu opening', (WidgetTester tester) async {
addTearDown(tester.view.reset); addTearDown(tester.view.reset);
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -653,7 +654,7 @@ void main() { ...@@ -653,7 +654,7 @@ void main() {
// Go without throw. // Go without throw.
}); });
testWidgets('DropdownMenu can customize trailing icon button', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu can customize trailing icon button', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -679,7 +680,7 @@ void main() { ...@@ -679,7 +680,7 @@ void main() {
expect(menuMaterial, findsOneWidget); expect(menuMaterial, findsOneWidget);
}); });
testWidgets('Down key can highlight the menu item on desktop platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('Down key can highlight the menu item on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -721,7 +722,7 @@ void main() { ...@@ -721,7 +722,7 @@ void main() {
expect(item0material.color, Colors.transparent); // the previous item should not be highlighted. expect(item0material.color, Colors.transparent); // the previous item should not be highlighted.
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('Up key can highlight the menu item on desktop platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('Up key can highlight the menu item on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -763,7 +764,7 @@ void main() { ...@@ -763,7 +764,7 @@ void main() {
expect(item5material.color, Colors.transparent); // the previous item should not be highlighted. expect(item5material.color, Colors.transparent); // the previous item should not be highlighted.
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('The text input should match the label of the menu item ' testWidgetsWithLeakTracking('The text input should match the label of the menu item '
'while pressing down key on desktop platforms', (WidgetTester tester) async { 'while pressing down key on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -794,7 +795,7 @@ void main() { ...@@ -794,7 +795,7 @@ void main() {
expect(find.widgetWithText(TextField, 'Item 2'), findsOneWidget); expect(find.widgetWithText(TextField, 'Item 2'), findsOneWidget);
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('The text input should match the label of the menu item ' testWidgetsWithLeakTracking('The text input should match the label of the menu item '
'while pressing up key on desktop platforms', (WidgetTester tester) async { 'while pressing up key on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -825,7 +826,7 @@ void main() { ...@@ -825,7 +826,7 @@ void main() {
expect(find.widgetWithText(TextField, 'Item 3'), findsOneWidget); expect(find.widgetWithText(TextField, 'Item 3'), findsOneWidget);
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('Disabled button will be skipped while pressing up/down key on desktop platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('Disabled button will be skipped while pressing up/down key on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final List<DropdownMenuEntry<TestMenu>> menuWithDisabledItems = <DropdownMenuEntry<TestMenu>>[ final List<DropdownMenuEntry<TestMenu>> menuWithDisabledItems = <DropdownMenuEntry<TestMenu>>[
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 0'), const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 0'),
...@@ -869,7 +870,7 @@ void main() { ...@@ -869,7 +870,7 @@ void main() {
expect(item3Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); expect(item3Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('Searching is enabled by default on mobile platforms if initialSelection is non null', (WidgetTester tester) async { testWidgetsWithLeakTracking('Searching is enabled by default on mobile platforms if initialSelection is non null', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -892,7 +893,7 @@ void main() { ...@@ -892,7 +893,7 @@ void main() {
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Menu 1 button is highlighted. expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Menu 1 button is highlighted.
}, variant: TargetPlatformVariant.mobile()); }, variant: TargetPlatformVariant.mobile());
testWidgets('Searching is enabled by default on desktop platform', (WidgetTester tester) async { testWidgetsWithLeakTracking('Searching is enabled by default on desktop platform', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -916,7 +917,7 @@ void main() { ...@@ -916,7 +917,7 @@ void main() {
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Menu 1 button is highlighted. expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); // Menu 1 button is highlighted.
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('Highlight can move up/down starting from the searching result on desktop platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('Highlight can move up/down starting from the searching result on desktop platforms', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -962,7 +963,7 @@ void main() { ...@@ -962,7 +963,7 @@ void main() {
expect(item5Material.color, themeData.colorScheme.onSurface.withOpacity(0.12)); expect(item5Material.color, themeData.colorScheme.onSurface.withOpacity(0.12));
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('Filtering is disabled by default', (WidgetTester tester) async { testWidgetsWithLeakTracking('Filtering is disabled by default', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -986,7 +987,7 @@ void main() { ...@@ -986,7 +987,7 @@ void main() {
} }
}); });
testWidgets('Enable filtering', (WidgetTester tester) async { testWidgetsWithLeakTracking('Enable filtering', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
...@@ -1017,9 +1018,11 @@ void main() { ...@@ -1017,9 +1018,11 @@ void main() {
} }
}); });
testWidgets('The controller can access the value in the input field', (WidgetTester tester) async { testWidgetsWithLeakTracking('The controller can access the value in the input field', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
home: StatefulBuilder( home: StatefulBuilder(
...@@ -1049,9 +1052,11 @@ void main() { ...@@ -1049,9 +1052,11 @@ void main() {
expect(controller.text, 'New Item'); expect(controller.text, 'New Item');
}); });
testWidgets('The menu should be closed after text editing is complete', (WidgetTester tester) async { testWidgetsWithLeakTracking('The menu should be closed after text editing is complete', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
home: Scaffold( home: Scaffold(
...@@ -1077,7 +1082,7 @@ void main() { ...@@ -1077,7 +1082,7 @@ void main() {
expect(menuAnchor.controller!.isOpen, false); expect(menuAnchor.controller!.isOpen, false);
}); });
testWidgets('The onSelected gets called only when a selection is made', (WidgetTester tester) async { testWidgetsWithLeakTracking('The onSelected gets called only when a selection is made', (WidgetTester tester) async {
int selectionCount = 0; int selectionCount = 0;
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
...@@ -1088,6 +1093,8 @@ void main() { ...@@ -1088,6 +1093,8 @@ void main() {
const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 3'), const DropdownMenuEntry<TestMenu>(value: TestMenu.mainMenu0, label: 'Item 3'),
]; ];
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
home: StatefulBuilder( home: StatefulBuilder(
...@@ -1174,9 +1181,11 @@ void main() { ...@@ -1174,9 +1181,11 @@ void main() {
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('The selectedValue gives an initial text and highlights the according item', (WidgetTester tester) async { testWidgetsWithLeakTracking('The selectedValue gives an initial text and highlights the according item', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: themeData, theme: themeData,
home: StatefulBuilder( home: StatefulBuilder(
...@@ -1208,7 +1217,7 @@ void main() { ...@@ -1208,7 +1217,7 @@ void main() {
expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12)); expect(itemMaterial.color, themeData.colorScheme.onSurface.withOpacity(0.12));
}); });
testWidgets('The default text input field should not be focused on mobile platforms ' testWidgetsWithLeakTracking('The default text input field should not be focused on mobile platforms '
'when it is tapped', (WidgetTester tester) async { 'when it is tapped', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
...@@ -1234,7 +1243,7 @@ void main() { ...@@ -1234,7 +1243,7 @@ void main() {
expect(result.canRequestFocus, false); expect(result.canRequestFocus, false);
}, variant: TargetPlatformVariant.mobile()); }, variant: TargetPlatformVariant.mobile());
testWidgets('The text input field should be focused on desktop platforms ' testWidgetsWithLeakTracking('The text input field should be focused on desktop platforms '
'when it is tapped', (WidgetTester tester) async { 'when it is tapped', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
...@@ -1259,7 +1268,7 @@ void main() { ...@@ -1259,7 +1268,7 @@ void main() {
expect(result.canRequestFocus, true); expect(result.canRequestFocus, true);
}, variant: TargetPlatformVariant.desktop()); }, variant: TargetPlatformVariant.desktop());
testWidgets('If requestFocusOnTap is true, the text input field can request focus, ' testWidgetsWithLeakTracking('If requestFocusOnTap is true, the text input field can request focus, '
'otherwise it cannot request focus', (WidgetTester tester) async { 'otherwise it cannot request focus', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
...@@ -1309,7 +1318,7 @@ void main() { ...@@ -1309,7 +1318,7 @@ void main() {
expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget); expect(find.widgetWithText(TextField, 'Item 0'), findsOneWidget);
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('If requestFocusOnTap is false, the mouse cursor should be clickable when hovered', (WidgetTester tester) async { testWidgetsWithLeakTracking('If requestFocusOnTap is false, the mouse cursor should be clickable when hovered', (WidgetTester tester) async {
Widget buildDropdownMenu() => MaterialApp( Widget buildDropdownMenu() => MaterialApp(
home: Scaffold( home: Scaffold(
body: Column( body: Column(
...@@ -1335,7 +1344,7 @@ void main() { ...@@ -1335,7 +1344,7 @@ void main() {
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click); expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
}); });
testWidgets('The menu has the same width as the input field in ListView', (WidgetTester tester) async { testWidgetsWithLeakTracking('The menu has the same width as the input field in ListView', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/123631 // Regression test for https://github.com/flutter/flutter/issues/123631
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -1387,11 +1396,12 @@ void main() { ...@@ -1387,11 +1396,12 @@ void main() {
expect(menu1.width, 200); expect(menu1.width, 200);
}); });
testWidgets('Semantics does not include hint when input is not empty', (WidgetTester tester) async { testWidgetsWithLeakTracking('Semantics does not include hint when input is not empty', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
const String hintText = 'I am hintText'; const String hintText = 'I am hintText';
TestMenu? selectedValue; TestMenu? selectedValue;
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
StatefulBuilder( StatefulBuilder(
...@@ -1430,7 +1440,7 @@ void main() { ...@@ -1430,7 +1440,7 @@ void main() {
expect(node.value, 'Item 3'); expect(node.value, 'Item 3');
}); });
testWidgets('helperText is not visible when errorText is not null', (WidgetTester tester) async { testWidgetsWithLeakTracking('helperText is not visible when errorText is not null', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
const String helperText = 'I am helperText'; const String helperText = 'I am helperText';
const String errorText = 'I am errorText'; const String errorText = 'I am errorText';
...@@ -1460,7 +1470,7 @@ void main() { ...@@ -1460,7 +1470,7 @@ void main() {
expect(find.text(errorText), findsOneWidget); expect(find.text(errorText), findsOneWidget);
}); });
testWidgets('DropdownMenu can respect helperText when helperText is not null', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu can respect helperText when helperText is not null', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
const String helperText = 'I am helperText'; const String helperText = 'I am helperText';
...@@ -1482,7 +1492,7 @@ void main() { ...@@ -1482,7 +1492,7 @@ void main() {
expect(find.text(helperText), findsOneWidget); expect(find.text(helperText), findsOneWidget);
}); });
testWidgets('DropdownMenu can respect errorText when errorText is not null', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenu can respect errorText when errorText is not null', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(); final ThemeData themeData = ThemeData();
const String errorText = 'I am errorText'; const String errorText = 'I am errorText';
...@@ -1504,7 +1514,7 @@ void main() { ...@@ -1504,7 +1514,7 @@ void main() {
expect(find.text(errorText), findsOneWidget); expect(find.text(errorText), findsOneWidget);
}); });
testWidgets('Can scroll to the highlighted item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can scroll to the highlighted item', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
body: DropdownMenu<TestMenu>( body: DropdownMenu<TestMenu>(
...@@ -1527,7 +1537,7 @@ void main() { ...@@ -1527,7 +1537,7 @@ void main() {
}); });
// This is a regression test for https://github.com/flutter/flutter/issues/131676. // This is a regression test for https://github.com/flutter/flutter/issues/131676.
testWidgets('Material3 - DropdownMenu uses correct text styles', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - DropdownMenu uses correct text styles', (WidgetTester tester) async {
const TextStyle inputTextThemeStyle = TextStyle( const TextStyle inputTextThemeStyle = TextStyle(
fontSize: 18.5, fontSize: 18.5,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
...@@ -1573,9 +1583,10 @@ void main() { ...@@ -1573,9 +1583,10 @@ void main() {
expect(material.textStyle?.decoration, menuItemTextThemeStyle.decoration); expect(material.textStyle?.decoration, menuItemTextThemeStyle.decoration);
}); });
testWidgets('DropdownMenuEntries do not overflow when width is specified', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenuEntries do not overflow when width is specified', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126882 // Regression test for https://github.com/flutter/flutter/issues/126882
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1619,9 +1630,10 @@ void main() { ...@@ -1619,9 +1630,10 @@ void main() {
expect(controller.text, 'Item 0 $longText'); expect(controller.text, 'Item 0 $longText');
}); });
testWidgets('DropdownMenuEntry.labelWidget is Text that specifies maxLines 1 or 2', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenuEntry.labelWidget is Text that specifies maxLines 1 or 2', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126882 // Regression test for https://github.com/flutter/flutter/issues/126882
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
Widget buildFrame({ required int maxLines }) { Widget buildFrame({ required int maxLines }) {
return MaterialApp( return MaterialApp(
...@@ -1682,7 +1694,7 @@ void main() { ...@@ -1682,7 +1694,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/131350. // Regression test for https://github.com/flutter/flutter/issues/131350.
testWidgets('DropdownMenuEntry.leadingIcon default layout', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenuEntry.leadingIcon default layout', (WidgetTester tester) async {
// The DropdownMenu should not get extra padding in DropdownMenuEntry items // The DropdownMenu should not get extra padding in DropdownMenuEntry items
// when both text field and DropdownMenuEntry have leading icons. // when both text field and DropdownMenuEntry have leading icons.
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
......
...@@ -22,6 +22,7 @@ import 'package:flutter/material.dart'; ...@@ -22,6 +22,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 '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart'; import 'feedback_tester.dart';
...@@ -339,7 +340,7 @@ Future<void> checkDropdownColor(WidgetTester tester, {Color? color, bool isFormF ...@@ -339,7 +340,7 @@ Future<void> checkDropdownColor(WidgetTester tester, {Color? color, bool isFormF
} }
void main() { void main() {
testWidgets('Default dropdown golden', (WidgetTester tester) async { testWidgetsWithLeakTracking('Default dropdown golden', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build() => buildFrame(buttonKey: buttonKey, onChanged: onChanged, useMaterial3: false); Widget build() => buildFrame(buttonKey: buttonKey, onChanged: onChanged, useMaterial3: false);
await tester.pumpWidget(build()); await tester.pumpWidget(build());
...@@ -351,7 +352,7 @@ void main() { ...@@ -351,7 +352,7 @@ void main() {
); );
}); });
testWidgets('Expanded dropdown golden', (WidgetTester tester) async { testWidgetsWithLeakTracking('Expanded dropdown golden', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build() => buildFrame(buttonKey: buttonKey, isExpanded: true, onChanged: onChanged, useMaterial3: false); Widget build() => buildFrame(buttonKey: buttonKey, isExpanded: true, onChanged: onChanged, useMaterial3: false);
await tester.pumpWidget(build()); await tester.pumpWidget(build());
...@@ -363,7 +364,7 @@ void main() { ...@@ -363,7 +364,7 @@ void main() {
); );
}); });
testWidgets('Dropdown button control test', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button control test', (WidgetTester tester) async {
String? value = 'one'; String? value = 'one';
void didChangeValue(String? newValue) { void didChangeValue(String? newValue) {
value = newValue; value = newValue;
...@@ -402,7 +403,7 @@ void main() { ...@@ -402,7 +403,7 @@ void main() {
expect(value, equals('two')); expect(value, equals('two'));
}); });
testWidgets('Dropdown button with no app', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button with no app', (WidgetTester tester) async {
String? value = 'one'; String? value = 'one';
void didChangeValue(String? newValue) { void didChangeValue(String? newValue) {
value = newValue; value = newValue;
...@@ -461,7 +462,7 @@ void main() { ...@@ -461,7 +462,7 @@ void main() {
expect(value, equals('two')); expect(value, equals('two'));
}); });
testWidgets('DropdownButton does not allow duplicate item values', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton does not allow duplicate item values', (WidgetTester tester) async {
final List<DropdownMenuItem<String>> itemsWithDuplicateValues = <String>['a', 'b', 'c', 'c'] final List<DropdownMenuItem<String>> itemsWithDuplicateValues = <String>['a', 'b', 'c', 'c']
.map<DropdownMenuItem<String>>((String value) { .map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
...@@ -490,7 +491,7 @@ void main() { ...@@ -490,7 +491,7 @@ void main() {
); );
}); });
testWidgets('DropdownButton value should only appear in one menu item', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton value should only appear in one menu item', (WidgetTester tester) async {
final List<DropdownMenuItem<String>> itemsWithDuplicateValues = <String>['a', 'b', 'c', 'd'] final List<DropdownMenuItem<String>> itemsWithDuplicateValues = <String>['a', 'b', 'c', 'd']
.map<DropdownMenuItem<String>>((String value) { .map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
...@@ -519,7 +520,7 @@ void main() { ...@@ -519,7 +520,7 @@ void main() {
); );
}); });
testWidgets('Dropdown form field uses form field state', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown form field uses form field state', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
final GlobalKey<FormState> formKey = GlobalKey<FormState>(); final GlobalKey<FormState> formKey = GlobalKey<FormState>();
String? value; String? value;
...@@ -576,7 +577,7 @@ void main() { ...@@ -576,7 +577,7 @@ void main() {
expect(value, equals('three')); expect(value, equals('three'));
}); });
testWidgets('Dropdown in ListView', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown in ListView', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/12053 // Regression test for https://github.com/flutter/flutter/issues/12053
// Positions a DropdownButton at the left and right edges of the screen, // Positions a DropdownButton at the left and right edges of the screen,
// forcing it to be sized down to the viewport width // forcing it to be sized down to the viewport width
...@@ -611,7 +612,7 @@ void main() { ...@@ -611,7 +612,7 @@ void main() {
expect(itemBoxes[1].size.width, equals(800.0 - 16.0 * 2)); expect(itemBoxes[1].size.width, equals(800.0 - 16.0 * 2));
}); });
testWidgets('Dropdown menu can position correctly inside a nested navigator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu can position correctly inside a nested navigator', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/66870 // Regression test for https://github.com/flutter/flutter/issues/66870
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -657,7 +658,7 @@ void main() { ...@@ -657,7 +658,7 @@ void main() {
expect(secondItem.localToGlobal(Offset.zero).dy, equals(176.0)); expect(secondItem.localToGlobal(Offset.zero).dy, equals(176.0));
}); });
testWidgets('Dropdown screen edges', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown screen edges', (WidgetTester tester) async {
int? value = 4; int? value = 4;
final List<DropdownMenuItem<int>> items = <DropdownMenuItem<int>>[ final List<DropdownMenuItem<int>> items = <DropdownMenuItem<int>>[
for (int i = 0; i < 20; ++i) DropdownMenuItem<int>(value: i, child: Text('$i')), for (int i = 0; i < 20; ++i) DropdownMenuItem<int>(value: i, child: Text('$i')),
...@@ -701,7 +702,7 @@ void main() { ...@@ -701,7 +702,7 @@ void main() {
}); });
for (final TextDirection textDirection in TextDirection.values) { for (final TextDirection textDirection in TextDirection.values) {
testWidgets('Dropdown button aligns selected menu item ($textDirection)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button aligns selected menu item ($textDirection)', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build() => buildFrame(buttonKey: buttonKey, textDirection: textDirection, onChanged: onChanged, useMaterial3: false); Widget build() => buildFrame(buttonKey: buttonKey, textDirection: textDirection, onChanged: onChanged, useMaterial3: false);
...@@ -745,7 +746,7 @@ void main() { ...@@ -745,7 +746,7 @@ void main() {
}); });
} }
testWidgets('Arrow icon aligns with the edge of button when expanded', (WidgetTester tester) async { testWidgetsWithLeakTracking('Arrow icon aligns with the edge of button when expanded', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build() => buildFrame(buttonKey: buttonKey, isExpanded: true, onChanged: onChanged); Widget build() => buildFrame(buttonKey: buttonKey, isExpanded: true, onChanged: onChanged);
...@@ -764,7 +765,7 @@ void main() { ...@@ -764,7 +765,7 @@ void main() {
); );
}); });
testWidgets('Dropdown button icon will accept widgets as icons', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button icon will accept widgets as icons', (WidgetTester tester) async {
final Widget customWidget = Container( final Widget customWidget = Container(
decoration: ShapeDecoration( decoration: ShapeDecoration(
shape: CircleBorder( shape: CircleBorder(
...@@ -793,7 +794,7 @@ void main() { ...@@ -793,7 +794,7 @@ void main() {
expect(find.byIcon(Icons.arrow_drop_down), findsNothing); expect(find.byIcon(Icons.arrow_drop_down), findsNothing);
}); });
testWidgets('Dropdown button icon should have default size and colors when not defined', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button icon should have default size and colors when not defined', (WidgetTester tester) async {
final Key iconKey = UniqueKey(); final Key iconKey = UniqueKey();
final Icon customIcon = Icon(Icons.assessment, key: iconKey); final Icon customIcon = Icon(Icons.assessment, key: iconKey);
...@@ -819,7 +820,7 @@ void main() { ...@@ -819,7 +820,7 @@ void main() {
expect(disabledRichText.text.style!.color, Colors.grey.shade400); expect(disabledRichText.text.style!.color, Colors.grey.shade400);
}); });
testWidgets('Dropdown button icon should have the passed in size and color instead of defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button icon should have the passed in size and color instead of defaults', (WidgetTester tester) async {
final Key iconKey = UniqueKey(); final Key iconKey = UniqueKey();
final Icon customIcon = Icon(Icons.assessment, key: iconKey); final Icon customIcon = Icon(Icons.assessment, key: iconKey);
...@@ -851,7 +852,7 @@ void main() { ...@@ -851,7 +852,7 @@ void main() {
expect(disabledRichText.text.style!.color, Colors.orange); expect(disabledRichText.text.style!.color, Colors.orange);
}); });
testWidgets('Dropdown button should use its own size and color properties over those defined by the theme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button should use its own size and color properties over those defined by the theme', (WidgetTester tester) async {
final Key iconKey = UniqueKey(); final Key iconKey = UniqueKey();
final Icon customIcon = Icon( final Icon customIcon = Icon(
...@@ -889,7 +890,7 @@ void main() { ...@@ -889,7 +890,7 @@ void main() {
expect(disabledRichText.text.style!.color, Colors.yellow); expect(disabledRichText.text.style!.color, Colors.yellow);
}); });
testWidgets('Dropdown button with isDense:true aligns selected menu item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button with isDense:true aligns selected menu item', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build() => buildFrame(buttonKey: buttonKey, isDense: true, onChanged: onChanged); Widget build() => buildFrame(buttonKey: buttonKey, isDense: true, onChanged: onChanged);
...@@ -925,7 +926,7 @@ void main() { ...@@ -925,7 +926,7 @@ void main() {
checkSelectedItemTextGeometry(tester, 'two'); checkSelectedItemTextGeometry(tester, 'two');
}); });
testWidgets('Dropdown button can have a text style with no fontSize specified', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button can have a text style with no fontSize specified', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/33425 // Regression test for https://github.com/flutter/flutter/issues/33425
const String value = 'foo'; const String value = 'foo';
final UniqueKey itemKey = UniqueKey(); final UniqueKey itemKey = UniqueKey();
...@@ -952,7 +953,7 @@ void main() { ...@@ -952,7 +953,7 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('Dropdown menu scrolls to first item in long lists', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu scrolls to first item in long lists', (WidgetTester tester) async {
// Open the dropdown menu // Open the dropdown menu
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
...@@ -981,7 +982,7 @@ void main() { ...@@ -981,7 +982,7 @@ void main() {
); );
}); });
testWidgets('Dropdown menu aligns selected item with button in long lists', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu aligns selected item with button in long lists', (WidgetTester tester) async {
// Open the dropdown menu // Open the dropdown menu
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
...@@ -1006,7 +1007,7 @@ void main() { ...@@ -1006,7 +1007,7 @@ void main() {
); );
}); });
testWidgets('Dropdown menu scrolls to last item in long lists', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu scrolls to last item in long lists', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
buttonKey: buttonKey, buttonKey: buttonKey,
...@@ -1042,7 +1043,7 @@ void main() { ...@@ -1042,7 +1043,7 @@ void main() {
); );
}); });
testWidgets('Size of DropdownButton with null value', (WidgetTester tester) async { testWidgetsWithLeakTracking('Size of DropdownButton with null value', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
String? value; String? value;
...@@ -1063,7 +1064,7 @@ void main() { ...@@ -1063,7 +1064,7 @@ void main() {
expect(buttonBox.size, equals(buttonBoxNullValue.size)); expect(buttonBox.size, equals(buttonBoxNullValue.size));
}); });
testWidgets('Size of DropdownButton with no items', (WidgetTester tester) async { testWidgetsWithLeakTracking('Size of DropdownButton with no items', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/26419 // Regression test for https://github.com/flutter/flutter/issues/26419
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
List<String>? items; List<String>? items;
...@@ -1090,7 +1091,7 @@ void main() { ...@@ -1090,7 +1091,7 @@ void main() {
expect(buttonBox.size, equals(buttonBoxNullItems.size)); expect(buttonBox.size, equals(buttonBoxNullItems.size));
}); });
testWidgets('Layout of a DropdownButton with null value', (WidgetTester tester) async { testWidgetsWithLeakTracking('Layout of a DropdownButton with null value', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
String? value; String? value;
...@@ -1118,7 +1119,7 @@ void main() { ...@@ -1118,7 +1119,7 @@ void main() {
expect(value, equals('one')); expect(value, equals('one'));
}); });
testWidgets('Size of DropdownButton with null value and a hint', (WidgetTester tester) async { testWidgetsWithLeakTracking('Size of DropdownButton with null value and a hint', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
String? value; String? value;
...@@ -1141,7 +1142,7 @@ void main() { ...@@ -1141,7 +1142,7 @@ void main() {
expect(buttonBox.size, equals(buttonBoxHintValue.size)); expect(buttonBox.size, equals(buttonBoxHintValue.size));
}); });
testWidgets('Dropdown menus must fit within the screen', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menus must fit within the screen', (WidgetTester tester) async {
// The dropdown menu isn't readily accessible. To find it we're assuming that it // The dropdown menu isn't readily accessible. To find it we're assuming that it
// contains a ListView and that it's an instance of _DropdownMenu. // contains a ListView and that it's an instance of _DropdownMenu.
...@@ -1248,7 +1249,7 @@ void main() { ...@@ -1248,7 +1249,7 @@ void main() {
expect(menuRect.bottomRight, const Offset(800.0, 600.0)); expect(menuRect.bottomRight, const Offset(800.0, 600.0));
}); });
testWidgets('Dropdown menus are dismissed on screen orientation changes, but not on keyboard hide', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menus are dismissed on screen orientation changes, but not on keyboard hide', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(onChanged: onChanged, mediaSize: const Size(800, 600))); await tester.pumpWidget(buildFrame(onChanged: onChanged, mediaSize: const Size(800, 600)));
await tester.tap(find.byType(dropdownButtonType)); await tester.tap(find.byType(dropdownButtonType));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1270,7 +1271,7 @@ void main() { ...@@ -1270,7 +1271,7 @@ void main() {
expect(find.byType(ListView, skipOffstage: false), findsNothing); expect(find.byType(ListView, skipOffstage: false), findsNothing);
}); });
testWidgets('Semantics Tree contains only selected element', (WidgetTester tester) async { testWidgetsWithLeakTracking('Semantics Tree contains only selected element', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(buildFrame(onChanged: onChanged)); await tester.pumpWidget(buildFrame(onChanged: onChanged));
...@@ -1282,7 +1283,7 @@ void main() { ...@@ -1282,7 +1283,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Dropdown button includes semantics', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button includes semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final SemanticsHandle handle = tester.ensureSemantics();
const Key key = Key('test'); const Key key = Key('test');
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
...@@ -1317,7 +1318,7 @@ void main() { ...@@ -1317,7 +1318,7 @@ void main() {
handle.dispose(); handle.dispose();
}); });
testWidgets('Dropdown menu includes semantics', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu includes semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
const Key key = Key('test'); const Key key = Key('test');
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
...@@ -1393,7 +1394,7 @@ void main() { ...@@ -1393,7 +1394,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('disabledHint displays on empty items or onChanged', (WidgetTester tester) async { testWidgetsWithLeakTracking('disabledHint displays on empty items or onChanged', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build({ List<String>? items, ValueChanged<String?>? onChanged }) => buildFrame( Widget build({ List<String>? items, ValueChanged<String?>? onChanged }) => buildFrame(
...@@ -1432,7 +1433,7 @@ void main() { ...@@ -1432,7 +1433,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/70177 // Regression test for https://github.com/flutter/flutter/issues/70177
testWidgets('disabledHint behavior test', (WidgetTester tester) async { testWidgetsWithLeakTracking('disabledHint behavior test', (WidgetTester tester) async {
Widget build({ List<String>? items, ValueChanged<String?>? onChanged, String? value, Widget? hint, Widget? disabledHint }) => buildFrame( Widget build({ List<String>? items, ValueChanged<String?>? onChanged, String? value, Widget? hint, Widget? disabledHint }) => buildFrame(
items: items, items: items,
onChanged: onChanged, onChanged: onChanged,
...@@ -1489,7 +1490,7 @@ void main() { ...@@ -1489,7 +1490,7 @@ void main() {
expect(getIndex(), null); expect(getIndex(), null);
}); });
testWidgets('DropdownButton selected item color test', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton selected item color test', (WidgetTester tester) async {
Widget build({ ValueChanged<String?>? onChanged, String? value, Widget? hint, Widget? disabledHint }) { Widget build({ ValueChanged<String?>? onChanged, String? value, Widget? hint, Widget? disabledHint }) {
return MaterialApp( return MaterialApp(
theme: ThemeData( theme: ThemeData(
...@@ -1543,7 +1544,7 @@ void main() { ...@@ -1543,7 +1544,7 @@ void main() {
expect(textColor('two'), Colors.pink); expect(textColor('two'), Colors.pink);
}); });
testWidgets( testWidgetsWithLeakTracking(
'DropdownButton hint displays when the items list is empty, ' 'DropdownButton hint displays when the items list is empty, '
'items is null, and disabledHint is null', 'items is null, and disabledHint is null',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1567,7 +1568,7 @@ void main() { ...@@ -1567,7 +1568,7 @@ void main() {
}, },
); );
testWidgets('DropdownButton disabledHint is null by default', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton disabledHint is null by default', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget build({ List<String>? items }) { Widget build({ List<String>? items }) {
...@@ -1587,7 +1588,7 @@ void main() { ...@@ -1587,7 +1588,7 @@ void main() {
expect(find.text('hint used when disabled'), findsOneWidget); expect(find.text('hint used when disabled'), findsOneWidget);
}); });
testWidgets('Size of largest widget is used DropdownButton when selectedItemBuilder is non-null', (WidgetTester tester) async { testWidgetsWithLeakTracking('Size of largest widget is used DropdownButton when selectedItemBuilder is non-null', (WidgetTester tester) async {
final List<String> items = <String>['25', '50', '100']; final List<String> items = <String>['25', '50', '100'];
const String selectedItem = '25'; const String selectedItem = '25';
...@@ -1619,7 +1620,7 @@ void main() { ...@@ -1619,7 +1620,7 @@ void main() {
expect(dropdownButtonRenderBox.size.width, 100 + 24.0); expect(dropdownButtonRenderBox.size.width, 100 + 24.0);
}); });
testWidgets( testWidgetsWithLeakTracking(
'Enabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Enabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null and hint is defined, but smaller than largest selected item widget', 'is non-null and hint is defined, but smaller than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1657,7 +1658,7 @@ void main() { ...@@ -1657,7 +1658,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Enabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Enabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null and hint is defined, but larger than largest selected item widget', 'is non-null and hint is defined, but larger than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1699,7 +1700,7 @@ void main() { ...@@ -1699,7 +1700,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null, and hint is defined, but smaller than largest selected item widget', 'is non-null, and hint is defined, but smaller than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1736,7 +1737,7 @@ void main() { ...@@ -1736,7 +1737,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null and hint is defined, but larger than largest selected item widget', 'is non-null and hint is defined, but larger than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1773,7 +1774,7 @@ void main() { ...@@ -1773,7 +1774,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null, and disabledHint is defined, but smaller than largest selected item widget', 'is non-null, and disabledHint is defined, but smaller than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1810,7 +1811,7 @@ void main() { ...@@ -1810,7 +1811,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder ' 'Disabled button - Size of largest widget is used DropdownButton when selectedItemBuilder '
'is non-null and disabledHint is defined, but larger than largest selected item widget', 'is non-null and disabledHint is defined, but larger than largest selected item widget',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1847,7 +1848,7 @@ void main() { ...@@ -1847,7 +1848,7 @@ void main() {
}, },
); );
testWidgets('Dropdown in middle showing middle item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown in middle showing middle item', (WidgetTester tester) async {
final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate( final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate(
100, 100,
(int i) => DropdownMenuItem<int>(value: i, child: Text('$i')), (int i) => DropdownMenuItem<int>(value: i, child: Text('$i')),
...@@ -1881,7 +1882,7 @@ void main() { ...@@ -1881,7 +1882,7 @@ void main() {
expect(getMenuScroll(), 2180.0); expect(getMenuScroll(), 2180.0);
}); });
testWidgets('Dropdown in top showing bottom item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown in top showing bottom item', (WidgetTester tester) async {
final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate( final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate(
100, 100,
(int i) => DropdownMenuItem<int>(value: i, child: Text('$i')), (int i) => DropdownMenuItem<int>(value: i, child: Text('$i')),
...@@ -1916,7 +1917,7 @@ void main() { ...@@ -1916,7 +1917,7 @@ void main() {
expect(getMenuScroll(), 4312.0); expect(getMenuScroll(), 4312.0);
}); });
testWidgets('Dropdown in bottom showing top item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown in bottom showing top item', (WidgetTester tester) async {
final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate( final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate(
100, 100,
(int i) => DropdownMenuItem<int>(value: i, child: Text('$i')), (int i) => DropdownMenuItem<int>(value: i, child: Text('$i')),
...@@ -1951,7 +1952,7 @@ void main() { ...@@ -1951,7 +1952,7 @@ void main() {
expect(getMenuScroll(), 0.0); expect(getMenuScroll(), 0.0);
}); });
testWidgets('Dropdown in center showing bottom item', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown in center showing bottom item', (WidgetTester tester) async {
final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate( final List<DropdownMenuItem<int>> items = List<DropdownMenuItem<int>>.generate(
100, 100,
(int i) => DropdownMenuItem<int>(value: i, child: Text('$i')), (int i) => DropdownMenuItem<int>(value: i, child: Text('$i')),
...@@ -1985,7 +1986,7 @@ void main() { ...@@ -1985,7 +1986,7 @@ void main() {
expect(getMenuScroll(), 4312.0); expect(getMenuScroll(), 4312.0);
}); });
testWidgets('Dropdown menu respects parent size limits', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu respects parent size limits', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/24417 // Regression test for https://github.com/flutter/flutter/issues/24417
int? selectedIndex; int? selectedIndex;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2031,7 +2032,7 @@ void main() { ...@@ -2031,7 +2032,7 @@ void main() {
expect(selectedIndex, 13); expect(selectedIndex, 13);
}); });
testWidgets('Dropdown button will accept widgets as its underline', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown button will accept widgets as its underline', (WidgetTester tester) async {
const BoxDecoration decoration = BoxDecoration( const BoxDecoration decoration = BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xFFCCBB00), width: 4.0)), border: Border(bottom: BorderSide(color: Color(0xFFCCBB00), width: 4.0)),
); );
...@@ -2058,7 +2059,7 @@ void main() { ...@@ -2058,7 +2059,7 @@ void main() {
expect(tester.widgetList<DecoratedBox>(decoratedBox).last.decoration, defaultDecoration); expect(tester.widgetList<DecoratedBox>(decoratedBox).last.decoration, defaultDecoration);
}); });
testWidgets('DropdownButton selectedItemBuilder builds custom buttons', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton selectedItemBuilder builds custom buttons', (WidgetTester tester) async {
const List<String> items = <String>[ const List<String> items = <String>[
'One', 'One',
'Two', 'Two',
...@@ -2104,19 +2105,19 @@ void main() { ...@@ -2104,19 +2105,19 @@ void main() {
expect(find.text('Two as an Arabic numeral: 2'), findsOneWidget); expect(find.text('Two as an Arabic numeral: 2'), findsOneWidget);
}); });
testWidgets('DropdownButton uses default color when expanded', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton uses default color when expanded', (WidgetTester tester) async {
await checkDropdownColor(tester); await checkDropdownColor(tester);
}); });
testWidgets('DropdownButton uses dropdownColor when expanded', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton uses dropdownColor when expanded', (WidgetTester tester) async {
await checkDropdownColor(tester, color: const Color.fromRGBO(120, 220, 70, 0.8)); await checkDropdownColor(tester, color: const Color.fromRGBO(120, 220, 70, 0.8));
}); });
testWidgets('DropdownButtonFormField uses dropdownColor when expanded', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButtonFormField uses dropdownColor when expanded', (WidgetTester tester) async {
await checkDropdownColor(tester, color: const Color.fromRGBO(120, 220, 70, 0.8), isFormField: true); await checkDropdownColor(tester, color: const Color.fromRGBO(120, 220, 70, 0.8), isFormField: true);
}); });
testWidgets('DropdownButton hint displays properly when selectedItemBuilder is defined', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton hint displays properly when selectedItemBuilder is defined', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/42340 // Regression test for https://github.com/flutter/flutter/issues/42340
final List<String> items = <String>['1', '2', '3']; final List<String> items = <String>['1', '2', '3'];
String? selectedItem; String? selectedItem;
...@@ -2162,7 +2163,7 @@ void main() { ...@@ -2162,7 +2163,7 @@ void main() {
expect(find.text('You have selected: 1'), findsOneWidget); expect(find.text('You have selected: 1'), findsOneWidget);
}); });
testWidgets('Variable size and oversized menu items', (WidgetTester tester) async { testWidgetsWithLeakTracking('Variable size and oversized menu items', (WidgetTester tester) async {
final List<double> itemHeights = <double>[30, 40, 50, 60]; final List<double> itemHeights = <double>[30, 40, 50, 60];
double? dropdownValue = itemHeights[0]; double? dropdownValue = itemHeights[0];
...@@ -2261,7 +2262,7 @@ void main() { ...@@ -2261,7 +2262,7 @@ void main() {
expect(tester.getCenter(item40.first).dy, tester.getCenter(item40.last).dy); expect(tester.getCenter(item40.first).dy, tester.getCenter(item40.last).dy);
}); });
testWidgets('DropdownButton menu items do not resize when its route is popped', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton menu items do not resize when its route is popped', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/44877. // Regression test for https://github.com/flutter/flutter/issues/44877.
const List<String> items = <String>[ const List<String> items = <String>[
'one', 'one',
...@@ -2317,7 +2318,7 @@ void main() { ...@@ -2317,7 +2318,7 @@ void main() {
expect(find.text('two'), findsOneWidget); expect(find.text('two'), findsOneWidget);
}); });
testWidgets('DropdownButton hint is selected item', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton hint is selected item', (WidgetTester tester) async {
const double hintPaddingOffset = 8; const double hintPaddingOffset = 8;
const List<String> itemValues = <String>['item0', 'item1', 'item2', 'item3']; const List<String> itemValues = <String>['item0', 'item1', 'item2', 'item3'];
String? selectedItem = 'item0'; String? selectedItem = 'item0';
...@@ -2385,10 +2386,12 @@ void main() { ...@@ -2385,10 +2386,12 @@ void main() {
expect(tester.getTopLeft(find.text('-item0-')).dx, 8); expect(tester.getTopLeft(find.text('-item0-')).dx, 8);
}); });
testWidgets('DropdownButton can be focused, and has focusColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton can be focused, and has focusColor', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final UniqueKey buttonKey = UniqueKey(); final UniqueKey buttonKey = UniqueKey();
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
await tester.pumpWidget(buildFrame(buttonKey: buttonKey, onChanged: onChanged, focusNode: focusNode, autofocus: true, useMaterial3: false)); await tester.pumpWidget(buildFrame(buttonKey: buttonKey, onChanged: onChanged, focusNode: focusNode, autofocus: true, useMaterial3: false));
await tester.pumpAndSettle(); // Pump a frame for autofocus to take effect. await tester.pumpAndSettle(); // Pump a frame for autofocus to take effect.
expect(focusNode.hasPrimaryFocus, isTrue); expect(focusNode.hasPrimaryFocus, isTrue);
...@@ -2399,10 +2402,12 @@ void main() { ...@@ -2399,10 +2402,12 @@ void main() {
expect(find.byType(Material), paints..rect(rect: const Rect.fromLTRB(348.0, 276.0, 452.0, 324.0), color: const Color(0x1f00ff00))); expect(find.byType(Material), paints..rect(rect: const Rect.fromLTRB(348.0, 276.0, 452.0, 324.0), color: const Color(0x1f00ff00)));
}); });
testWidgets('DropdownButtonFormField can be focused, and has focusColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButtonFormField can be focused, and has focusColor', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
final UniqueKey buttonKey = UniqueKey(); final UniqueKey buttonKey = UniqueKey();
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButtonFormField'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButtonFormField');
addTearDown(focusNode.dispose);
await tester.pumpWidget(buildFrame(isFormField: true, buttonKey: buttonKey, onChanged: onChanged, focusNode: focusNode, autofocus: true)); await tester.pumpWidget(buildFrame(isFormField: true, buttonKey: buttonKey, onChanged: onChanged, focusNode: focusNode, autofocus: true));
await tester.pumpAndSettle(); // Pump a frame for autofocus to take effect. await tester.pumpAndSettle(); // Pump a frame for autofocus to take effect.
expect(focusNode.hasPrimaryFocus, isTrue); expect(focusNode.hasPrimaryFocus, isTrue);
...@@ -2413,17 +2418,20 @@ void main() { ...@@ -2413,17 +2418,20 @@ void main() {
expect(find.byType(Material), paints ..rect(rect: const Rect.fromLTRB(0.0, 264.0, 800.0, 336.0), color: const Color(0x1f00ff00))); expect(find.byType(Material), paints ..rect(rect: const Rect.fromLTRB(0.0, 264.0, 800.0, 336.0), color: const Color(0x1f00ff00)));
}); });
testWidgets("DropdownButton won't be focused if not enabled", (WidgetTester tester) async { testWidgetsWithLeakTracking("DropdownButton won't be focused if not enabled", (WidgetTester tester) async {
final UniqueKey buttonKey = UniqueKey(); final UniqueKey buttonKey = UniqueKey();
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
await tester.pumpWidget(buildFrame(buttonKey: buttonKey, focusNode: focusNode, autofocus: true, focusColor: const Color(0xff00ff00))); await tester.pumpWidget(buildFrame(buttonKey: buttonKey, focusNode: focusNode, autofocus: true, focusColor: const Color(0xff00ff00)));
await tester.pump(); // Pump a frame for autofocus to take effect (although it shouldn't). await tester.pump(); // Pump a frame for autofocus to take effect (although it shouldn't).
expect(focusNode.hasPrimaryFocus, isFalse); expect(focusNode.hasPrimaryFocus, isFalse);
expect(find.byKey(buttonKey), isNot(paints ..rrect(rrect: const RRect.fromLTRBXY(0.0, 0.0, 104.0, 48.0, 4.0, 4.0), color: const Color(0xff00ff00)))); expect(find.byKey(buttonKey), isNot(paints ..rrect(rrect: const RRect.fromLTRBXY(0.0, 0.0, 104.0, 48.0, 4.0, 4.0), color: const Color(0xff00ff00))));
}); });
testWidgets('DropdownButton is activated with the enter key', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton is activated with the enter key', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
String? value = 'one'; String? value = 'one';
Widget buildFrame() { Widget buildFrame() {
...@@ -2478,7 +2486,7 @@ void main() { ...@@ -2478,7 +2486,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/77655. // Regression test for https://github.com/flutter/flutter/issues/77655.
testWidgets('DropdownButton selecting a null valued item should be selected', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton selecting a null valued item should be selected', (WidgetTester tester) async {
final List<MapEntry<String?, String>> items = <MapEntry<String?, String>>[ final List<MapEntry<String?, String>> items = <MapEntry<String?, String>>[
const MapEntry<String?, String>(null, 'None'), const MapEntry<String?, String>(null, 'None'),
const MapEntry<String?, String>('one', 'One'), const MapEntry<String?, String>('one', 'One'),
...@@ -2518,8 +2526,9 @@ void main() { ...@@ -2518,8 +2526,9 @@ void main() {
expect(find.text('None'), findsOneWidget); expect(find.text('None'), findsOneWidget);
}); });
testWidgets('DropdownButton is activated with the space key', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton is activated with the space key', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
String? value = 'one'; String? value = 'one';
Widget buildFrame() { Widget buildFrame() {
...@@ -2573,8 +2582,9 @@ void main() { ...@@ -2573,8 +2582,9 @@ void main() {
expect(value, equals('two')); expect(value, equals('two'));
}); });
testWidgets('Selected element is focused when dropdown is opened', (WidgetTester tester) async { testWidgetsWithLeakTracking('Selected element is focused when dropdown is opened', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
String? value = 'one'; String? value = 'one';
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -2633,8 +2643,9 @@ void main() { ...@@ -2633,8 +2643,9 @@ void main() {
expect(node.hasFocus, isTrue); expect(node.hasFocus, isTrue);
}); });
testWidgets('Selected element is correctly focused with dropdown that more items than fit on the screen', (WidgetTester tester) async { testWidgetsWithLeakTracking('Selected element is correctly focused with dropdown that more items than fit on the screen', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
int? value = 1; int? value = 1;
final List<int> hugeMenuItems = List<int>.generate(50, (int index) => index); final List<int> hugeMenuItems = List<int>.generate(50, (int index) => index);
...@@ -2696,8 +2707,9 @@ void main() { ...@@ -2696,8 +2707,9 @@ void main() {
expect(node.hasFocus, isTrue); expect(node.hasFocus, isTrue);
}); });
testWidgets("Having a focused element doesn't interrupt scroll when flung by touch", (WidgetTester tester) async { testWidgetsWithLeakTracking("Having a focused element doesn't interrupt scroll when flung by touch", (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
int? value = 1; int? value = 1;
final List<int> hugeMenuItems = List<int>.generate(100, (int index) => index); final List<int> hugeMenuItems = List<int>.generate(100, (int index) => index);
...@@ -2771,8 +2783,9 @@ void main() { ...@@ -2771,8 +2783,9 @@ void main() {
expect(Focus.of(tester.element(find.byKey(const ValueKey<int>(91), skipOffstage: false).last)).hasPrimaryFocus, isFalse); expect(Focus.of(tester.element(find.byKey(const ValueKey<int>(91), skipOffstage: false).last)).hasPrimaryFocus, isFalse);
}); });
testWidgets('DropdownButton onTap callback can request focus', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton onTap callback can request focus', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton')..addListener(() { }); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton')..addListener(() { });
addTearDown(focusNode.dispose);
int? value = 1; int? value = 1;
final List<int> hugeMenuItems = List<int>.generate(100, (int index) => index); final List<int> hugeMenuItems = List<int>.generate(100, (int index) => index);
...@@ -2819,8 +2832,9 @@ void main() { ...@@ -2819,8 +2832,9 @@ void main() {
expect(focusNode.hasPrimaryFocus, isTrue); expect(focusNode.hasPrimaryFocus, isTrue);
}); });
testWidgets('DropdownButton changes selected item with arrow keys', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton changes selected item with arrow keys', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton'); final FocusNode focusNode = FocusNode(debugLabel: 'DropdownButton');
addTearDown(focusNode.dispose);
String? value = 'one'; String? value = 'one';
Widget buildFrame() { Widget buildFrame() {
...@@ -2878,7 +2892,7 @@ void main() { ...@@ -2878,7 +2892,7 @@ void main() {
expect(value, equals('two')); expect(value, equals('two'));
}); });
testWidgets('DropdownButton onTap callback is called when defined', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton onTap callback is called when defined', (WidgetTester tester) async {
int dropdownButtonTapCounter = 0; int dropdownButtonTapCounter = 0;
String? value = 'one'; String? value = 'one';
...@@ -2923,7 +2937,7 @@ void main() { ...@@ -2923,7 +2937,7 @@ void main() {
expect(dropdownButtonTapCounter, 2); // Should not change. expect(dropdownButtonTapCounter, 2); // Should not change.
}); });
testWidgets('DropdownMenuItem onTap callback is called when defined', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownMenuItem onTap callback is called when defined', (WidgetTester tester) async {
String? value = 'one'; String? value = 'one';
final List<int> menuItemTapCounters = <int>[0, 0, 0, 0]; final List<int> menuItemTapCounters = <int>[0, 0, 0, 0];
void onChanged(String? newValue) { value = newValue; } void onChanged(String? newValue) { value = newValue; }
...@@ -3008,7 +3022,7 @@ void main() { ...@@ -3008,7 +3022,7 @@ void main() {
expect(menuItemTapCounters, <int>[0, 2, 1, 0]); expect(menuItemTapCounters, <int>[0, 2, 1, 0]);
}); });
testWidgets('Does not crash when option is selected without waiting for opening animation to complete', (WidgetTester tester) async { testWidgetsWithLeakTracking('Does not crash when option is selected without waiting for opening animation to complete', (WidgetTester tester) async {
// Regression test for b/171846624. // Regression test for b/171846624.
final List<String> options = <String>['first', 'second', 'third']; final List<String> options = <String>['first', 'second', 'third'];
...@@ -3056,7 +3070,7 @@ void main() { ...@@ -3056,7 +3070,7 @@ void main() {
expect(find.text('second').hitTestable(), findsNothing); expect(find.text('second').hitTestable(), findsNothing);
}); });
testWidgets('Dropdown menu should persistently show a scrollbar if it is scrollable', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown menu should persistently show a scrollbar if it is scrollable', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
value: '0', value: '0',
// menu is short enough to fit onto the screen. // menu is short enough to fit onto the screen.
...@@ -3088,7 +3102,7 @@ void main() { ...@@ -3088,7 +3102,7 @@ void main() {
expect(find.byType(Scrollbar), paints..rect()); expect(find.byType(Scrollbar), paints..rect());
}); });
testWidgets("Dropdown menu's maximum height should be influenced by DropdownButton.menuMaxHeight.", (WidgetTester tester) async { testWidgetsWithLeakTracking("Dropdown menu's maximum height should be influenced by DropdownButton.menuMaxHeight.", (WidgetTester tester) async {
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
value: '0', value: '0',
items: List<String>.generate(/*length=*/64, (int index) => index.toString()), items: List<String>.generate(/*length=*/64, (int index) => index.toString()),
...@@ -3142,7 +3156,7 @@ void main() { ...@@ -3142,7 +3156,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/89029 // Regression test for https://github.com/flutter/flutter/issues/89029
testWidgets('menu position test with `menuMaxHeight`', (WidgetTester tester) async { testWidgetsWithLeakTracking('menu position test with `menuMaxHeight`', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
await tester.pumpWidget(buildFrame( await tester.pumpWidget(buildFrame(
buttonKey: buttonKey, buttonKey: buttonKey,
...@@ -3165,7 +3179,7 @@ void main() { ...@@ -3165,7 +3179,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/76614 // Regression test for https://github.com/flutter/flutter/issues/76614
testWidgets('Do not crash if used in very short screen', (WidgetTester tester) async { testWidgetsWithLeakTracking('Do not crash if used in very short screen', (WidgetTester tester) async {
// The default item height is 48.0 pixels and needs two items padding since // The default item height is 48.0 pixels and needs two items padding since
// the menu requires empty space surrounding the menu. Finally, the constraint height // the menu requires empty space surrounding the menu. Finally, the constraint height
// is 47.0 pixels for the menu rendering. // is 47.0 pixels for the menu rendering.
...@@ -3221,7 +3235,7 @@ void main() { ...@@ -3221,7 +3235,7 @@ void main() {
); );
}); });
testWidgets('Tapping a disabled item should not close DropdownButton', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tapping a disabled item should not close DropdownButton', (WidgetTester tester) async {
String? value = 'first'; String? value = 'first';
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3267,7 +3281,7 @@ void main() { ...@@ -3267,7 +3281,7 @@ void main() {
expect(find.text('second').hitTestable(), findsOneWidget); expect(find.text('second').hitTestable(), findsOneWidget);
}); });
testWidgets('Disabled item should not be focusable', (WidgetTester tester) async { testWidgetsWithLeakTracking('Disabled item should not be focusable', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -3298,7 +3312,7 @@ void main() { ...@@ -3298,7 +3312,7 @@ void main() {
expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus'); expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus');
}); });
testWidgets('alignment test', (WidgetTester tester) async { testWidgetsWithLeakTracking('alignment test', (WidgetTester tester) async {
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
Widget buildFrame({AlignmentGeometry? buttonAlignment, AlignmentGeometry? menuAlignment}) { Widget buildFrame({AlignmentGeometry? buttonAlignment, AlignmentGeometry? menuAlignment}) {
return MaterialApp( return MaterialApp(
...@@ -3402,7 +3416,7 @@ void main() { ...@@ -3402,7 +3416,7 @@ void main() {
); );
} }
testWidgets('Dropdown with enabled feedback', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown with enabled feedback', (WidgetTester tester) async {
const bool enableFeedback = true; const bool enableFeedback = true;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback)); await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
...@@ -3415,7 +3429,7 @@ void main() { ...@@ -3415,7 +3429,7 @@ void main() {
expect(feedback.hapticCount, 0); expect(feedback.hapticCount, 0);
}); });
testWidgets('Dropdown with disabled feedback', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown with disabled feedback', (WidgetTester tester) async {
const bool enableFeedback = false; const bool enableFeedback = false;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback)); await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
...@@ -3428,7 +3442,7 @@ void main() { ...@@ -3428,7 +3442,7 @@ void main() {
expect(feedback.hapticCount, 0); expect(feedback.hapticCount, 0);
}); });
testWidgets('Dropdown with enabled feedback by default', (WidgetTester tester) async { testWidgetsWithLeakTracking('Dropdown with enabled feedback by default', (WidgetTester tester) async {
await tester.pumpWidget(feedbackBoilerplate()); await tester.pumpWidget(feedbackBoilerplate());
await tester.tap(find.text('One')); await tester.tap(find.text('One'));
...@@ -3440,7 +3454,7 @@ void main() { ...@@ -3440,7 +3454,7 @@ void main() {
}); });
}); });
testWidgets('DropdownButton changes mouse cursor when hovered', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton changes mouse cursor when hovered', (WidgetTester tester) async {
const Key key = Key('testDropdownButton'); const Key key = Key('testDropdownButton');
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -3498,7 +3512,7 @@ void main() { ...@@ -3498,7 +3512,7 @@ void main() {
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic); expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
}); });
testWidgets('Conflicting scrollbars are not applied by ScrollBehavior to Dropdown', (WidgetTester tester) async { testWidgetsWithLeakTracking('Conflicting scrollbars are not applied by ScrollBehavior to Dropdown', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/83819 // Regression test for https://github.com/flutter/flutter/issues/83819
// Open the dropdown menu // Open the dropdown menu
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
...@@ -3521,7 +3535,7 @@ void main() { ...@@ -3521,7 +3535,7 @@ void main() {
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('borderRadius property works properly', (WidgetTester tester) async { testWidgetsWithLeakTracking('borderRadius property works properly', (WidgetTester tester) async {
const double radius = 20.0; const double radius = 20.0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3564,7 +3578,7 @@ void main() { ...@@ -3564,7 +3578,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/88574 // Regression test for https://github.com/flutter/flutter/issues/88574
testWidgets("specifying itemHeight affects popup menu items' height", (WidgetTester tester) async { testWidgetsWithLeakTracking("specifying itemHeight affects popup menu items' height", (WidgetTester tester) async {
const String value = 'One'; const String value = 'One';
const double itemHeight = 80; const double itemHeight = 80;
final List<DropdownMenuItem<String>> menuItems = <String>[ final List<DropdownMenuItem<String>> menuItems = <String>[
...@@ -3605,7 +3619,7 @@ void main() { ...@@ -3605,7 +3619,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/92438 // Regression test for https://github.com/flutter/flutter/issues/92438
testWidgets('Do not throw due to the double precision', (WidgetTester tester) async { testWidgetsWithLeakTracking('Do not throw due to the double precision', (WidgetTester tester) async {
const String value = 'One'; const String value = 'One';
const double itemHeight = 77.701; const double itemHeight = 77.701;
final List<DropdownMenuItem<String>> menuItems = <String>[ final List<DropdownMenuItem<String>> menuItems = <String>[
...@@ -3639,7 +3653,7 @@ void main() { ...@@ -3639,7 +3653,7 @@ void main() {
expect(tester.takeException(), null); expect(tester.takeException(), null);
}); });
testWidgets('BorderRadius property works properly for DropdownButtonFormField', (WidgetTester tester) async { testWidgetsWithLeakTracking('BorderRadius property works properly for DropdownButtonFormField', (WidgetTester tester) async {
const double radius = 20.0; const double radius = 20.0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3680,7 +3694,7 @@ void main() { ...@@ -3680,7 +3694,7 @@ void main() {
); );
}); });
testWidgets('DropdownButton hint alignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton hint alignment', (WidgetTester tester) async {
const String hintText = 'hint'; const String hintText = 'hint';
// AlignmentDirectional.centerStart (default) // AlignmentDirectional.centerStart (default)
...@@ -3785,7 +3799,7 @@ void main() { ...@@ -3785,7 +3799,7 @@ void main() {
expect(tester.getBottomRight(find.text(hintText,skipOffstage: false)).dy, 350.0); expect(tester.getBottomRight(find.text(hintText,skipOffstage: false)).dy, 350.0);
}); });
testWidgets('DropdownButton hint alignment with selectedItemBuilder', (WidgetTester tester) async { testWidgetsWithLeakTracking('DropdownButton hint alignment with selectedItemBuilder', (WidgetTester tester) async {
const String hintText = 'hint'; const String hintText = 'hint';
// AlignmentDirectional.centerStart (default) // AlignmentDirectional.centerStart (default)
...@@ -3904,7 +3918,7 @@ void main() { ...@@ -3904,7 +3918,7 @@ void main() {
expect(tester.getBottomRight(find.text(hintText,skipOffstage: false)).dy, 350.0); expect(tester.getBottomRight(find.text(hintText,skipOffstage: false)).dy, 350.0);
}); });
testWidgets('BorderRadius property clips dropdown button and dropdown menu', (WidgetTester tester) async { testWidgetsWithLeakTracking('BorderRadius property clips dropdown button and dropdown menu', (WidgetTester tester) async {
const double radius = 20.0; const double radius = 20.0;
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3943,7 +3957,7 @@ void main() { ...@@ -3943,7 +3957,7 @@ void main() {
expect(renderClip.borderRadius, BorderRadius.circular(radius)); expect(renderClip.borderRadius, BorderRadius.circular(radius));
}); });
testWidgets('Size of DropdownButton with padding', (WidgetTester tester) async { testWidgetsWithLeakTracking('Size of DropdownButton with padding', (WidgetTester tester) async {
const double padVertical = 5; const double padVertical = 5;
const double padHorizontal = 10; const double padHorizontal = 10;
final Key buttonKey = UniqueKey(); final Key buttonKey = UniqueKey();
......
...@@ -1782,6 +1782,7 @@ void main() { ...@@ -1782,6 +1782,7 @@ void main() {
count += 1; count += 1;
} }
final MaterialStatesController controller = MaterialStatesController(); final MaterialStatesController controller = MaterialStatesController();
addTearDown(controller.dispose);
controller.addListener(valueChanged); controller.addListener(valueChanged);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -1882,21 +1883,23 @@ void main() { ...@@ -1882,21 +1883,23 @@ void main() {
await gesture.removePointer(); await gesture.removePointer();
} }
testWidgets('ElevatedButton statesController', (WidgetTester tester) async { testWidgetsWithLeakTracking('ElevatedButton statesController', (WidgetTester tester) async {
testStatesController(null, tester); testStatesController(null, tester);
}); });
testWidgets('ElevatedButton.icon statesController', (WidgetTester tester) async { testWidgetsWithLeakTracking('ElevatedButton.icon statesController', (WidgetTester tester) async {
testStatesController(const Icon(Icons.add), tester); testStatesController(const Icon(Icons.add), tester);
}); });
testWidgets('Disabled ElevatedButton statesController', (WidgetTester tester) async { testWidgetsWithLeakTracking('Disabled ElevatedButton statesController', (WidgetTester tester) async {
int count = 0; int count = 0;
void valueChanged() { void valueChanged() {
count += 1; count += 1;
} }
final MaterialStatesController controller = MaterialStatesController(); final MaterialStatesController controller = MaterialStatesController();
addTearDown(controller.dispose);
controller.addListener(valueChanged); controller.addListener(valueChanged);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Center( home: Center(
......
...@@ -589,7 +589,7 @@ void main() { ...@@ -589,7 +589,7 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/71435 // Regression test for https://github.com/flutter/flutter/issues/71435
testWidgets( testWidgetsWithLeakTracking(
'Scaffold.bottomSheet should be updated without creating a new RO' 'Scaffold.bottomSheet should be updated without creating a new RO'
' when the new widget has the same key and type.', ' when the new widget has the same key and type.',
(WidgetTester tester) async { (WidgetTester tester) async {
......
...@@ -1134,7 +1134,7 @@ void main() { ...@@ -1134,7 +1134,7 @@ void main() {
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 14.0 + 40.0); // margin + bottom padding expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 14.0 + 40.0); // margin + bottom padding
}); });
testWidgets( testWidgetsWithLeakTracking(
'Material2 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.fixed', 'Material2 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.fixed',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -1191,7 +1191,7 @@ void main() { ...@@ -1191,7 +1191,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Material3 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.fixed', 'Material3 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.fixed',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -1405,7 +1405,7 @@ void main() { ...@@ -1405,7 +1405,7 @@ void main() {
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 24.0); // margin (with no bottom padding) expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 24.0); // margin (with no bottom padding)
}); });
testWidgets( testWidgetsWithLeakTracking(
'Material2 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.floating', 'Material2 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.floating',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -1465,7 +1465,7 @@ void main() { ...@@ -1465,7 +1465,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'Material3 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.floating', 'Material3 - Custom padding between SnackBar and its contents when set to SnackBarBehavior.floating',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -1873,7 +1873,7 @@ void main() { ...@@ -1873,7 +1873,7 @@ void main() {
behavior: behavior, behavior: behavior,
); );
testWidgets( testWidgetsWithLeakTracking(
'$behavior should align SnackBar with the bottom of Scaffold ' '$behavior should align SnackBar with the bottom of Scaffold '
'when Scaffold has no other elements', 'when Scaffold has no other elements',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1902,7 +1902,7 @@ void main() { ...@@ -1902,7 +1902,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'$behavior should align SnackBar with the top of BottomNavigationBar ' '$behavior should align SnackBar with the top of BottomNavigationBar '
'when Scaffold has no FloatingActionButton', 'when Scaffold has no FloatingActionButton',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -1934,7 +1934,7 @@ void main() { ...@@ -1934,7 +1934,7 @@ void main() {
); );
} }
testWidgets( testWidgetsWithLeakTracking(
'Padding of ${SnackBarBehavior.fixed} is not consumed by viewInsets', 'Padding of ${SnackBarBehavior.fixed} is not consumed by viewInsets',
(WidgetTester tester) async { (WidgetTester tester) async {
final Widget child = MaterialApp( final Widget child = MaterialApp(
...@@ -1997,7 +1997,7 @@ void main() { ...@@ -1997,7 +1997,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.fixed} should align SnackBar with the bottom of Scaffold ' '${SnackBarBehavior.fixed} should align SnackBar with the bottom of Scaffold '
'when Scaffold has a FloatingActionButton', 'when Scaffold has a FloatingActionButton',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -2032,7 +2032,7 @@ void main() { ...@@ -2032,7 +2032,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton when Scaffold has a FloatingActionButton', '${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton when Scaffold has a FloatingActionButton',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -2072,7 +2072,7 @@ void main() { ...@@ -2072,7 +2072,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.floating} should not align SnackBar with the top of FloatingActionButton ' '${SnackBarBehavior.floating} should not align SnackBar with the top of FloatingActionButton '
'when Scaffold has a FloatingActionButton and floatingActionButtonLocation is set to a top position', 'when Scaffold has a FloatingActionButton and floatingActionButtonLocation is set to a top position',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -2125,7 +2125,7 @@ void main() { ...@@ -2125,7 +2125,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton ' '${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton '
'when Scaffold has a FloatingActionButton and floatingActionButtonLocation is not set to a top position', 'when Scaffold has a FloatingActionButton and floatingActionButtonLocation is not set to a top position',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -2193,7 +2193,7 @@ void main() { ...@@ -2193,7 +2193,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.fixed} should align SnackBar with the top of BottomNavigationBar ' '${SnackBarBehavior.fixed} should align SnackBar with the top of BottomNavigationBar '
'when Scaffold has a BottomNavigationBar and FloatingActionButton', 'when Scaffold has a BottomNavigationBar and FloatingActionButton',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -2230,7 +2230,7 @@ void main() { ...@@ -2230,7 +2230,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton ' '${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton '
'when Scaffold has BottomNavigationBar and FloatingActionButton', 'when Scaffold has BottomNavigationBar and FloatingActionButton',
(WidgetTester tester) async { (WidgetTester tester) async {
...@@ -2400,7 +2400,7 @@ void main() { ...@@ -2400,7 +2400,7 @@ void main() {
expect(errorMessages.contains(offScreenMessage), isTrue); expect(errorMessages.contains(offScreenMessage), isTrue);
}); });
testWidgets( testWidgetsWithLeakTracking(
'SnackBar has correct end padding when it contains an action with fixed behavior', 'SnackBar has correct end padding when it contains an action with fixed behavior',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2437,7 +2437,7 @@ void main() { ...@@ -2437,7 +2437,7 @@ void main() {
}, },
); );
testWidgets( testWidgetsWithLeakTracking(
'SnackBar has correct end padding when it contains an action with floating behavior', 'SnackBar has correct end padding when it contains an action with floating behavior',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
...@@ -3337,7 +3337,7 @@ void main() { ...@@ -3337,7 +3337,7 @@ void main() {
); );
}); });
testWidgets( testWidgetsWithLeakTracking(
'ScaffoldMessenger will alert for snackbars that cannot be presented', (WidgetTester tester) async { 'ScaffoldMessenger will alert for snackbars that cannot be presented', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/103004 // Regression test for https://github.com/flutter/flutter/issues/103004
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
...@@ -3723,7 +3723,7 @@ testWidgetsWithLeakTracking('SnackBarAction backgroundColor works as a Color', ( ...@@ -3723,7 +3723,7 @@ testWidgetsWithLeakTracking('SnackBarAction backgroundColor works as a Color', (
expect(completer.isCompleted, true); expect(completer.isCompleted, true);
}); });
testWidgets("Can't tap on button behind snack bar defined by margin and HitTestBehavior.opaque", (WidgetTester tester) async { testWidgetsWithLeakTracking("Can't tap on button behind snack bar defined by margin and HitTestBehavior.opaque", (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/78537. // Regression test for https://github.com/flutter/flutter/issues/78537.
tester.view.physicalSize = const Size.square(200); tester.view.physicalSize = const Size.square(200);
tester.view.devicePixelRatio = 1; tester.view.devicePixelRatio = 1;
......
...@@ -11,6 +11,7 @@ import 'package:flutter/gestures.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter/gestures.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';
const String _tab1Text = 'tab 1'; const String _tab1Text = 'tab 1';
const String _tab2Text = 'tab 2'; const String _tab2Text = 'tab 2';
...@@ -36,6 +37,12 @@ Widget buildTabBar({ ...@@ -36,6 +37,12 @@ Widget buildTabBar({
bool isScrollable = false, bool isScrollable = false,
bool useMaterial3 = false, bool useMaterial3 = false,
}) { }) {
final TabController controller = TabController(
length: tabs.length,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
if (secondaryTabBar) { if (secondaryTabBar) {
return MaterialApp( return MaterialApp(
theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: useMaterial3), theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: useMaterial3),
...@@ -45,7 +52,7 @@ Widget buildTabBar({ ...@@ -45,7 +52,7 @@ Widget buildTabBar({
child: TabBar.secondary( child: TabBar.secondary(
tabs: tabs, tabs: tabs,
isScrollable: isScrollable, isScrollable: isScrollable,
controller: TabController(length: tabs.length, vsync: const TestVSync()), controller: controller,
), ),
), ),
), ),
...@@ -59,7 +66,7 @@ Widget buildTabBar({ ...@@ -59,7 +66,7 @@ Widget buildTabBar({
child: TabBar( child: TabBar(
tabs: tabs, tabs: tabs,
isScrollable: isScrollable, isScrollable: isScrollable,
controller: TabController(length: tabs.length, vsync: const TestVSync()), controller: controller,
), ),
), ),
), ),
...@@ -103,7 +110,7 @@ void main() { ...@@ -103,7 +110,7 @@ void main() {
expect(identical(TabBarTheme.lerp(theme, theme, 0.5), theme), true); expect(identical(TabBarTheme.lerp(theme, theme, 0.5), theme), true);
}); });
testWidgets('Tab bar defaults (primary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar defaults (primary)', (WidgetTester tester) async {
// Test default label color and label styles. // Test default label color and label styles.
await tester.pumpWidget(buildTabBar(useMaterial3: true)); await tester.pumpWidget(buildTabBar(useMaterial3: true));
...@@ -154,7 +161,7 @@ void main() { ...@@ -154,7 +161,7 @@ void main() {
); );
}); });
testWidgets('Tab bar defaults (secondary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar defaults (secondary)', (WidgetTester tester) async {
// Test default label color and label styles. // Test default label color and label styles.
await tester.pumpWidget(buildTabBar(secondaryTabBar: true, useMaterial3: true)); await tester.pumpWidget(buildTabBar(secondaryTabBar: true, useMaterial3: true));
...@@ -210,7 +217,7 @@ void main() { ...@@ -210,7 +217,7 @@ void main() {
); );
}); });
testWidgets('Tab bar theme overrides label color (selected)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides label color (selected)', (WidgetTester tester) async {
const Color labelColor = Colors.black; const Color labelColor = Colors.black;
const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor); const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor);
...@@ -222,7 +229,7 @@ void main() { ...@@ -222,7 +229,7 @@ void main() {
expect(tabIcon.text.style!.color, equals(labelColor)); expect(tabIcon.text.style!.color, equals(labelColor));
}); });
testWidgets('Tab bar theme overrides label padding', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides label padding', (WidgetTester tester) async {
const double topPadding = 10.0; const double topPadding = 10.0;
const double bottomPadding = 7.0; const double bottomPadding = 7.0;
const double rightPadding = 13.0; const double rightPadding = 13.0;
...@@ -259,7 +266,7 @@ void main() { ...@@ -259,7 +266,7 @@ void main() {
expect(tabOneRect.right, equals(tabTwoRect.left - leftPadding - rightPadding)); expect(tabOneRect.right, equals(tabTwoRect.left - leftPadding - rightPadding));
}); });
testWidgets('Tab bar theme overrides label styles', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides label styles', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(fontFamily: 'foobar'); const TextStyle labelStyle = TextStyle(fontFamily: 'foobar');
const TextStyle unselectedLabelStyle = TextStyle(fontFamily: 'baz'); const TextStyle unselectedLabelStyle = TextStyle(fontFamily: 'baz');
const TabBarTheme tabBarTheme = TabBarTheme( const TabBarTheme tabBarTheme = TabBarTheme(
...@@ -275,7 +282,7 @@ void main() { ...@@ -275,7 +282,7 @@ void main() {
expect(unselectedLabel.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily)); expect(unselectedLabel.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily));
}); });
testWidgets('Tab bar theme with just label style specified', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme with just label style specified', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/28784 // Regression test for https://github.com/flutter/flutter/issues/28784
const TextStyle labelStyle = TextStyle(fontFamily: 'foobar'); const TextStyle labelStyle = TextStyle(fontFamily: 'foobar');
const TabBarTheme tabBarTheme = TabBarTheme( const TabBarTheme tabBarTheme = TabBarTheme(
...@@ -292,7 +299,7 @@ void main() { ...@@ -292,7 +299,7 @@ void main() {
expect(unselectedLabel.text.style!.color, equals(Colors.white.withAlpha(0xB2))); expect(unselectedLabel.text.style!.color, equals(Colors.white.withAlpha(0xB2)));
}); });
testWidgets('Tab bar label styles override theme label styles', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar label styles override theme label styles', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(fontFamily: '1'); const TextStyle labelStyle = TextStyle(fontFamily: '1');
const TextStyle unselectedLabelStyle = TextStyle(fontFamily: '2'); const TextStyle unselectedLabelStyle = TextStyle(fontFamily: '2');
const TextStyle themeLabelStyle = TextStyle(fontFamily: '3'); const TextStyle themeLabelStyle = TextStyle(fontFamily: '3');
...@@ -301,6 +308,11 @@ void main() { ...@@ -301,6 +308,11 @@ void main() {
labelStyle: themeLabelStyle, labelStyle: themeLabelStyle,
unselectedLabelStyle: themeUnselectedLabelStyle, unselectedLabelStyle: themeUnselectedLabelStyle,
); );
final TabController controller = TabController(
length: _tabs.length,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -308,7 +320,7 @@ void main() { ...@@ -308,7 +320,7 @@ void main() {
home: Scaffold( home: Scaffold(
body: TabBar( body: TabBar(
tabs: _tabs, tabs: _tabs,
controller: TabController(length: _tabs.length, vsync: const TestVSync()), controller: controller,
labelStyle: labelStyle, labelStyle: labelStyle,
unselectedLabelStyle: unselectedLabelStyle, unselectedLabelStyle: unselectedLabelStyle,
), ),
...@@ -322,7 +334,7 @@ void main() { ...@@ -322,7 +334,7 @@ void main() {
expect(unselectedLabel.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily)); expect(unselectedLabel.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily));
}); });
testWidgets('Material2 - Tab bar label padding overrides theme label padding', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - Tab bar label padding overrides theme label padding', (WidgetTester tester) async {
const double verticalPadding = 10.0; const double verticalPadding = 10.0;
const double horizontalPadding = 10.0; const double horizontalPadding = 10.0;
const EdgeInsetsGeometry labelPadding = EdgeInsets.symmetric( const EdgeInsetsGeometry labelPadding = EdgeInsets.symmetric(
...@@ -341,6 +353,12 @@ void main() { ...@@ -341,6 +353,12 @@ void main() {
const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: themeLabelPadding); const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: themeLabelPadding);
final TabController controller = TabController(
length: _sizedTabs.length,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: false), theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: false),
...@@ -350,7 +368,7 @@ void main() { ...@@ -350,7 +368,7 @@ void main() {
child: TabBar( child: TabBar(
tabs: _sizedTabs, tabs: _sizedTabs,
isScrollable: true, isScrollable: true,
controller: TabController(length: _sizedTabs.length, vsync: const TestVSync()), controller: controller,
labelPadding: labelPadding, labelPadding: labelPadding,
), ),
), ),
...@@ -376,7 +394,7 @@ void main() { ...@@ -376,7 +394,7 @@ void main() {
expect(tabOneRect.right, equals(tabTwoRect.left - (2 * horizontalPadding))); expect(tabOneRect.right, equals(tabTwoRect.left - (2 * horizontalPadding)));
}); });
testWidgets('Material3 - Tab bar label padding overrides theme label padding', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - Tab bar label padding overrides theme label padding', (WidgetTester tester) async {
const double tabStartOffset = 52.0; const double tabStartOffset = 52.0;
const double verticalPadding = 10.0; const double verticalPadding = 10.0;
const double horizontalPadding = 10.0; const double horizontalPadding = 10.0;
...@@ -396,6 +414,12 @@ void main() { ...@@ -396,6 +414,12 @@ void main() {
const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: themeLabelPadding); const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: themeLabelPadding);
final TabController controller = TabController(
length: _sizedTabs.length,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: true), theme: ThemeData(tabBarTheme: tabBarTheme, useMaterial3: true),
...@@ -405,7 +429,7 @@ void main() { ...@@ -405,7 +429,7 @@ void main() {
child: TabBar( child: TabBar(
tabs: _sizedTabs, tabs: _sizedTabs,
isScrollable: true, isScrollable: true,
controller: TabController(length: _sizedTabs.length, vsync: const TestVSync()), controller: controller,
labelPadding: labelPadding, labelPadding: labelPadding,
), ),
), ),
...@@ -431,7 +455,7 @@ void main() { ...@@ -431,7 +455,7 @@ void main() {
expect(tabOneRect.right, equals(tabTwoRect.left - (2 * horizontalPadding))); expect(tabOneRect.right, equals(tabTwoRect.left - (2 * horizontalPadding)));
}); });
testWidgets('Tab bar theme overrides label color (unselected)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides label color (unselected)', (WidgetTester tester) async {
const Color unselectedLabelColor = Colors.black; const Color unselectedLabelColor = Colors.black;
const TabBarTheme tabBarTheme = TabBarTheme(unselectedLabelColor: unselectedLabelColor); const TabBarTheme tabBarTheme = TabBarTheme(unselectedLabelColor: unselectedLabelColor);
...@@ -443,7 +467,7 @@ void main() { ...@@ -443,7 +467,7 @@ void main() {
expect(iconRenderObject.text.style!.color, equals(unselectedLabelColor)); expect(iconRenderObject.text.style!.color, equals(unselectedLabelColor));
}); });
testWidgets('Tab bar default tab indicator size (primary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar default tab indicator size (primary)', (WidgetTester tester) async {
await tester.pumpWidget(buildTabBar(useMaterial3: true, isScrollable: true)); await tester.pumpWidget(buildTabBar(useMaterial3: true, isScrollable: true));
await expectLater( await expectLater(
...@@ -452,7 +476,7 @@ void main() { ...@@ -452,7 +476,7 @@ void main() {
); );
}); });
testWidgets('Tab bar default tab indicator size (secondary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar default tab indicator size (secondary)', (WidgetTester tester) async {
await tester.pumpWidget(buildTabBar(useMaterial3: true, isScrollable: true)); await tester.pumpWidget(buildTabBar(useMaterial3: true, isScrollable: true));
await expectLater( await expectLater(
...@@ -461,7 +485,7 @@ void main() { ...@@ -461,7 +485,7 @@ void main() {
); );
}); });
testWidgets('Tab bar theme overrides tab indicator size (tab)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides tab indicator size (tab)', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab); const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab);
await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme)); await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme));
...@@ -472,7 +496,7 @@ void main() { ...@@ -472,7 +496,7 @@ void main() {
); );
}); });
testWidgets('Tab bar theme overrides tab indicator size (label)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides tab indicator size (label)', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.label); const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.label);
await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme)); await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme));
...@@ -483,7 +507,7 @@ void main() { ...@@ -483,7 +507,7 @@ void main() {
); );
}); });
testWidgets('Tab bar theme overrides tab mouse cursor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme overrides tab mouse cursor', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(mouseCursor: MaterialStateMouseCursor.textable); const TabBarTheme tabBarTheme = TabBarTheme(mouseCursor: MaterialStateMouseCursor.textable);
await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme)); await tester.pumpWidget(buildTabBar(tabBarTheme: tabBarTheme));
...@@ -498,7 +522,7 @@ void main() { ...@@ -498,7 +522,7 @@ void main() {
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text); expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
}); });
testWidgets('Tab bar theme - custom tab indicator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme - custom tab indicator', (WidgetTester tester) async {
final TabBarTheme tabBarTheme = TabBarTheme( final TabBarTheme tabBarTheme = TabBarTheme(
indicator: BoxDecoration( indicator: BoxDecoration(
border: Border.all(), border: Border.all(),
...@@ -513,7 +537,7 @@ void main() { ...@@ -513,7 +537,7 @@ void main() {
); );
}); });
testWidgets('Tab bar theme - beveled rect indicator', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar theme - beveled rect indicator', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme( const TabBarTheme tabBarTheme = TabBarTheme(
indicator: ShapeDecoration( indicator: ShapeDecoration(
shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))), shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))),
...@@ -529,7 +553,7 @@ void main() { ...@@ -529,7 +553,7 @@ void main() {
); );
}); });
testWidgets('TabAlignment.fill from TabBarTheme only supports non-scrollable tab bar', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabAlignment.fill from TabBarTheme only supports non-scrollable tab bar', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(tabAlignment: TabAlignment.fill); const TabBarTheme tabBarTheme = TabBarTheme(tabAlignment: TabAlignment.fill);
// Test TabAlignment.fill from TabBarTheme with non-scrollable tab bar. // Test TabAlignment.fill from TabBarTheme with non-scrollable tab bar.
...@@ -543,7 +567,7 @@ void main() { ...@@ -543,7 +567,7 @@ void main() {
expect(tester.takeException(), isAssertionError); expect(tester.takeException(), isAssertionError);
}); });
testWidgets( testWidgetsWithLeakTracking(
'TabAlignment.start & TabAlignment.startOffset from TabBarTheme only supports scrollable tab bar', 'TabAlignment.start & TabAlignment.startOffset from TabBarTheme only supports scrollable tab bar',
(WidgetTester tester) async { (WidgetTester tester) async {
TabBarTheme tabBarTheme = const TabBarTheme(tabAlignment: TabAlignment.start); TabBarTheme tabBarTheme = const TabBarTheme(tabAlignment: TabAlignment.start);
...@@ -571,7 +595,7 @@ void main() { ...@@ -571,7 +595,7 @@ void main() {
expect(tester.takeException(), isAssertionError); expect(tester.takeException(), isAssertionError);
}); });
testWidgets('TabBarTheme.indicatorSize provides correct tab indicator (primary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBarTheme.indicatorSize provides correct tab indicator (primary)', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.tab), tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.tab),
useMaterial3: true, useMaterial3: true,
...@@ -584,6 +608,7 @@ void main() { ...@@ -584,6 +608,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -626,7 +651,7 @@ void main() { ...@@ -626,7 +651,7 @@ void main() {
); );
}); });
testWidgets('TabBarTheme.indicatorSize provides correct tab indicator (secondary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBarTheme.indicatorSize provides correct tab indicator (secondary)', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.label), tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.label),
useMaterial3: true, useMaterial3: true,
...@@ -639,6 +664,7 @@ void main() { ...@@ -639,6 +664,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -679,10 +705,16 @@ void main() { ...@@ -679,10 +705,16 @@ void main() {
); );
}); });
testWidgets('TabBar divider can use TabBarTheme.dividerColor & TabBarTheme.dividerHeight', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBar divider can use TabBarTheme.dividerColor & TabBarTheme.dividerHeight', (WidgetTester tester) async {
const Color dividerColor = Color(0xff00ff00); const Color dividerColor = Color(0xff00ff00);
const double dividerHeight = 10.0; const double dividerHeight = 10.0;
final TabController controller = TabController(
length: 3,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData( theme: ThemeData(
...@@ -695,7 +727,7 @@ void main() { ...@@ -695,7 +727,7 @@ void main() {
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
controller: TabController(length: 3, vsync: const TestVSync()), controller: controller,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'), Tab(text: 'Tab 2'),
...@@ -712,10 +744,16 @@ void main() { ...@@ -712,10 +744,16 @@ void main() {
expect(tabBarBox, paints..line(color: dividerColor, strokeWidth: dividerHeight)); expect(tabBarBox, paints..line(color: dividerColor, strokeWidth: dividerHeight));
}); });
testWidgets('dividerColor & dividerHeight overrides TabBarTheme.dividerColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('dividerColor & dividerHeight overrides TabBarTheme.dividerColor', (WidgetTester tester) async {
const Color dividerColor = Color(0xff0000ff); const Color dividerColor = Color(0xff0000ff);
const double dividerHeight = 8.0; const double dividerHeight = 8.0;
final TabController controller = TabController(
length: 3,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData( theme: ThemeData(
...@@ -730,7 +768,7 @@ void main() { ...@@ -730,7 +768,7 @@ void main() {
bottom: TabBar( bottom: TabBar(
dividerColor: dividerColor, dividerColor: dividerColor,
dividerHeight: dividerHeight, dividerHeight: dividerHeight,
controller: TabController(length: 3, vsync: const TestVSync()), controller: controller,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'), Tab(text: 'Tab 2'),
...@@ -747,7 +785,13 @@ void main() { ...@@ -747,7 +785,13 @@ void main() {
expect(tabBarBox, paints..line(color: dividerColor, strokeWidth: dividerHeight)); expect(tabBarBox, paints..line(color: dividerColor, strokeWidth: dividerHeight));
}); });
testWidgets('TabBar respects TabBarTheme.tabAlignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBar respects TabBarTheme.tabAlignment', (WidgetTester tester) async {
final TabController controller1 = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
// Test non-scrollable tab bar. // Test non-scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -758,7 +802,7 @@ void main() { ...@@ -758,7 +802,7 @@ void main() {
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller1,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -778,6 +822,12 @@ void main() { ...@@ -778,6 +822,12 @@ void main() {
double tabTwoRight = (availableWidth / 2) + tabTwoRect.width + kTabLabelPadding.right; double tabTwoRight = (availableWidth / 2) + tabTwoRect.width + kTabLabelPadding.right;
expect(tabTwoRect.right, equals(tabTwoRight)); expect(tabTwoRect.right, equals(tabTwoRight));
final TabController controller2 = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller2.dispose);
// Test scrollable tab bar. // Test scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -789,7 +839,7 @@ void main() { ...@@ -789,7 +839,7 @@ void main() {
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
isScrollable: true, isScrollable: true,
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller2,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -810,7 +860,13 @@ void main() { ...@@ -810,7 +860,13 @@ void main() {
expect(tabTwoRect.right, equals(tabTwoRight)); expect(tabTwoRect.right, equals(tabTwoRight));
}); });
testWidgets('TabBar.tabAlignment overrides TabBarTheme.tabAlignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBar.tabAlignment overrides TabBarTheme.tabAlignment', (WidgetTester tester) async {
final TabController controller1 = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller1.dispose);
/// Test non-scrollable tab bar. /// Test non-scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -822,7 +878,7 @@ void main() { ...@@ -822,7 +878,7 @@ void main() {
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
tabAlignment: TabAlignment.center, tabAlignment: TabAlignment.center,
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller1,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -842,6 +898,12 @@ void main() { ...@@ -842,6 +898,12 @@ void main() {
double tabTwoRight = (availableWidth / 2) + tabTwoRect.width + kTabLabelPadding.right; double tabTwoRight = (availableWidth / 2) + tabTwoRect.width + kTabLabelPadding.right;
expect(tabTwoRect.right, equals(tabTwoRight)); expect(tabTwoRect.right, equals(tabTwoRight));
final TabController controller2 = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller2.dispose);
/// Test scrollable tab bar. /// Test scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -854,7 +916,7 @@ void main() { ...@@ -854,7 +916,7 @@ void main() {
bottom: TabBar( bottom: TabBar(
isScrollable: true, isScrollable: true,
tabAlignment: TabAlignment.start, tabAlignment: TabAlignment.start,
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller2,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -875,7 +937,7 @@ void main() { ...@@ -875,7 +937,7 @@ void main() {
expect(tabTwoRect.right, equals(tabTwoRight)); expect(tabTwoRect.right, equals(tabTwoRight));
}); });
testWidgets( testWidgetsWithLeakTracking(
'TabBar labels use colors from TabBarTheme.labelStyle & TabBarTheme.unselectedLabelStyle', 'TabBar labels use colors from TabBarTheme.labelStyle & TabBarTheme.unselectedLabelStyle',
(WidgetTester tester) async { (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle( const TextStyle labelStyle = TextStyle(
...@@ -911,7 +973,7 @@ void main() { ...@@ -911,7 +973,7 @@ void main() {
expect(unselectedTextStyle.fontStyle, unselectedLabelStyle.fontStyle); expect(unselectedTextStyle.fontStyle, unselectedLabelStyle.fontStyle);
}); });
testWidgets( testWidgetsWithLeakTracking(
"TabBarTheme's labelColor & unselectedLabelColor override labelStyle & unselectedLabelStyle colors", "TabBarTheme's labelColor & unselectedLabelColor override labelStyle & unselectedLabelStyle colors",
(WidgetTester tester) async { (WidgetTester tester) async {
const Color labelColor = Color(0xfff00000); const Color labelColor = Color(0xfff00000);
...@@ -975,7 +1037,7 @@ void main() { ...@@ -975,7 +1037,7 @@ void main() {
expect(unselectedTextStyle.fontStyle, unselectedLabelStyle.fontStyle); expect(unselectedTextStyle.fontStyle, unselectedLabelStyle.fontStyle);
}); });
testWidgets( testWidgetsWithLeakTracking(
"TabBarTheme's labelColor & unselectedLabelColor override TabBar.labelStyle & TabBar.unselectedLabelStyle colors", "TabBarTheme's labelColor & unselectedLabelColor override TabBar.labelStyle & TabBar.unselectedLabelStyle colors",
(WidgetTester tester) async { (WidgetTester tester) async {
const Color labelColor = Color(0xfff00000); const Color labelColor = Color(0xfff00000);
...@@ -1054,7 +1116,7 @@ void main() { ...@@ -1054,7 +1116,7 @@ void main() {
// support is deprecated and the APIs are removed, these tests // support is deprecated and the APIs are removed, these tests
// can be deleted. // can be deleted.
testWidgets('Tab bar defaults (primary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar defaults (primary)', (WidgetTester tester) async {
// Test default label color and label styles. // Test default label color and label styles.
await tester.pumpWidget(buildTabBar()); await tester.pumpWidget(buildTabBar());
...@@ -1094,7 +1156,7 @@ void main() { ...@@ -1094,7 +1156,7 @@ void main() {
expect(tabBarBox, paints..line(color: theme.indicatorColor)); expect(tabBarBox, paints..line(color: theme.indicatorColor));
}); });
testWidgets('Tab bar defaults (secondary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar defaults (secondary)', (WidgetTester tester) async {
// Test default label color and label styles. // Test default label color and label styles.
await tester.pumpWidget(buildTabBar(secondaryTabBar: true)); await tester.pumpWidget(buildTabBar(secondaryTabBar: true));
...@@ -1134,7 +1196,7 @@ void main() { ...@@ -1134,7 +1196,7 @@ void main() {
expect(tabBarBox, paints..line(color: theme.indicatorColor)); expect(tabBarBox, paints..line(color: theme.indicatorColor));
}); });
testWidgets('Tab bar default tab indicator size', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tab bar default tab indicator size', (WidgetTester tester) async {
await tester.pumpWidget(buildTabBar()); await tester.pumpWidget(buildTabBar());
await expectLater( await expectLater(
...@@ -1143,7 +1205,7 @@ void main() { ...@@ -1143,7 +1205,7 @@ void main() {
); );
}); });
testWidgets('TabBarTheme.indicatorSize provides correct tab indicator (primary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBarTheme.indicatorSize provides correct tab indicator (primary)', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.tab), tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.tab),
useMaterial3: false, useMaterial3: false,
...@@ -1156,6 +1218,7 @@ void main() { ...@@ -1156,6 +1218,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1193,7 +1256,7 @@ void main() { ...@@ -1193,7 +1256,7 @@ void main() {
); );
}); });
testWidgets('TabBarTheme.indicatorSize provides correct tab indicator (secondary)', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBarTheme.indicatorSize provides correct tab indicator (secondary)', (WidgetTester tester) async {
final ThemeData theme = ThemeData( final ThemeData theme = ThemeData(
tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.label), tabBarTheme: const TabBarTheme(indicatorSize: TabBarIndicatorSize.label),
useMaterial3: false, useMaterial3: false,
...@@ -1206,6 +1269,7 @@ void main() { ...@@ -1206,6 +1269,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1241,7 +1305,13 @@ void main() { ...@@ -1241,7 +1305,13 @@ void main() {
); );
}); });
testWidgets('TabBar respects TabBarTheme.tabAlignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBar respects TabBarTheme.tabAlignment', (WidgetTester tester) async {
final TabController controller = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
// Test non-scrollable tab bar. // Test non-scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1252,7 +1322,7 @@ void main() { ...@@ -1252,7 +1322,7 @@ void main() {
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -1272,7 +1342,13 @@ void main() { ...@@ -1272,7 +1342,13 @@ void main() {
expect(tabTwoRect.right, equals(tabTwoRight)); expect(tabTwoRect.right, equals(tabTwoRight));
}); });
testWidgets('TabBar.tabAlignment overrides TabBarTheme.tabAlignment', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBar.tabAlignment overrides TabBarTheme.tabAlignment', (WidgetTester tester) async {
final TabController controller = TabController(
length: 2,
vsync: const TestVSync(),
);
addTearDown(controller.dispose);
// Test non-scrollable tab bar. // Test non-scrollable tab bar.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1284,7 +1360,7 @@ void main() { ...@@ -1284,7 +1360,7 @@ void main() {
appBar: AppBar( appBar: AppBar(
bottom: TabBar( bottom: TabBar(
tabAlignment: TabAlignment.center, tabAlignment: TabAlignment.center,
controller: TabController(length: 2, vsync: const TestVSync()), controller: controller,
tabs: const <Widget>[ tabs: const <Widget>[
Tab(text: 'Tab 1'), Tab(text: 'Tab 1'),
Tab(text: 'Tab 3'), Tab(text: 'Tab 3'),
...@@ -1305,7 +1381,7 @@ void main() { ...@@ -1305,7 +1381,7 @@ void main() {
}); });
}); });
testWidgets('Material3 - TabBar indicator respects TabBarTheme.indicatorColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - TabBar indicator respects TabBarTheme.indicatorColor', (WidgetTester tester) async {
final List<Widget> tabs = List<Widget>.generate(4, (int index) { final List<Widget> tabs = List<Widget>.generate(4, (int index) {
return Tab(text: 'Tab $index'); return Tab(text: 'Tab $index');
}); });
...@@ -1314,6 +1390,7 @@ void main() { ...@@ -1314,6 +1390,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
const Color tabBarThemeIndicatorColor = Color(0xffff0000); const Color tabBarThemeIndicatorColor = Color(0xffff0000);
...@@ -1347,7 +1424,7 @@ void main() { ...@@ -1347,7 +1424,7 @@ void main() {
expect(tabBarBox,paints..rrect(color: tabBarThemeIndicatorColor)); expect(tabBarBox,paints..rrect(color: tabBarThemeIndicatorColor));
}); });
testWidgets('Material2 - TabBar indicator respects TabBarTheme.indicatorColor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - TabBar indicator respects TabBarTheme.indicatorColor', (WidgetTester tester) async {
final List<Widget> tabs = List<Widget>.generate(4, (int index) { final List<Widget> tabs = List<Widget>.generate(4, (int index) {
return Tab(text: 'Tab $index'); return Tab(text: 'Tab $index');
}); });
...@@ -1356,6 +1433,7 @@ void main() { ...@@ -1356,6 +1433,7 @@ void main() {
vsync: const TestVSync(), vsync: const TestVSync(),
length: tabs.length, length: tabs.length,
); );
addTearDown(controller.dispose);
const Color themeIndicatorColor = Color(0xffff0000); const Color themeIndicatorColor = Color(0xffff0000);
const Color tabBarThemeIndicatorColor = Color(0xffffff00); const Color tabBarThemeIndicatorColor = Color(0xffffff00);
...@@ -1391,7 +1469,7 @@ void main() { ...@@ -1391,7 +1469,7 @@ void main() {
expect(tabBarBox,paints..line(color: tabBarThemeIndicatorColor)); expect(tabBarBox,paints..line(color: tabBarThemeIndicatorColor));
}); });
testWidgets('TabBarTheme.labelColor resolves material states', (WidgetTester tester) async { testWidgetsWithLeakTracking('TabBarTheme.labelColor resolves material states', (WidgetTester tester) async {
const Color selectedColor = Color(0xff00ff00); const Color selectedColor = Color(0xff00ff00);
const Color unselectedColor = Color(0xffff0000); const Color unselectedColor = Color(0xffff0000);
final MaterialStateColor labelColor = MaterialStateColor.resolveWith((Set<MaterialState> states) { final MaterialStateColor labelColor = MaterialStateColor.resolveWith((Set<MaterialState> states) {
...@@ -1417,7 +1495,7 @@ void main() { ...@@ -1417,7 +1495,7 @@ void main() {
expect(unselectedTextStyle.color, unselectedColor); expect(unselectedTextStyle.color, unselectedColor);
}); });
testWidgets('TabBarTheme.labelColor & TabBarTheme.unselectedLabelColor override material state TabBarTheme.labelColor', testWidgetsWithLeakTracking('TabBarTheme.labelColor & TabBarTheme.unselectedLabelColor override material state TabBarTheme.labelColor',
(WidgetTester tester) async { (WidgetTester tester) async {
const Color selectedStateColor = Color(0xff00ff00); const Color selectedStateColor = Color(0xff00ff00);
const Color unselectedStateColor = Color(0xffff0000); const Color unselectedStateColor = Color(0xffff0000);
......
...@@ -787,7 +787,7 @@ void main() { ...@@ -787,7 +787,7 @@ void main() {
expect(state.widget.cursorColor, cursorColor); expect(state.widget.cursorColor, cursorColor);
}); });
testWidgets('Use error cursor color when an InputDecoration with an errorText or error widget is provided', (WidgetTester tester) async { testWidgetsWithLeakTracking('Use error cursor color when an InputDecoration with an errorText or error widget is provided', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
home: Material( home: Material(
...@@ -2484,7 +2484,7 @@ void main() { ...@@ -2484,7 +2484,7 @@ void main() {
expect(controller.selection.extentOffset, testValue.indexOf('g')); expect(controller.selection.extentOffset, testValue.indexOf('g'));
}); });
testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS)', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can move cursor when dragging, when tap is on collapsed selection (iOS)', (WidgetTester tester) async {
final TextEditingController controller = _textEditingController(); final TextEditingController controller = _textEditingController();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2527,6 +2527,11 @@ void main() { ...@@ -2527,6 +2527,11 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, testValue.indexOf('i')); expect(controller.selection.baseOffset, testValue.indexOf('i'));
// End gesture and skip the magnifier hide animation, so it can release
// resources.
await gesture.up();
await tester.pumpAndSettle();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -2576,7 +2581,7 @@ void main() { ...@@ -2576,7 +2581,7 @@ void main() {
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS) - multiline', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can move cursor when dragging, when tap is on collapsed selection (iOS) - multiline', (WidgetTester tester) async {
final TextEditingController controller = _textEditingController(); final TextEditingController controller = _textEditingController();
await tester.pumpWidget( await tester.pumpWidget(
...@@ -2620,11 +2625,16 @@ void main() { ...@@ -2620,11 +2625,16 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, testValue.indexOf('i')); expect(controller.selection.baseOffset, testValue.indexOf('i'));
// End gesture and skip the magnifier hide animation, so it can release
// resources.
await gesture.up();
await tester.pumpAndSettle();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
testWidgets('Can move cursor when dragging, when tap is on collapsed selection (iOS) - ListView', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can move cursor when dragging, when tap is on collapsed selection (iOS) - ListView', (WidgetTester tester) async {
// This is a regression test for // This is a regression test for
// https://github.com/flutter/flutter/issues/122519 // https://github.com/flutter/flutter/issues/122519
final TextEditingController controller = _textEditingController(); final TextEditingController controller = _textEditingController();
...@@ -2697,6 +2707,11 @@ void main() { ...@@ -2697,6 +2707,11 @@ void main() {
expect(controller.selection.isCollapsed, true); expect(controller.selection.isCollapsed, true);
expect(controller.selection.baseOffset, testValue.indexOf('i')); expect(controller.selection.baseOffset, testValue.indexOf('i'));
// End gesture and skip the magnifier hide animation, so it can release
// resources.
await gesture.up();
await tester.pumpAndSettle();
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
...@@ -12361,7 +12376,7 @@ void main() { ...@@ -12361,7 +12376,7 @@ void main() {
skip: isBrowser, // [intended] Browser handles arrow keys differently. skip: isBrowser, // [intended] Browser handles arrow keys differently.
); );
testWidgets('long press drag can edge scroll vertically', (WidgetTester tester) async { testWidgetsWithLeakTracking('long press drag can edge scroll vertically', (WidgetTester tester) async {
final TextEditingController controller = _textEditingController( final TextEditingController controller = _textEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neigse Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges', text: 'Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neigse Atwater Peel Sherbrooke Bonaventure Angrignon Peel Côte-des-Neiges',
); );
...@@ -12438,6 +12453,11 @@ void main() { ...@@ -12438,6 +12453,11 @@ void main() {
textOffsetToPosition(tester, 0).dy, textOffsetToPosition(tester, 0).dy,
moreOrLessEquals(firstCharY - lineHeight, epsilon: 1), moreOrLessEquals(firstCharY - lineHeight, epsilon: 1),
); );
// End gesture and skip the magnifier hide animation, so it can release
// resources.
await gesture.up();
await tester.pumpAndSettle();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgetsWithLeakTracking('keyboard selection change scrolls the field vertically', (WidgetTester tester) async { testWidgetsWithLeakTracking('keyboard selection change scrolls the field vertically', (WidgetTester tester) async {
...@@ -14283,7 +14303,7 @@ void main() { ...@@ -14283,7 +14303,7 @@ void main() {
}); });
}); });
}); });
testWidgets("Arrow keys don't move input focus", (WidgetTester tester) async { testWidgetsWithLeakTracking("Arrow keys don't move input focus", (WidgetTester tester) async {
final TextEditingController controller1 = _textEditingController(); final TextEditingController controller1 = _textEditingController();
final TextEditingController controller2 = _textEditingController(); final TextEditingController controller2 = _textEditingController();
final TextEditingController controller3 = _textEditingController(); final TextEditingController controller3 = _textEditingController();
...@@ -14446,7 +14466,7 @@ void main() { ...@@ -14446,7 +14466,7 @@ void main() {
expect(textFieldSize1, equals(textFieldSize2)); expect(textFieldSize1, equals(textFieldSize2));
}); });
testWidgets( testWidgetsWithLeakTracking(
'The selection menu displays in an Overlay without error', 'The selection menu displays in an Overlay without error',
(WidgetTester tester) async { (WidgetTester tester) async {
// This is a regression test for // This is a regression test for
...@@ -14455,6 +14475,9 @@ void main() { ...@@ -14455,6 +14475,9 @@ void main() {
text: 'This is a test that shows some odd behavior with Text Selection!', text: 'This is a test that shows some odd behavior with Text Selection!',
); );
late final OverlayEntry overlayEntry;
addTearDown(() => overlayEntry..remove()..dispose());
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
body: ColoredBox( body: ColoredBox(
...@@ -14466,7 +14489,7 @@ void main() { ...@@ -14466,7 +14489,7 @@ void main() {
height: 600, height: 600,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( overlayEntry = OverlayEntry(
builder: (BuildContext context) => Center( builder: (BuildContext context) => Center(
child: TextField( child: TextField(
controller: controller, controller: controller,
...@@ -16271,7 +16294,7 @@ void main() { ...@@ -16271,7 +16294,7 @@ void main() {
}); });
group('magnifier builder', () { group('magnifier builder', () {
testWidgets('should build custom magnifier if given', testWidgetsWithLeakTracking('should build custom magnifier if given',
(WidgetTester tester) async { (WidgetTester tester) async {
final Widget customMagnifier = Container( final Widget customMagnifier = Container(
key: UniqueKey(), key: UniqueKey(),
...@@ -16286,14 +16309,15 @@ void main() { ...@@ -16286,14 +16309,15 @@ void main() {
home: Placeholder(), home: Placeholder(),
)); ));
final BuildContext context = final BuildContext context = tester.firstElement(find.byType(Placeholder));
tester.firstElement(find.byType(Placeholder)); final ValueNotifier<MagnifierInfo> magnifierInfo = ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
addTearDown(magnifierInfo.dispose);
expect( expect(
textField.magnifierConfiguration!.magnifierBuilder( textField.magnifierConfiguration!.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty), magnifierInfo,
), ),
isA<Widget>().having( isA<Widget>().having(
(Widget widget) => widget.key, (Widget widget) => widget.key,
...@@ -16302,24 +16326,26 @@ void main() { ...@@ -16302,24 +16326,26 @@ void main() {
}); });
group('defaults', () { group('defaults', () {
testWidgets('should build Magnifier on Android', (WidgetTester tester) async { testWidgetsWithLeakTracking('should build Magnifier on Android', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
home: Scaffold(body: TextField())) home: Scaffold(body: TextField()))
); );
final BuildContext context = tester.firstElement(find.byType(TextField)); final BuildContext context = tester.firstElement(find.byType(TextField));
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
final ValueNotifier<MagnifierInfo> magnifierInfo = ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
addTearDown(magnifierInfo.dispose);
expect( expect(
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty), magnifierInfo,
), ),
isA<TextMagnifier>()); isA<TextMagnifier>());
}, variant: TargetPlatformVariant.only(TargetPlatform.android)); }, variant: TargetPlatformVariant.only(TargetPlatform.android));
testWidgets('should build CupertinoMagnifier on iOS', testWidgetsWithLeakTracking('should build CupertinoMagnifier on iOS',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
home: Scaffold(body: TextField())) home: Scaffold(body: TextField()))
...@@ -16327,17 +16353,19 @@ void main() { ...@@ -16327,17 +16353,19 @@ void main() {
final BuildContext context = tester.firstElement(find.byType(TextField)); final BuildContext context = tester.firstElement(find.byType(TextField));
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
final ValueNotifier<MagnifierInfo> magnifierInfo = ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
addTearDown(magnifierInfo.dispose);
expect( expect(
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty), magnifierInfo,
), ),
isA<CupertinoTextMagnifier>()); isA<CupertinoTextMagnifier>());
}, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS));
testWidgets('should build nothing on Android and iOS', testWidgetsWithLeakTracking('should build nothing on Android and iOS',
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
home: Scaffold(body: TextField())) home: Scaffold(body: TextField()))
...@@ -16345,12 +16373,14 @@ void main() { ...@@ -16345,12 +16373,14 @@ void main() {
final BuildContext context = tester.firstElement(find.byType(TextField)); final BuildContext context = tester.firstElement(find.byType(TextField));
final EditableText editableText = tester.widget(find.byType(EditableText)); final EditableText editableText = tester.widget(find.byType(EditableText));
final ValueNotifier<MagnifierInfo> magnifierInfo = ValueNotifier<MagnifierInfo>(MagnifierInfo.empty);
addTearDown(magnifierInfo.dispose);
expect( expect(
editableText.magnifierConfiguration.magnifierBuilder( editableText.magnifierConfiguration.magnifierBuilder(
context, context,
MagnifierController(), MagnifierController(),
ValueNotifier<MagnifierInfo>(MagnifierInfo.empty), magnifierInfo,
), ),
isNull); isNull);
}, },
......
...@@ -694,7 +694,7 @@ void main() { ...@@ -694,7 +694,7 @@ void main() {
expect(find.text('5 of 10'), findsOneWidget); expect(find.text('5 of 10'), findsOneWidget);
}); });
testWidgets('readonly text form field will hide cursor by default', (WidgetTester tester) async { testWidgetsWithLeakTracking('readonly text form field will hide cursor by default', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
......
...@@ -864,12 +864,15 @@ void main() { ...@@ -864,12 +864,15 @@ void main() {
expect(tooltipContainer.padding, const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0)); expect(tooltipContainer.padding, const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0));
}, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows})); }, variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows}));
testWidgets('Can tooltip decoration be customized', (WidgetTester tester) async { testWidgetsWithLeakTracking('Can tooltip decoration be customized', (WidgetTester tester) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>(); final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
const Decoration customDecoration = ShapeDecoration( const Decoration customDecoration = ShapeDecoration(
shape: StadiumBorder(), shape: StadiumBorder(),
color: Color(0x80800000), color: Color(0x80800000),
); );
late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Theme( Theme(
data: ThemeData(useMaterial3: false), data: ThemeData(useMaterial3: false),
...@@ -877,7 +880,7 @@ void main() { ...@@ -877,7 +880,7 @@ void main() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( entry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return Tooltip( return Tooltip(
key: tooltipKey, key: tooltipKey,
...@@ -1422,16 +1425,18 @@ void main() { ...@@ -1422,16 +1425,18 @@ void main() {
await tester.pump(waitDuration); await tester.pump(waitDuration);
}); });
testWidgets('Does tooltip contribute semantics', (WidgetTester tester) async { testWidgetsWithLeakTracking('Does tooltip contribute semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>(); final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
Directionality( Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Overlay( child: Overlay(
initialEntries: <OverlayEntry>[ initialEntries: <OverlayEntry>[
OverlayEntry( entry = OverlayEntry(
builder: (BuildContext context) { builder: (BuildContext context) {
return Stack( return Stack(
children: <Widget>[ children: <Widget>[
...@@ -2349,7 +2354,7 @@ void main() { ...@@ -2349,7 +2354,7 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
}); });
testWidgets('Tooltip is not selectable', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip is not selectable', (WidgetTester tester) async {
const String tooltipText = 'AAAAAAAAAAAAAAAAAAAAAAA'; const String tooltipText = 'AAAAAAAAAAAAAAAAAAAAAAA';
String? selectedText; String? selectedText;
await tester.pumpWidget( await tester.pumpWidget(
......
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