Unverified Commit 1a9e866f authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Reland update bottom navigation bar test for m3 (#137998)

This is a reland of https://github.com/flutter/flutter/pull/136624 which was reverted because one new M3 golden test failed. The failure was related to the ink sparkle animation.

Ink sparkle is the M3 default animation, it does not play well with golden because it introduces an element of randomness.
One way to avoid this randomness is to use the `InkSparkle.constantTurbulenceSeedSplashFactory`.

This PR has two commits:
- the first one is the original PR (https://github.com/flutter/flutter/pull/136624).
- the second one updates the failing test using `InkSparkle.constantTurbulenceSeedSplashFactory`.
parent 9c9e0617
...@@ -1159,7 +1159,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -1159,7 +1159,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
removeBottom: true, removeBottom: true,
child: DefaultTextStyle.merge( child: DefaultTextStyle.merge(
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _createTiles(layout), children: _createTiles(layout),
), ),
......
...@@ -7,8 +7,10 @@ ...@@ -7,8 +7,10 @@
@Tags(<String>['reduced-test-set']) @Tags(<String>['reduced-test-set'])
library; library;
import 'dart:math';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -49,7 +51,7 @@ void main() { ...@@ -49,7 +51,7 @@ void main() {
expect(mutatedIndex, 1); expect(mutatedIndex, 1);
}); });
testWidgetsWithLeakTracking('BottomNavigationBar content test', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - BottomNavigationBar content test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -76,7 +78,34 @@ void main() { ...@@ -76,7 +78,34 @@ void main() {
expect(find.text('Alarm'), findsOneWidget); expect(find.text('Alarm'), findsOneWidget);
}); });
testWidgetsWithLeakTracking('Fixed BottomNavigationBar defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - BottomNavigationBar content test', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(box.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
expect(find.text('AC'), findsOneWidget);
expect(find.text('Alarm'), findsOneWidget);
});
testWidgetsWithLeakTracking('Material2 - Fixed BottomNavigationBar defaults', (WidgetTester tester) async {
const Color primaryColor = Color(0xFF000001); const Color primaryColor = Color(0xFF000001);
const Color unselectedWidgetColor = Color(0xFF000002); const Color unselectedWidgetColor = Color(0xFF000002);
...@@ -141,6 +170,71 @@ void main() { ...@@ -141,6 +170,71 @@ void main() {
expect(_getMaterial(tester).elevation, equals(8.0)); expect(_getMaterial(tester).elevation, equals(8.0));
}); });
testWidgetsWithLeakTracking('Material3 - Fixed BottomNavigationBar defaults', (WidgetTester tester) async {
const Color primaryColor = Color(0xFF000001);
const Color unselectedWidgetColor = Color(0xFF000002);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
colorScheme: const ColorScheme.light().copyWith(primary: primaryColor),
unselectedWidgetColor: unselectedWidgetColor,
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
const double selectedFontSize = 14.0;
const double unselectedFontSize = 12.0;
final TextStyle selectedFontStyle = tester.renderObject<RenderParagraph>(find.text('AC')).text.style!;
final TextStyle unselectedFontStyle = tester.renderObject<RenderParagraph>(find.text('Alarm')).text.style!;
final TextStyle selectedIcon = _iconStyle(tester, Icons.ac_unit);
final TextStyle unselectedIcon = _iconStyle(tester, Icons.access_alarm);
expect(selectedFontStyle.color, equals(primaryColor));
expect(selectedFontStyle.fontSize, selectedFontSize);
expect(selectedFontStyle.fontWeight, equals(FontWeight.w400));
expect(selectedFontStyle.height, 1.43);
expect(unselectedFontStyle.color, equals(unselectedWidgetColor));
expect(unselectedFontStyle.fontWeight, equals(FontWeight.w400));
expect(unselectedFontStyle.height, 1.43);
// Unselected label has a font size of 14 but is scaled down to be font size 12.
expect(
tester.firstWidget<Transform>(find.ancestor(of: find.text('Alarm'), matching: find.byType(Transform))).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedFontSize / selectedFontSize))),
);
expect(selectedIcon.color, equals(primaryColor));
expect(selectedIcon.fontSize, equals(24.0));
expect(unselectedIcon.color, equals(unselectedWidgetColor));
expect(unselectedIcon.fontSize, equals(24.0));
// There should not be any [Opacity] or [FadeTransition] widgets
// since showUnselectedLabels and showSelectedLabels are true.
final Finder findOpacity = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(Opacity),
);
final Finder findFadeTransition = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(FadeTransition),
);
expect(findOpacity, findsNothing);
expect(findFadeTransition, findsNothing);
expect(_getMaterial(tester).elevation, equals(8.0));
});
testWidgetsWithLeakTracking('Custom selected and unselected font styles', (WidgetTester tester) async { testWidgetsWithLeakTracking('Custom selected and unselected font styles', (WidgetTester tester) async {
const TextStyle selectedTextStyle = TextStyle(fontWeight: FontWeight.w200, fontSize: 18.0); const TextStyle selectedTextStyle = TextStyle(fontWeight: FontWeight.w200, fontSize: 18.0);
const TextStyle unselectedTextStyle = TextStyle(fontWeight: FontWeight.w600, fontSize: 12.0); const TextStyle unselectedTextStyle = TextStyle(fontWeight: FontWeight.w600, fontSize: 12.0);
...@@ -411,7 +505,7 @@ void main() { ...@@ -411,7 +505,7 @@ void main() {
expect(unselectedItemPadding.bottom, equals((selectedIconSize - unselectedIconSize) / 2.0)); expect(unselectedItemPadding.bottom, equals((selectedIconSize - unselectedIconSize) / 2.0));
}); });
testWidgetsWithLeakTracking('Shifting BottomNavigationBar defaults', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - Shifting BottomNavigationBar defaults', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -440,6 +534,35 @@ void main() { ...@@ -440,6 +534,35 @@ void main() {
expect(_getMaterial(tester).elevation, equals(8.0)); expect(_getMaterial(tester).elevation, equals(8.0));
}); });
testWidgetsWithLeakTracking('Material3 - Shifting BottomNavigationBar defaults', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
const double selectedFontSize = 14.0;
expect(tester.renderObject<RenderParagraph>(find.text('AC')).text.style!.fontSize, selectedFontSize);
final ThemeData theme = Theme.of(tester.element(find.text('AC')));
expect(tester.renderObject<RenderParagraph>(find.text('AC')).text.style!.color, equals(theme.colorScheme.surface));
expect(_getOpacity(tester, 'Alarm'), equals(0.0));
expect(_getMaterial(tester).elevation, equals(8.0));
});
testWidgetsWithLeakTracking('Fixed BottomNavigationBar custom font size, color', (WidgetTester tester) async { testWidgetsWithLeakTracking('Fixed BottomNavigationBar custom font size, color', (WidgetTester tester) async {
const Color primaryColor = Color(0xFF000000); const Color primaryColor = Color(0xFF000000);
const Color unselectedWidgetColor = Color(0xFFD501FF); const Color unselectedWidgetColor = Color(0xFFD501FF);
...@@ -990,7 +1113,7 @@ void main() { ...@@ -990,7 +1113,7 @@ void main() {
expect(_getMaterial(tester).elevation, equals(customElevation)); expect(_getMaterial(tester).elevation, equals(customElevation));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar adds bottom padding to height', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - BottomNavigationBar adds bottom padding to height', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -1018,6 +1141,33 @@ void main() { ...@@ -1018,6 +1141,33 @@ void main() {
expect(tester.getSize(find.byType(BottomNavigationBar)).height, expectedHeight); expect(tester.getSize(find.byType(BottomNavigationBar)).height, expectedHeight);
}); });
testWidgetsWithLeakTracking('Material3 - BottomNavigationBar adds bottom padding to height', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: MediaQuery(
data: const MediaQueryData(viewPadding: EdgeInsets.only(bottom: 40.0)),
child: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
),
);
const double expectedMinHeight = kBottomNavigationBarHeight + 40.0;
expect(tester.getSize(find.byType(BottomNavigationBar)).height >= expectedMinHeight, isTrue);
});
testWidgetsWithLeakTracking('BottomNavigationBar adds bottom padding to height with a custom font size', (WidgetTester tester) async { testWidgetsWithLeakTracking('BottomNavigationBar adds bottom padding to height with a custom font size', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1323,7 +1473,7 @@ void main() { ...@@ -1323,7 +1473,7 @@ void main() {
expect(builderIconSize, 12.0); expect(builderIconSize, 12.0);
}); });
testWidgetsWithLeakTracking('BottomNavigationBar responds to textScaleFactor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - BottomNavigationBar responds to textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -1344,10 +1494,8 @@ void main() { ...@@ -1344,10 +1494,8 @@ void main() {
), ),
), ),
); );
final RenderBox defaultBox = tester.renderObject(find.byType(BottomNavigationBar)); final RenderBox defaultBox = tester.renderObject(find.byType(BottomNavigationBar));
expect(defaultBox.size.height, equals(kBottomNavigationBarHeight)); expect(defaultBox.size.height, equals(kBottomNavigationBarHeight));
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -1367,10 +1515,8 @@ void main() { ...@@ -1367,10 +1515,8 @@ void main() {
), ),
), ),
); );
final RenderBox shiftingBox = tester.renderObject(find.byType(BottomNavigationBar)); final RenderBox shiftingBox = tester.renderObject(find.byType(BottomNavigationBar));
expect(shiftingBox.size.height, equals(kBottomNavigationBarHeight)); expect(shiftingBox.size.height, equals(kBottomNavigationBarHeight));
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: MediaQuery( home: MediaQuery(
...@@ -1397,7 +1543,81 @@ void main() { ...@@ -1397,7 +1543,81 @@ void main() {
expect(box.size.height, equals(56.0)); expect(box.size.height, equals(56.0));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar does not grow with textScaleFactor when labels are provided', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - BottomNavigationBar responds to textScaleFactor', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
);
final RenderBox defaultBox = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(defaultBox.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
);
final RenderBox shiftingBox = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(shiftingBox.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
await tester.pumpWidget(
MaterialApp(
home: MediaQuery(
data: const MediaQueryData(textScaleFactor: 2.0),
child: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
),
);
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(box.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
});
testWidgetsWithLeakTracking('Material2 - BottomNavigationBar does not grow with textScaleFactor when labels are provided', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false), theme: ThemeData(useMaterial3: false),
...@@ -1448,7 +1668,7 @@ void main() { ...@@ -1448,7 +1668,7 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: MediaQuery( home: MediaQuery(
data: const MediaQueryData(textScaleFactor: 2.0), data: const MediaQueryData(textScaler: TextScaler.linear(2.0)),
child: Scaffold( child: Scaffold(
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[ items: const <BottomNavigationBarItem>[
...@@ -1471,7 +1691,83 @@ void main() { ...@@ -1471,7 +1691,83 @@ void main() {
expect(box.size.height, equals(kBottomNavigationBarHeight)); expect(box.size.height, equals(kBottomNavigationBarHeight));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar shows tool tips with text scaling on long press when labels are provided', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - BottomNavigationBar does not grow with textScaleFactor when labels are provided', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
);
final RenderBox defaultBox = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(defaultBox.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
);
final RenderBox shiftingBox = tester.renderObject(find.byType(BottomNavigationBar));
// kBottomNavigationBarHeight is a minimum dimension.
expect(shiftingBox.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
await tester.pumpWidget(
MaterialApp(
home: MediaQuery(
data: const MediaQueryData(textScaler: TextScaler.linear(2.0)),
child: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'A',
icon: Icon(Icons.ac_unit),
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
),
),
);
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
expect(box.size.height, equals(defaultBox.size.height));
expect(box.size.height, equals(shiftingBox.size.height));
});
testWidgetsWithLeakTracking('Material2 - BottomNavigationBar shows tool tips with text scaling on long press when labels are provided', (WidgetTester tester) async {
const String label = 'Foo'; const String label = 'Foo';
Widget buildApp({ required double textScaleFactor }) { Widget buildApp({ required double textScaleFactor }) {
...@@ -1529,6 +1825,63 @@ void main() { ...@@ -1529,6 +1825,63 @@ void main() {
expect(tester.getSize(find.text(label).last), equals(const Size(168.0, 56.0))); expect(tester.getSize(find.text(label).last), equals(const Size(168.0, 56.0)));
}); });
testWidgetsWithLeakTracking('Material3 - BottomNavigationBar shows tool tips with text scaling on long press when labels are provided', (WidgetTester tester) async {
const String label = 'Foo';
Widget buildApp({ required double textScaleFactor }) {
return MediaQuery(
data: MediaQueryData(textScaleFactor: textScaleFactor),
child: Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: Directionality(
textDirection: TextDirection.ltr,
child: Navigator(
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<void>(
builder: (BuildContext context) {
return MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: label,
icon: Icon(Icons.ac_unit),
tooltip: label,
),
BottomNavigationBarItem(
label: 'B',
icon: Icon(Icons.battery_alert),
),
],
),
),
);
},
);
},
),
),
),
);
}
await tester.pumpWidget(buildApp(textScaleFactor: 1.0));
expect(find.text(label), findsOneWidget);
await tester.longPress(find.text(label));
expect(find.text(label), findsNWidgets(2));
expect(tester.getSize(find.text(label).last).height, equals(20.0));
await tester.pumpAndSettle(const Duration(seconds: 2));
await tester.pumpWidget(buildApp(textScaleFactor: 4.0));
expect(find.text(label), findsOneWidget);
await tester.longPress(find.text(label));
expect(tester.getSize(find.text(label).last).height, equals(80.0));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgetsWithLeakTracking('Different behaviour of tool tip in BottomNavigationBarItem', (WidgetTester tester) async { testWidgetsWithLeakTracking('Different behaviour of tool tip in BottomNavigationBarItem', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1574,7 +1927,6 @@ void main() { ...@@ -1574,7 +1927,6 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData(useMaterial3: false),
home: Scaffold( home: Scaffold(
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[ items: <BottomNavigationBarItem>[
...@@ -1593,15 +1945,15 @@ void main() { ...@@ -1593,15 +1945,15 @@ void main() {
); );
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar)); final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
expect(box.size.height, equals(kBottomNavigationBarHeight)); expect(box.size.height, greaterThanOrEqualTo(kBottomNavigationBarHeight));
final RenderBox itemBoxA = tester.renderObject(find.text(longTextA)); final RenderBox itemBoxA = tester.renderObject(find.text(longTextA));
expect(itemBoxA.size, equals(const Size(400.0, 14.0))); expect(itemBoxA.size.width, equals(400.0));
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB)); final RenderBox itemBoxB = tester.renderObject(find.text(longTextB));
expect(itemBoxB.size, equals(const Size(400.0, 14.0))); expect(itemBoxB.size.width, equals(400.0));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar paints circles', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - BottomNavigationBar paints circles', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
boilerplate( boilerplate(
useMaterial3: false, useMaterial3: false,
...@@ -1637,6 +1989,7 @@ void main() { ...@@ -1637,6 +1989,7 @@ void main() {
// Now we flip the directionality and verify that the circles switch positions. // Now we flip the directionality and verify that the circles switch positions.
await tester.pumpWidget( await tester.pumpWidget(
boilerplate( boilerplate(
useMaterial3: false,
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[ items: const <BottomNavigationBarItem>[
...@@ -1929,7 +2282,7 @@ void main() { ...@@ -1929,7 +2282,7 @@ void main() {
expect(tester.widget<Material>(backgroundMaterial).color, Colors.green); expect(tester.widget<Material>(backgroundMaterial).color, Colors.green);
}); });
group('BottomNavigationBar shifting backgroundColor with transition', () { group('Material2 - BottomNavigationBar shifting backgroundColor with transition', () {
// Regression test for: https://github.com/flutter/flutter/issues/22226 // Regression test for: https://github.com/flutter/flutter/issues/22226
Widget runTest() { Widget runTest() {
int currentIndex = 0; int currentIndex = 0;
...@@ -1976,7 +2329,63 @@ void main() { ...@@ -1976,7 +2329,63 @@ void main() {
} }
await expectLater( await expectLater(
find.byType(BottomNavigationBar), find.byType(BottomNavigationBar),
matchesGoldenFile('bottom_navigation_bar.shifting_transition.${pump - 1}.png'), matchesGoldenFile('m2_bottom_navigation_bar.shifting_transition.${pump - 1}.png'),
);
});
}
});
group('Material3 - BottomNavigationBar shifting backgroundColor with transition', () {
// Regression test for: https://github.com/flutter/flutter/issues/22226
Widget runTest() {
int currentIndex = 0;
const bool useInkSparkle = !kIsWeb;
return MaterialApp(
// Because this test relies on golden, fo a constant seed for the ink sparkle animation.
theme: ThemeData(splashFactory: useInkSparkle ? InkSparkle.constantTurbulenceSeedSplashFactory : null),
home: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Scaffold(
bottomNavigationBar: RepaintBoundary(
child: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
currentIndex: currentIndex,
onTap: (int index) {
setState(() {
currentIndex = index;
});
},
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Red',
backgroundColor: Colors.red,
icon: Icon(Icons.dashboard),
),
BottomNavigationBarItem(
label: 'Green',
backgroundColor: Colors.green,
icon: Icon(Icons.menu),
),
],
),
),
);
},
),
);
}
for (int pump = 1; pump < 9; pump++) {
testWidgetsWithLeakTracking('pump $pump', (WidgetTester tester) async {
await tester.pumpWidget(runTest());
await tester.tap(find.text('Green'));
for (int i = 0; i < pump; i++) {
await tester.pump(const Duration(milliseconds: 30));
}
await expectLater(
find.byType(BottomNavigationBar),
matchesGoldenFile('m3_bottom_navigation_bar.shifting_transition.${pump - 1}.png'),
); );
}); });
} }
...@@ -2368,7 +2777,7 @@ void main() { ...@@ -2368,7 +2777,7 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgetsWithLeakTracking('BottomNavigationBar default layout', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material2 - BottomNavigationBar default layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey(); final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey(); final Key icon1 = UniqueKey();
...@@ -2416,7 +2825,74 @@ void main() { ...@@ -2416,7 +2825,74 @@ void main() {
expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(500.0, 560.0, 700.0, 570.0)); expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(500.0, 560.0, 700.0, 570.0));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar centered landscape layout', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - BottomNavigationBar default layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey();
const double iconHeight = 10;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: SizedBox(key: icon0, width: 200, height: iconHeight),
label: 'Title0',
),
BottomNavigationBarItem(
icon: SizedBox(key: icon1, width: 200, height: iconHeight),
label: 'Title1',
),
],
),
);
},
),
),
);
expect(tester.getSize(find.byType(BottomNavigationBar)), const Size(800, kBottomNavigationBarHeight));
expect(tester.getRect(find.byType(BottomNavigationBar)), const Rect.fromLTRB(0, 600 - kBottomNavigationBarHeight, 800, 600));
const double navigationBarTop = 600 - kBottomNavigationBarHeight; // 544
const double selectedFontSize = 14.0;
const double m3LineHeight = 1.43;
final double labelHeight = (selectedFontSize * m3LineHeight).floorToDouble(); // 20
const double navigationTileVerticalPadding = selectedFontSize / 2; // 7.0
final double navigationTileHeight = iconHeight + labelHeight + 2 * navigationTileVerticalPadding;
// Navigation tiles parent is a Row with crossAxisAlignment set to center.
final double navigationTileVerticalOffset = (kBottomNavigationBarHeight - navigationTileHeight) / 2;
final double iconTop = navigationBarTop + navigationTileVerticalOffset + navigationTileVerticalPadding;
final double labelBottom = 600 - (navigationTileVerticalOffset + navigationTileVerticalPadding);
expect(tester.getRect(find.byKey(icon0)).top, iconTop);
expect(tester.getRect(find.text('Title0')).bottom, labelBottom);
// The items are padded horizontally according to
// MainAxisAlignment.spaceAround. Left/right padding is:
// 800 - (200 * 2) / 4 = 100
// The layout of the unselected item's label is slightly different; not
// checking that here.
final double firstLabelWidth = tester.getSize(find.text('Title0')).width;
const double itemsWidth = 800 / 2; // 2 items.
const double firstLabelCenter = itemsWidth / 2;
expect(
tester.getRect(find.text('Title0')),
Rect.fromLTRB(
firstLabelCenter - firstLabelWidth / 2,
labelBottom - labelHeight,
firstLabelCenter + firstLabelWidth / 2,
labelBottom,
),
);
expect(tester.getRect(find.byKey(icon0)), Rect.fromLTRB(100.0, iconTop, 300.0, iconTop + iconHeight));
expect(tester.getRect(find.byKey(icon1)), Rect.fromLTRB(500.0, iconTop, 700.0, iconTop + iconHeight));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgetsWithLeakTracking('Material2 - BottomNavigationBar centered landscape layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey(); final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey(); final Key icon1 = UniqueKey();
...@@ -2462,7 +2938,80 @@ void main() { ...@@ -2462,7 +2938,80 @@ void main() {
expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(450.0, 560.0, 650.0, 570.0)); expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(450.0, 560.0, 650.0, 570.0));
}); });
testWidgetsWithLeakTracking('BottomNavigationBar linear landscape layout', (WidgetTester tester) async { testWidgetsWithLeakTracking('Material3 - BottomNavigationBar centered landscape layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey();
const double iconWidth = 200;
const double iconHeight = 10;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: SizedBox(key: icon0, width: iconWidth, height: iconHeight),
label: 'Title0',
),
BottomNavigationBarItem(
icon: SizedBox(key: icon1, width: iconWidth, height: iconHeight),
label: 'Title1',
),
],
),
);
},
),
),
);
expect(tester.getSize(find.byType(BottomNavigationBar)), const Size(800, kBottomNavigationBarHeight));
expect(tester.getRect(find.byType(BottomNavigationBar)), const Rect.fromLTRB(0, 600 - kBottomNavigationBarHeight, 800, 600));
const double navigationBarTop = 600 - kBottomNavigationBarHeight; // 544
const double selectedFontSize = 14.0;
const double m3LineHeight = 1.43;
final double labelHeight = (selectedFontSize * m3LineHeight).floorToDouble(); // 20
const double navigationTileVerticalPadding = selectedFontSize / 2; // 7.0
final double navigationTileHeight = iconHeight + labelHeight + 2 * navigationTileVerticalPadding;
// Navigation tiles parent is a Row with crossAxisAlignment sets to center.
final double navigationTileVerticalOffset = (kBottomNavigationBarHeight - navigationTileHeight) / 2;
final double iconTop = navigationBarTop + navigationTileVerticalOffset + navigationTileVerticalPadding;
final double labelBottom = 600 - (navigationTileVerticalOffset + navigationTileVerticalPadding);
// The items are laid out as in the default case, within width = 600
// (the "portrait" width) and the result is centered with the
// landscape width = 800.
// So item 0's left edges are:
// ((800 - 600) / 2) + ((600 - 400) / 4) = 150.
// Item 1's right edge is:
// 800 - 150 = 650
// The layout of the unselected item's label is slightly different; not
// checking that here.
final double firstLabelWidth = tester.getSize(find.text('Title0')).width;
const double itemWidth = iconWidth; // 200
const double firstItemLeft = 150;
const double firstLabelCenter = firstItemLeft + itemWidth / 2; // 250
expect(tester.getRect(
find.text('Title0')),
Rect.fromLTRB(
firstLabelCenter - firstLabelWidth / 2,
labelBottom - labelHeight,
firstLabelCenter + firstLabelWidth / 2,
labelBottom,
),
);
expect(tester.getRect(find.byKey(icon0)), Rect.fromLTRB(150.0, iconTop, 350.0, iconTop + iconHeight));
expect(tester.getRect(find.byKey(icon1)), Rect.fromLTRB(450.0, iconTop, 650.0, iconTop + iconHeight));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
testWidgetsWithLeakTracking('Material2 - BottomNavigationBar linear landscape layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey(); final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey(); final Key icon1 = UniqueKey();
...@@ -2502,6 +3051,80 @@ void main() { ...@@ -2502,6 +3051,80 @@ void main() {
expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(104.0, 562.0, 204.0, 582.0)); expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(104.0, 562.0, 204.0, 582.0));
expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(504.0, 562.0, 604.0, 582.0)); expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(504.0, 562.0, 604.0, 582.0));
}); });
testWidgetsWithLeakTracking('Material3 - BottomNavigationBar linear landscape layout', (WidgetTester tester) async {
final Key icon0 = UniqueKey();
final Key icon1 = UniqueKey();
const double iconWidth = 100;
const double iconHeight = 20;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(),
home: Builder(
builder: (BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
landscapeLayout: BottomNavigationBarLandscapeLayout.linear,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: SizedBox(key: icon0, width: iconWidth, height: iconHeight),
label: 'Title0',
),
BottomNavigationBarItem(
icon: SizedBox(key: icon1, width: iconWidth, height: iconHeight),
label: 'Title1',
),
],
),
);
},
),
),
);
expect(tester.getSize(find.byType(BottomNavigationBar)), const Size(800, kBottomNavigationBarHeight));
expect(tester.getRect(find.byType(BottomNavigationBar)), const Rect.fromLTRB(0, 600 - kBottomNavigationBarHeight, 800, 600));
const double navigationBarTop = 600 - kBottomNavigationBarHeight; // 544
const double selectedFontSize = 14.0;
const double m3LineHeight = 1.43;
final double labelHeight = (selectedFontSize * m3LineHeight).floorToDouble(); // 20
const double navigationTileVerticalPadding = selectedFontSize / 2; // 7.0
// Icon and label are in the same row.
final double navigationTileHeight = max(iconHeight, labelHeight) + 2 * navigationTileVerticalPadding;
// Navigation tiles parent is a Row with crossAxisAlignment sets to center.
final double navigationTileVerticalOffset = (kBottomNavigationBarHeight - navigationTileHeight) / 2;
final double iconTop = navigationBarTop + navigationTileVerticalOffset + navigationTileVerticalPadding;
final double labelBottom = 600 - (navigationTileVerticalOffset + navigationTileVerticalPadding);
// The items are laid out as in the default case except each
// item's icon/label is arranged in a row, with 8 pixels in
// between the icon and label. The layout of the unselected
// item's label is slightly different; not checking that here.
const double itemFullWith = 800 / 2; // Two items in the navigation bar.
const double separatorWidth = 8;
final double firstLabelWidth = tester.getSize(find.text('Title0')).width;
final double firstItemContentWidth = iconWidth + separatorWidth + firstLabelWidth;
final double firstItemLeft = itemFullWith / 2 - firstItemContentWidth / 2;
final double secondLabelWidth = tester.getSize(find.text('Title1')).width;
final double secondItemContentWidth = iconWidth + separatorWidth + secondLabelWidth;
final double secondItemLeft = itemFullWith + itemFullWith / 2 - secondItemContentWidth / 2;
expect(tester.getRect(
find.text('Title0')),
Rect.fromLTRB(
firstItemLeft + iconWidth + separatorWidth,
labelBottom - labelHeight,
firstItemLeft + iconWidth + separatorWidth + firstLabelWidth,
labelBottom,
),
);
expect(tester.getRect(find.byKey(icon0)), Rect.fromLTRB(firstItemLeft, iconTop, firstItemLeft + iconWidth, iconTop + iconHeight));
expect(tester.getRect(find.byKey(icon1)), Rect.fromLTRB(secondItemLeft, iconTop, secondItemLeft + iconWidth, iconTop + iconHeight));
}, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933
} }
Widget boilerplate({ Widget? bottomNavigationBar, required TextDirection textDirection, bool? useMaterial3 }) { Widget boilerplate({ Widget? bottomNavigationBar, required TextDirection textDirection, bool? useMaterial3 }) {
......
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