[Material] selected/unselected label styles + icon themes on BottomNavigationBar (#31018)

* add text style params

* add icon theme params

* Added tests
This commit is contained in:
MH Johnson 2019-05-02 18:20:16 -04:00 committed by GitHub
parent 1d91bd2583
commit a40e5c90f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 457 additions and 44 deletions

View file

@ -189,7 +189,6 @@ class _BottomNavigationDemoState extends State<BottomNavigationDemo>
.toList(),
currentIndex: _currentIndex,
type: _type,
//iconSize: 4.0,
onTap: (int index) {
setState(() {
_navigationViews[_currentIndex].controller.reverse();

View file

@ -147,6 +147,17 @@ class BottomNavigationBar extends StatefulWidget {
/// The [iconSize], [selectedFontSize], [unselectedFontSize], and [elevation]
/// arguments must be non-null and non-negative.
///
/// If [selectedLabelStyle.color] and [unselectedLabelStyle.color] values
/// are non-null, they will be used instead of [selectedItemColor] and
/// [unselectedItemColor].
///
/// If custom [IconThemData]s are used, you must provide both
/// [selectedIconTheme] and [unselectedIconTheme], and both
/// [IconThemeData.color] and [IconThemeData.size] must be set.
///
/// If both [selectedLabelStyle.fontSize] and [selectedFontSize] are set,
/// [selectedLabelStyle.fontSize] will be used.
///
/// Only one of [selectedItemColor] and [fixedColor] can be specified. The
/// former is preferred, [fixedColor] only exists for the sake of
/// backwards compatibility.
@ -168,8 +179,12 @@ class BottomNavigationBar extends StatefulWidget {
this.iconSize = 24.0,
Color selectedItemColor,
this.unselectedItemColor,
this.selectedIconTheme = const IconThemeData(),
this.unselectedIconTheme = const IconThemeData(),
this.selectedFontSize = 14.0,
this.unselectedFontSize = 12.0,
this.selectedLabelStyle,
this.unselectedLabelStyle,
this.showSelectedLabels = true,
bool showUnselectedLabels,
}) : assert(items != null),
@ -250,14 +265,48 @@ class BottomNavigationBar extends StatefulWidget {
/// If null then the [TextTheme.caption]'s color is used.
final Color unselectedItemColor;
/// The size, opacity, and color of the icon in the currently selected
/// [BottomNavigationBarItem.icon].
///
/// If this is not provided, the size will default to [iconSize], the color
/// will default to [selectedItemColor].
///
/// It this field is provided, it must contain non-null [IconThemeData.size]
/// and [IconThemeData.color] properties. Also, if this field is supplied,
/// [unselectedIconTheme] must be provided.
final IconThemeData selectedIconTheme;
/// The size, opacity, and color of the icon in the currently unselected
/// [BottomNavigationBarItem.icon]s
///
/// If this is not provided, the size will default to [iconSize], the color
/// will default to [unselectedItemColor].
///
/// It this field is provided, it must contain non-null [IconThemeData.size]
/// and [IconThemeData.color] properties. Also, if this field is supplied,
/// [unselectedIconTheme] must be provided.
final IconThemeData unselectedIconTheme;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are
/// selected.
final TextStyle selectedLabelStyle;
/// The [TextStyle] of the [BottomNavigationBarItem] labels when they are not
/// selected.
final TextStyle unselectedLabelStyle;
/// The font size of the [BottomNavigationBarItem] labels when they are selected.
///
/// If [selectedLabelStyle.fontSize] is non-null, it will be used instead of this.
///
/// Defaults to `14.0`.
final double selectedFontSize;
/// The font size of the [BottomNavigationBarItem] labels when they are not
/// selected.
///
/// If [unselectedLabelStyle.fontSize] is non-null, it will be used instead of this.
///
/// Defaults to `12.0`.
final double unselectedFontSize;
@ -314,8 +363,10 @@ class _BottomNavigationTile extends StatelessWidget {
this.colorTween,
this.flex,
this.selected = false,
@required this.selectedFontSize,
@required this.unselectedFontSize,
@required this.selectedLabelStyle,
@required this.unselectedLabelStyle,
@required this.selectedIconTheme,
@required this.unselectedIconTheme,
this.showSelectedLabels,
this.showUnselectedLabels,
this.indexLabel,
@ -323,8 +374,8 @@ class _BottomNavigationTile extends StatelessWidget {
assert(item != null),
assert(animation != null),
assert(selected != null),
assert(selectedFontSize != null && selectedFontSize >= 0),
assert(unselectedFontSize != null && unselectedFontSize >= 0);
assert(selectedLabelStyle != null),
assert(unselectedLabelStyle != null);
final BottomNavigationBarType type;
final BottomNavigationBarItem item;
@ -334,8 +385,10 @@ class _BottomNavigationTile extends StatelessWidget {
final ColorTween colorTween;
final double flex;
final bool selected;
final double selectedFontSize;
final double unselectedFontSize;
final IconThemeData selectedIconTheme;
final IconThemeData unselectedIconTheme;
final TextStyle selectedLabelStyle;
final TextStyle unselectedLabelStyle;
final String indexLabel;
final bool showSelectedLabels;
final bool showUnselectedLabels;
@ -348,41 +401,63 @@ class _BottomNavigationTile extends StatelessWidget {
// (which is an integer) by a large number.
int size;
double bottomPadding = selectedFontSize / 2.0;
double topPadding = selectedFontSize / 2.0;
final double selectedFontSize = selectedLabelStyle.fontSize;
final double selectedIconSize = selectedIconTheme?.size ?? iconSize;
final double unselectedIconSize = unselectedIconTheme?.size ?? iconSize;
// The amount that the selected icon is bigger than the unselected icons,
// (or zero if the selected icon is not bigger than the unselected icons).
final double selectedIconDiff = math.max(selectedIconSize - unselectedIconSize, 0);
// The amount that the unselected icons are bigger than the selected icon,
// (or zero if the unselected icons are not any bigger than the selected icon).
final double unselectedIconDiff = math.max(unselectedIconSize - selectedIconSize, 0);
// Defines the padding for the animating icons + labels.
//
// The animations go from "Unselected":
// =======
// | <-- Padding equal to the text height.
// | <-- Padding equal to the text height + 1/2 selectedIconDiff.
// |
// | text <-- Invisible text.
// | text <-- Invisible text + padding equal to 1/2 selectedIconDiff.
// =======
//
// To "Selected":
//
// =======
// | <-- Padding equal to 1/2 text height.
// | <-- Padding equal to 1/2 text height + 1/2 unselectedIconDiff.
// |
// | text
// | <-- Padding equal to 1/2 text height.
// | <-- Padding equal to 1/2 text height + 1/2 unselectedIconDiff.
// =======
double bottomPadding;
double topPadding;
if (showSelectedLabels && !showUnselectedLabels) {
bottomPadding = Tween<double>(
begin: 0.0,
end: selectedFontSize / 2.0,
begin: selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0,
).evaluate(animation);
topPadding = Tween<double>(
begin: selectedFontSize,
end: selectedFontSize / 2.0,
begin: selectedFontSize + selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 - unselectedIconDiff / 2.0,
).evaluate(animation);
} else if (!showSelectedLabels && !showUnselectedLabels) {
bottomPadding = Tween<double>(
begin: selectedIconDiff / 2.0,
end: unselectedIconDiff / 2.0,
).evaluate(animation);
topPadding = Tween<double>(
begin: selectedFontSize + selectedIconDiff / 2.0,
end: selectedFontSize + unselectedIconDiff / 2.0,
).evaluate(animation);
} else {
bottomPadding = Tween<double>(
begin: selectedFontSize / 2.0 + selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 + unselectedIconDiff / 2.0,
).evaluate(animation);
topPadding = Tween<double>(
begin: selectedFontSize / 2.0 + selectedIconDiff / 2.0,
end: selectedFontSize / 2.0 + unselectedIconDiff / 2.0,
).evaluate(animation);
}
// Center all icons if no labels are shown.
if (!showSelectedLabels && !showUnselectedLabels) {
bottomPadding = 0.0;
topPadding = selectedFontSize;
}
switch (type) {
@ -417,13 +492,15 @@ class _BottomNavigationTile extends StatelessWidget {
iconSize: iconSize,
selected: selected,
item: item,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
),
_Label(
colorTween: colorTween,
animation: animation,
item: item,
selectedFontSize: selectedFontSize,
unselectedFontSize: unselectedFontSize,
selectedLabelStyle: selectedLabelStyle,
unselectedLabelStyle: unselectedLabelStyle,
showSelectedLabels: showSelectedLabels,
showUnselectedLabels: showUnselectedLabels,
),
@ -450,6 +527,8 @@ class _TileIcon extends StatelessWidget {
@required this.iconSize,
@required this.selected,
@required this.item,
@required this.selectedIconTheme,
@required this.unselectedIconTheme,
}) : assert(selected != null),
assert(item != null),
super(key: key);
@ -459,19 +538,28 @@ class _TileIcon extends StatelessWidget {
final double iconSize;
final bool selected;
final BottomNavigationBarItem item;
final IconThemeData selectedIconTheme;
final IconThemeData unselectedIconTheme;
@override
Widget build(BuildContext context) {
final Color iconColor = colorTween.evaluate(animation);
final IconThemeData defaultIconTheme = IconThemeData(
color: iconColor,
size: iconSize,
);
final IconThemeData iconThemeData = IconThemeData.lerp(
defaultIconTheme.merge(unselectedIconTheme),
defaultIconTheme.merge(selectedIconTheme),
animation.value,
);
return Align(
alignment: Alignment.topCenter,
heightFactor: 1.0,
child: Container(
child: IconTheme(
data: IconThemeData(
color: iconColor,
size: iconSize,
),
data: iconThemeData,
child: selected ? item.activeIcon : item.icon,
),
),
@ -485,15 +573,15 @@ class _Label extends StatelessWidget {
@required this.colorTween,
@required this.animation,
@required this.item,
@required this.selectedFontSize,
@required this.unselectedFontSize,
@required this.selectedLabelStyle,
@required this.unselectedLabelStyle,
@required this.showSelectedLabels,
@required this.showUnselectedLabels,
}) : assert(colorTween != null),
assert(animation != null),
assert(item != null),
assert(selectedFontSize != null),
assert(unselectedFontSize != null),
assert(selectedLabelStyle != null),
assert(unselectedLabelStyle != null),
assert(showSelectedLabels != null),
assert(showUnselectedLabels != null),
super(key: key);
@ -501,15 +589,23 @@ class _Label extends StatelessWidget {
final ColorTween colorTween;
final Animation<double> animation;
final BottomNavigationBarItem item;
final double selectedFontSize;
final double unselectedFontSize;
final TextStyle selectedLabelStyle;
final TextStyle unselectedLabelStyle;
final bool showSelectedLabels;
final bool showUnselectedLabels;
@override
Widget build(BuildContext context) {
final double selectedFontSize = selectedLabelStyle.fontSize;
final double unselectedFontSize = unselectedLabelStyle.fontSize;
final TextStyle customStyle = TextStyle.lerp(
unselectedLabelStyle,
selectedLabelStyle,
animation.value,
);
Widget text = DefaultTextStyle.merge(
style: TextStyle(
style: customStyle.copyWith(
fontSize: selectedFontSize,
color: colorTween.evaluate(animation),
),
@ -677,12 +773,25 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
}
}
// If the given [TextStyle] has a non-null `fontSize`, it should be used.
// Otherwise, the [selectedFontSize] parameter should be used.
static TextStyle _effectiveTextStyle(TextStyle textStyle, double fontSize) {
textStyle ??= const TextStyle(inherit: false);
// Prefer the font size on textStyle if present.
return textStyle.fontSize == null ? textStyle.copyWith(fontSize: fontSize) : textStyle;
}
List<Widget> _createTiles() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
assert(localizations != null);
final ThemeData themeData = Theme.of(context);
final TextStyle effectiveSelectedLabelStyle =
_effectiveTextStyle(widget.selectedLabelStyle, widget.selectedFontSize);
final TextStyle effectiveUnselectedLabelStyle =
_effectiveTextStyle(widget.unselectedLabelStyle, widget.unselectedFontSize);
Color themeColor;
switch (themeData.brightness) {
case Brightness.light:
@ -716,8 +825,10 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
widget.items[i],
_animations[i],
widget.iconSize,
selectedFontSize: widget.selectedFontSize,
unselectedFontSize: widget.unselectedFontSize,
selectedIconTheme: widget.selectedIconTheme,
unselectedIconTheme: widget.unselectedIconTheme,
selectedLabelStyle: effectiveSelectedLabelStyle,
unselectedLabelStyle: effectiveUnselectedLabelStyle,
onTap: () {
if (widget.onTap != null)
widget.onTap(i);

View file

@ -71,8 +71,8 @@ void main() {
});
testWidgets('Fixed BottomNavigationBar defaults', (WidgetTester tester) async {
const Color primaryColor = Colors.black;
const Color captionColor = Colors.purple;
const Color primaryColor = Color(0xFF000001);
const Color captionColor = Color(0xFF000002);
await tester.pumpWidget(
MaterialApp(
@ -100,19 +100,306 @@ void main() {
const double selectedFontSize = 14.0;
const double unselectedFontSize = 12.0;
expect(tester.renderObject<RenderParagraph>(find.text('AC')).text.style.fontSize, selectedFontSize);
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, isNull);
expect(selectedFontStyle.height, isNull);
expect(unselectedFontStyle.color, equals(captionColor));
expect(unselectedFontStyle.fontWeight, isNull);
expect(unselectedFontStyle.height, isNull);
// Unselected label has a font size of 14 but is scaled down to be font size 12.
expect(tester.renderObject<RenderParagraph>(find.text('Alarm')).text.style.fontSize, selectedFontSize);
expect(
tester.firstWidget<Transform>(find.ancestor(of: find.text('Alarm'), matching: find.byType(Transform))).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedFontSize / selectedFontSize))),
);
expect(tester.renderObject<RenderParagraph>(find.text('AC')).text.style.color, equals(primaryColor));
expect(tester.renderObject<RenderParagraph>(find.text('Alarm')).text.style.color, equals(captionColor));
expect(selectedIcon.color, equals(primaryColor));
expect(selectedIcon.fontSize, equals(24.0));
expect(unselectedIcon.color, equals(captionColor));
expect(unselectedIcon.fontSize, equals(24.0));
expect(_getOpacity(tester, 'Alarm'), equals(1.0));
expect(_getMaterial(tester).elevation, equals(8.0));
});
testWidgets('Custom selected and unselected font styles', (WidgetTester tester) async {
const TextStyle selectedTextStyle = TextStyle(fontWeight: FontWeight.w200, fontSize: 18.0);
const TextStyle unselectedTextStyle = TextStyle(fontWeight: FontWeight.w600, fontSize: 12.0);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedLabelStyle: selectedTextStyle,
unselectedLabelStyle: unselectedTextStyle,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final TextStyle selectedFontStyle = tester.renderObject<RenderParagraph>(find.text('AC')).text.style;
final TextStyle unselectedFontStyle = tester.renderObject<RenderParagraph>(find.text('Alarm')).text.style;
expect(selectedFontStyle.fontSize, equals(selectedTextStyle.fontSize));
expect(selectedFontStyle.fontWeight, equals(selectedTextStyle.fontWeight));
expect(
tester.firstWidget<Transform>(find.ancestor(of: find.text('Alarm'), matching: find.byType(Transform))).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedTextStyle.fontSize / selectedTextStyle.fontSize))),
);
expect(unselectedFontStyle.fontWeight, equals(unselectedTextStyle.fontWeight));
});
testWidgets('font size on text styles overrides font size params', (WidgetTester tester) async {
const TextStyle selectedTextStyle = TextStyle(fontSize: 18.0);
const TextStyle unselectedTextStyle = TextStyle(fontSize: 12.0);
const double selectedFontSize = 17.0;
const double unselectedFontSize = 11.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedLabelStyle: selectedTextStyle,
unselectedLabelStyle: unselectedTextStyle,
selectedFontSize: selectedFontSize,
unselectedFontSize: unselectedFontSize,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final TextStyle selectedFontStyle = tester.renderObject<RenderParagraph>(find.text('AC')).text.style;
expect(selectedFontStyle.fontSize, equals(selectedTextStyle.fontSize));
expect(
tester.firstWidget<Transform>(find.ancestor(of: find.text('Alarm'), matching: find.byType(Transform))).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedTextStyle.fontSize / selectedTextStyle.fontSize))),
);
});
testWidgets('Custom selected and unselected icon themes', (WidgetTester tester) async {
const IconThemeData selectedIconTheme = IconThemeData(size: 36, color: Color(1));
const IconThemeData unselectedIconTheme = IconThemeData(size: 18, color: Color(2));
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final TextStyle selectedIcon = _iconStyle(tester, Icons.ac_unit);
final TextStyle unselectedIcon = _iconStyle(tester, Icons.access_alarm);
expect(selectedIcon.color, equals(selectedIconTheme.color));
expect(selectedIcon.fontSize, equals(selectedIconTheme.size));
expect(unselectedIcon.color, equals(unselectedIconTheme.color));
expect(unselectedIcon.fontSize, equals(unselectedIconTheme.size));
});
testWidgets('color on icon theme overrides selected and unselected item colors', (WidgetTester tester) async {
const IconThemeData selectedIconTheme = IconThemeData(size: 36, color: Color(1));
const IconThemeData unselectedIconTheme = IconThemeData(size: 18, color: Color(2));
const Color selectedItemColor = Color(3);
const Color unselectedItemColor = Color(4);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
selectedItemColor: selectedItemColor,
unselectedItemColor: unselectedItemColor,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
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(selectedIcon.color, equals(selectedIconTheme.color));
expect(unselectedIcon.color, equals(unselectedIconTheme.color));
expect(selectedFontStyle.color, equals(selectedItemColor));
expect(unselectedFontStyle.color, equals(unselectedItemColor));
});
testWidgets('Padding is calculated properly on items - all labels', (WidgetTester tester) async {
const double selectedFontSize = 16.0;
const double unselectedFontSize = 12.0;
const double selectedIconSize = 36.0;
const double unselectedIconSize = 20.0;
const IconThemeData selectedIconTheme = IconThemeData(size: selectedIconSize);
const IconThemeData unselectedIconTheme = IconThemeData(size: unselectedIconSize);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
showSelectedLabels: true,
showUnselectedLabels: true,
selectedFontSize: selectedFontSize,
unselectedFontSize: unselectedFontSize,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final EdgeInsets selectedItemPadding = _itemPadding(tester, Icons.ac_unit);
expect(selectedItemPadding.top, equals(selectedFontSize / 2.0));
expect(selectedItemPadding.bottom, equals(selectedFontSize / 2.0));
final EdgeInsets unselectedItemPadding = _itemPadding(tester, Icons.access_alarm);
const double expectedUnselectedPadding = (selectedIconSize - unselectedIconSize) / 2.0 + selectedFontSize / 2.0;
expect(unselectedItemPadding.top, equals(expectedUnselectedPadding));
expect(unselectedItemPadding.bottom, equals(expectedUnselectedPadding));
});
testWidgets('Padding is calculated properly on items - selected labels only', (WidgetTester tester) async {
const double selectedFontSize = 16.0;
const double unselectedFontSize = 12.0;
const double selectedIconSize = 36.0;
const double unselectedIconSize = 20.0;
const IconThemeData selectedIconTheme = IconThemeData(size: selectedIconSize);
const IconThemeData unselectedIconTheme = IconThemeData(size: unselectedIconSize);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
showSelectedLabels: true,
showUnselectedLabels: false,
selectedFontSize: selectedFontSize,
unselectedFontSize: unselectedFontSize,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final EdgeInsets selectedItemPadding = _itemPadding(tester, Icons.ac_unit);
expect(selectedItemPadding.top, equals(selectedFontSize / 2.0));
expect(selectedItemPadding.bottom, equals(selectedFontSize / 2.0));
final EdgeInsets unselectedItemPadding = _itemPadding(tester, Icons.access_alarm);
expect(unselectedItemPadding.top, equals((selectedIconSize - unselectedIconSize) / 2.0 + selectedFontSize));
expect(unselectedItemPadding.bottom, equals((selectedIconSize - unselectedIconSize) / 2.0));
});
testWidgets('Padding is calculated properly on items - no labels', (WidgetTester tester) async {
const double selectedFontSize = 16.0;
const double unselectedFontSize = 12.0;
const double selectedIconSize = 36.0;
const double unselectedIconSize = 20.0;
const IconThemeData selectedIconTheme = IconThemeData(size: selectedIconSize);
const IconThemeData unselectedIconTheme = IconThemeData(size: unselectedIconSize);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
showSelectedLabels: false,
showUnselectedLabels: false,
selectedFontSize: selectedFontSize,
unselectedFontSize: unselectedFontSize,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
title: Text('AC'),
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
title: Text('Alarm'),
),
],
),
),
)
);
final EdgeInsets selectedItemPadding = _itemPadding(tester, Icons.ac_unit);
expect(selectedItemPadding.top, equals(selectedFontSize));
expect(selectedItemPadding.bottom, equals(0.0));
final EdgeInsets unselectedItemPadding = _itemPadding(tester, Icons.access_alarm);
expect(unselectedItemPadding.top, equals((selectedIconSize - unselectedIconSize) / 2.0 + selectedFontSize));
expect(unselectedItemPadding.bottom, equals((selectedIconSize - unselectedIconSize) / 2.0));
});
testWidgets('Shifting BottomNavigationBar defaults', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
@ -1399,3 +1686,19 @@ Material _getMaterial(WidgetTester tester) {
find.descendant(of: find.byType(BottomNavigationBar), matching: find.byType(Material)),
);
}
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
final RichText iconRichText = tester.widget<RichText>(
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
);
return iconRichText.text.style;
}
EdgeInsets _itemPadding(WidgetTester tester, IconData icon) {
return tester.widget<Padding>(
find.descendant(
of: find.ancestor(of: find.byIcon(icon), matching: find.byType(InkResponse)),
matching: find.byType(Padding)
).first,
).padding.resolve(TextDirection.ltr);
}