blob: 5076a8ce7f1c0ee813ee533b02256b63494aace1 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
library;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('copyWith, ==, hashCode basics', () {
expect(const NavigationBarThemeData(), const NavigationBarThemeData().copyWith());
expect(const NavigationBarThemeData().hashCode, const NavigationBarThemeData().copyWith().hashCode);
});
test('NavigationBarThemeData lerp special cases', () {
expect(NavigationBarThemeData.lerp(null, null, 0), null);
const NavigationBarThemeData data = NavigationBarThemeData();
expect(identical(NavigationBarThemeData.lerp(data, data, 0.5), data), true);
});
testWidgets('Default debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const NavigationBarThemeData().debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
testWidgets('Custom debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const NavigationBarThemeData(
height: 200.0,
backgroundColor: Color(0x00000099),
elevation: 20.0,
indicatorColor: Color(0x00000098),
indicatorShape: CircleBorder(),
labelTextStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(fontSize: 7.0)),
iconTheme: MaterialStatePropertyAll<IconThemeData>(IconThemeData(color: Color(0x00000097))),
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
overlayColor: MaterialStatePropertyAll<Color>(Color(0x00000096)),
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'height: 200.0');
expect(description[1], 'backgroundColor: Color(0x00000099)');
expect(description[2], 'elevation: 20.0');
expect(description[3], 'indicatorColor: Color(0x00000098)');
expect(description[4], 'indicatorShape: CircleBorder(BorderSide(width: 0.0, style: none))');
expect(description[5], 'labelTextStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 7.0))');
// Ignore instance address for IconThemeData.
expect(description[6].contains('iconTheme: WidgetStatePropertyAll(IconThemeData'), isTrue);
expect(description[6].contains('(color: Color(0x00000097))'), isTrue);
expect(description[7], 'labelBehavior: NavigationDestinationLabelBehavior.alwaysHide');
expect(description[8], 'overlayColor: WidgetStatePropertyAll(Color(0x00000096))');
});
testWidgets('NavigationBarThemeData values are used when no NavigationBar properties are specified', (WidgetTester tester) async {
const double height = 200.0;
const Color backgroundColor = Color(0x00000001);
const double elevation = 42.0;
const Color indicatorColor = Color(0x00000002);
const ShapeBorder indicatorShape = CircleBorder();
const double selectedIconSize = 25.0;
const double unselectedIconSize = 23.0;
const Color selectedIconColor = Color(0x00000003);
const Color unselectedIconColor = Color(0x00000004);
const double selectedIconOpacity = 0.99;
const double unselectedIconOpacity = 0.98;
const double selectedLabelFontSize = 13.0;
const double unselectedLabelFontSize = 11.0;
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: NavigationBarTheme(
data: NavigationBarThemeData(
height: height,
backgroundColor: backgroundColor,
elevation: elevation,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
iconTheme: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const IconThemeData(
size: selectedIconSize,
color: selectedIconColor,
opacity: selectedIconOpacity,
);
}
return const IconThemeData(
size: unselectedIconSize,
color: unselectedIconColor,
opacity: unselectedIconOpacity,
);
}),
labelTextStyle: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const TextStyle(fontSize: selectedLabelFontSize);
}
return const TextStyle(fontSize: unselectedLabelFontSize);
}),
labelBehavior: labelBehavior,
),
child: NavigationBar(
destinations: _destinations(),
),
),
),
),
);
expect(_barHeight(tester), height);
expect(_barMaterial(tester).color, backgroundColor);
expect(_barMaterial(tester).elevation, elevation);
expect(_indicator(tester)?.color, indicatorColor);
expect(_indicator(tester)?.shape, indicatorShape);
expect(_selectedIconTheme(tester).size, selectedIconSize);
expect(_selectedIconTheme(tester).color, selectedIconColor);
expect(_selectedIconTheme(tester).opacity, selectedIconOpacity);
expect(_unselectedIconTheme(tester).size, unselectedIconSize);
expect(_unselectedIconTheme(tester).color, unselectedIconColor);
expect(_unselectedIconTheme(tester).opacity, unselectedIconOpacity);
expect(_selectedLabelStyle(tester).fontSize, selectedLabelFontSize);
expect(_unselectedLabelStyle(tester).fontSize, unselectedLabelFontSize);
expect(_labelBehavior(tester), labelBehavior);
});
testWidgets('NavigationBar values take priority over NavigationBarThemeData values when both properties are specified', (WidgetTester tester) async {
const double height = 200.0;
const Color backgroundColor = Color(0x00000001);
const double elevation = 42.0;
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: NavigationBarTheme(
data: const NavigationBarThemeData(
height: 100.0,
elevation: 18.0,
backgroundColor: Color(0x00000099),
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
),
child: NavigationBar(
height: height,
elevation: elevation,
backgroundColor: backgroundColor,
labelBehavior: labelBehavior,
destinations: _destinations(),
),
),
),
),
);
expect(_barHeight(tester), height);
expect(_barMaterial(tester).color, backgroundColor);
expect(_barMaterial(tester).elevation, elevation);
expect(_labelBehavior(tester), labelBehavior);
});
testWidgets('Custom label style renders ink ripple properly', (WidgetTester tester) async {
Widget buildWidget({ NavigationDestinationLabelBehavior? labelBehavior }) {
return MaterialApp(
theme: ThemeData(
navigationBarTheme: const NavigationBarThemeData(
labelTextStyle: MaterialStatePropertyAll<TextStyle>(
TextStyle(fontSize: 25, color: Color(0xff0000ff)),
),
),
useMaterial3: true,
),
home: Scaffold(
bottomNavigationBar: Center(
child: NavigationBar(
labelBehavior: labelBehavior,
destinations: const <Widget>[
NavigationDestination(
icon: SizedBox(),
label: 'AC',
),
NavigationDestination(
icon: SizedBox(),
label: 'Alarm',
),
],
onDestinationSelected: (int i) { },
),
),
),
);
}
await tester.pumpWidget(buildWidget());
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(NavigationDestination).last));
await tester.pumpAndSettle();
await expectLater(find.byType(NavigationBar), matchesGoldenFile('indicator_custom_label_style.png'));
});
testWidgets('NavigationBar respects NavigationBarTheme.overlayColor in active/pressed/hovered states', (WidgetTester tester) async {
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
const Color hoverColor = Color(0xff0000ff);
const Color focusColor = Color(0xff00ffff);
const Color pressedColor = Color(0xffff00ff);
final MaterialStateProperty<Color?> overlayColor = MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoverColor;
}
if (states.contains(MaterialState.focused)) {
return focusColor;
}
if (states.contains(MaterialState.pressed)) {
return pressedColor;
}
return Colors.transparent;
});
await tester.pumpWidget(MaterialApp(
theme: ThemeData(navigationBarTheme: NavigationBarThemeData(overlayColor: overlayColor)),
home: Scaffold(
bottomNavigationBar: RepaintBoundary(
child: NavigationBar(
destinations: const <Widget>[
NavigationDestination(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
onDestinationSelected: (int i) { },
),
),
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(NavigationIndicator).last));
await tester.pumpAndSettle();
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
// Test hovered state.
expect(
inkFeatures,
kIsWeb
? (paints..rrect()..rrect()..circle(color: hoverColor))
: (paints..circle(color: hoverColor)),
);
await gesture.down(tester.getCenter(find.byType(NavigationIndicator).last));
await tester.pumpAndSettle();
// Test pressed state.
expect(
inkFeatures,
kIsWeb
? (paints..circle()..circle()..circle(color: pressedColor))
: (paints..circle()..circle(color: pressedColor)),
);
await gesture.up();
await tester.pumpAndSettle();
// Press tab to focus the navigation bar.
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pumpAndSettle();
// Test focused state.
expect(
inkFeatures,
kIsWeb ? (paints..circle()..circle(color: focusColor)) : (paints..circle()..circle(color: focusColor)),
);
});
}
List<NavigationDestination> _destinations() {
return const <NavigationDestination>[
NavigationDestination(
icon: Icon(Icons.favorite_border),
selectedIcon: Icon(Icons.favorite),
label: 'Abc',
),
NavigationDestination(
icon: Icon(Icons.star_border),
selectedIcon: Icon(Icons.star),
label: 'Def',
),
];
}
double _barHeight(WidgetTester tester) {
return tester.getRect(
find.byType(NavigationBar),
).height;
}
Material _barMaterial(WidgetTester tester) {
return tester.firstWidget<Material>(
find.descendant(
of: find.byType(NavigationBar),
matching: find.byType(Material),
),
);
}
ShapeDecoration? _indicator(WidgetTester tester) {
return tester.firstWidget<Container>(
find.descendant(
of: find.byType(FadeTransition),
matching: find.byType(Container),
),
).decoration as ShapeDecoration?;
}
IconThemeData _selectedIconTheme(WidgetTester tester) {
return _iconTheme(tester, Icons.favorite);
}
IconThemeData _unselectedIconTheme(WidgetTester tester) {
return _iconTheme(tester, Icons.star_border);
}
IconThemeData _iconTheme(WidgetTester tester, IconData icon) {
return tester.firstWidget<IconTheme>(
find.ancestor(
of: find.byIcon(icon),
matching: find.byType(IconTheme),
),
).data;
}
TextStyle _selectedLabelStyle(WidgetTester tester) {
return tester.widget<RichText>(
find.descendant(
of: find.text('Abc'),
matching: find.byType(RichText),
),
).text.style!;
}
TextStyle _unselectedLabelStyle(WidgetTester tester) {
return tester.widget<RichText>(
find.descendant(
of: find.text('Def'),
matching: find.byType(RichText),
),
).text.style!;
}
NavigationDestinationLabelBehavior _labelBehavior(WidgetTester tester) {
if (_opacityAboveLabel('Abc').evaluate().isNotEmpty && _opacityAboveLabel('Def').evaluate().isNotEmpty) {
return _labelOpacity(tester, 'Abc') == 1
? NavigationDestinationLabelBehavior.onlyShowSelected
: NavigationDestinationLabelBehavior.alwaysHide;
} else {
return NavigationDestinationLabelBehavior.alwaysShow;
}
}
Finder _opacityAboveLabel(String text) {
return find.ancestor(
of: find.text(text),
matching: find.byType(Opacity),
);
}
// Only valid when labelBehavior != alwaysShow.
double _labelOpacity(WidgetTester tester, String text) {
final Opacity opacityWidget = tester.widget<Opacity>(
find.ancestor(
of: find.text(text),
matching: find.byType(Opacity),
),
);
return opacityWidget.opacity;
}