Fix TabBar
tab icons not respecting custom IconTheme
(#157724)
Fixes [TabBar ignores Theme's iconTheme.size](https://github.com/flutter/flutter/issues/155518) ### Description When `ThemeData.IconTheme` with color and size, is provided, it can override the default `Tab` icon color and size. ### Code sample <details> <summary>expand to view the code sample</summary> ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( iconTheme: const IconThemeData(color: Colors.amber, size: 40), ), home: const Scaffold( body: SafeArea( child: DefaultTabController( length: 2, child: Column( children: [ TabBar( tabs: [ Tab(icon: Icon(Icons.backpack_outlined), text: 'Backpack'), Tab(icon: Icon(Icons.map_outlined), text: 'Map'), ], overlayColor: WidgetStatePropertyAll(Colors.transparent), ), Expanded( child: TabBarView( children: [ Icon(Icons.backpack_outlined), Icon(Icons.map_outlined), ], ), ), ], ), ), ), ), ); } } ``` </details> --- <img width="666" alt="Screenshot 2024-10-28 at 16 25 06" src="https://github.com/user-attachments/assets/56d31115-7ee9-4378-9811-75a3a2a4ce0f"> <img width="666" alt="Screenshot 2024-10-28 at 16 24 52" src="https://github.com/user-attachments/assets/ab8a3e4b-e912-40ac-b249-6358492581e0">
This commit is contained in:
parent
03c405a43f
commit
97596e5895
@ -269,7 +269,10 @@ class _TabStyle extends AnimatedWidget {
|
||||
final TabBarThemeData defaults;
|
||||
final Widget child;
|
||||
|
||||
MaterialStateColor _resolveWithLabelColor(BuildContext context) {
|
||||
MaterialStateColor _resolveWithLabelColor(
|
||||
BuildContext context, {
|
||||
IconThemeData? iconTheme,
|
||||
}) {
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final TabBarThemeData tabBarTheme = TabBarTheme.of(context);
|
||||
final Animation<double> animation = listenable as Animation<double>;
|
||||
@ -295,6 +298,7 @@ class _TabStyle extends AnimatedWidget {
|
||||
?? tabBarTheme.unselectedLabelColor
|
||||
?? unselectedLabelStyle?.color
|
||||
?? tabBarTheme.unselectedLabelStyle?.color
|
||||
?? iconTheme?.color
|
||||
?? (themeData.useMaterial3
|
||||
? defaults.unselectedLabelColor!
|
||||
: selectedColor.withAlpha(0xB2)); // 70% alpha
|
||||
@ -310,6 +314,7 @@ class _TabStyle extends AnimatedWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final TabBarThemeData tabBarTheme = TabBarTheme.of(context);
|
||||
final Animation<double> animation = listenable as Animation<double>;
|
||||
|
||||
@ -331,14 +336,23 @@ class _TabStyle extends AnimatedWidget {
|
||||
final TextStyle textStyle = isSelected
|
||||
? TextStyle.lerp(selectedStyle, unselectedStyle, animation.value)!
|
||||
: TextStyle.lerp(unselectedStyle, selectedStyle, animation.value)!;
|
||||
final Color color = _resolveWithLabelColor(context).resolve(states);
|
||||
final Color defaultIconColor = switch (theme.colorScheme.brightness) {
|
||||
Brightness.light => kDefaultIconDarkColor,
|
||||
Brightness.dark => kDefaultIconLightColor,
|
||||
};
|
||||
final IconThemeData? customIconTheme = switch (IconTheme.of(context)) {
|
||||
final IconThemeData iconTheme when iconTheme.color != defaultIconColor => iconTheme,
|
||||
_ => null,
|
||||
};
|
||||
final Color iconColor = _resolveWithLabelColor(context, iconTheme: customIconTheme).resolve(states);
|
||||
final Color labelColor = _resolveWithLabelColor(context).resolve(states);
|
||||
|
||||
return DefaultTextStyle(
|
||||
style: textStyle.copyWith(color: color),
|
||||
style: textStyle.copyWith(color: labelColor),
|
||||
child: IconTheme.merge(
|
||||
data: IconThemeData(
|
||||
size: 24.0,
|
||||
color: color,
|
||||
size: customIconTheme?.size ?? 24.0,
|
||||
color: iconColor,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
|
@ -7464,4 +7464,56 @@ void main() {
|
||||
targetRect = const Rect.fromLTRB(275.0, 0.0, 325.0, 48.0);
|
||||
expectIndicatorAttrs(tabBarBox, rect: rect, targetRect: targetRect);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/155518.
|
||||
testWidgets('Tabs icon respects ambient icon theme', (WidgetTester tester) async {
|
||||
final ThemeData theme = ThemeData(
|
||||
iconTheme: const IconThemeData(
|
||||
color: Color(0xffff0000),
|
||||
size: 38.0,
|
||||
),
|
||||
);
|
||||
const IconData selectedIcon = Icons.ac_unit;
|
||||
const IconData unselectedIcon = Icons.access_alarm;
|
||||
await tester.pumpWidget(boilerplate(
|
||||
theme: theme,
|
||||
child: const DefaultTabController(
|
||||
length: 2,
|
||||
child: TabBar(
|
||||
tabs: <Widget>[
|
||||
Tab(
|
||||
icon: Icon(selectedIcon),
|
||||
text: 'Tab 1',
|
||||
),
|
||||
Tab(
|
||||
icon: Icon(unselectedIcon),
|
||||
text: 'Tab 2',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
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!;
|
||||
}
|
||||
|
||||
// The iconTheme color isn't applied to the selected icon.
|
||||
expect(iconStyle(tester, selectedIcon).color, equals(theme.colorScheme.primary));
|
||||
// The iconTheme color is applied to the unselected icon.
|
||||
expect(iconStyle(tester, unselectedIcon).color, equals(theme.iconTheme.color));
|
||||
|
||||
// Both selected and unselected icons should have the iconTheme size.
|
||||
expect(
|
||||
tester.getSize(find.byIcon(selectedIcon)),
|
||||
Size(theme.iconTheme.size!, theme.iconTheme.size!),
|
||||
);
|
||||
expect(
|
||||
tester.getSize(find.byIcon(unselectedIcon)),
|
||||
Size(theme.iconTheme.size!, theme.iconTheme.size!),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user