diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart index ce9e035398c2..de9b2b894473 100644 --- a/dev/tools/gen_defaults/bin/gen_defaults.dart +++ b/dev/tools/gen_defaults/bin/gen_defaults.dart @@ -46,7 +46,6 @@ import 'package:gen_defaults/segmented_button_template.dart'; import 'package:gen_defaults/slider_template.dart'; import 'package:gen_defaults/surface_tint.dart'; import 'package:gen_defaults/switch_template.dart'; -import 'package:gen_defaults/tabs_template.dart'; import 'package:gen_defaults/text_field_template.dart'; import 'package:gen_defaults/typography_template.dart'; @@ -166,6 +165,5 @@ Future main(List args) async { SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile(); SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile(); TextFieldTemplate('TextField', '$materialLib/text_field.dart', tokens).updateFile(); - TabsTemplate('Tabs', '$materialLib/tabs.dart', tokens).updateFile(); TypographyTemplate('Typography', '$materialLib/typography.dart', tokens).updateFile(); } diff --git a/dev/tools/gen_defaults/lib/tabs_template.dart b/dev/tools/gen_defaults/lib/tabs_template.dart deleted file mode 100644 index a901b4126289..000000000000 --- a/dev/tools/gen_defaults/lib/tabs_template.dart +++ /dev/null @@ -1,73 +0,0 @@ -// 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. - -import 'template.dart'; - -class TabsTemplate extends TokenTemplate { - const TabsTemplate(super.blockName, super.fileName, super.tokens, { - super.colorSchemePrefix = '_colors.', - super.textThemePrefix = '_textTheme.', - }); - - @override - String generate() => ''' -class _${blockName}DefaultsM3 extends TabBarTheme { - _${blockName}DefaultsM3(this.context) - : super(indicatorSize: TabBarIndicatorSize.label); - - final BuildContext context; - late final ColorScheme _colors = Theme.of(context).colorScheme; - late final TextTheme _textTheme = Theme.of(context).textTheme; - - @override - Color? get dividerColor => ${componentColor("md.comp.primary-navigation-tab.divider")}; - - @override - Color? get indicatorColor => ${componentColor("md.comp.primary-navigation-tab.active-indicator")}; - - @override - Color? get labelColor => ${componentColor("md.comp.primary-navigation-tab.with-label-text.active.label-text")}; - - @override - TextStyle? get labelStyle => ${textStyle("md.comp.primary-navigation-tab.with-label-text.label-text")}; - - @override - Color? get unselectedLabelColor => ${componentColor("md.comp.primary-navigation-tab.with-label-text.inactive.label-text")}; - - @override - TextStyle? get unselectedLabelStyle => ${textStyle("md.comp.primary-navigation-tab.with-label-text.label-text")}; - - @override - MaterialStateProperty get overlayColor { - return MaterialStateProperty.resolveWith((Set states) { - if (states.contains(MaterialState.selected)) { - if (states.contains(MaterialState.hovered)) { - return ${componentColor('md.comp.primary-navigation-tab.active.hover.state-layer')}; - } - if (states.contains(MaterialState.focused)) { - return ${componentColor('md.comp.primary-navigation-tab.active.focus.state-layer')}; - } - if (states.contains(MaterialState.pressed)) { - return ${componentColor('md.comp.primary-navigation-tab.active.pressed.state-layer')}; - } - return null; - } - if (states.contains(MaterialState.hovered)) { - return ${componentColor('md.comp.primary-navigation-tab.inactive.hover.state-layer')}; - } - if (states.contains(MaterialState.focused)) { - return ${componentColor('md.comp.primary-navigation-tab.inactive.focus.state-layer')}; - } - if (states.contains(MaterialState.pressed)) { - return ${componentColor('md.comp.primary-navigation-tab.inactive.pressed.state-layer')}; - } - return null; - }); - } - - @override - InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory; -} -'''; -} diff --git a/packages/flutter/lib/src/material/tab_bar_theme.dart b/packages/flutter/lib/src/material/tab_bar_theme.dart index ee6bc4077e88..70ea0e0e2219 100644 --- a/packages/flutter/lib/src/material/tab_bar_theme.dart +++ b/packages/flutter/lib/src/material/tab_bar_theme.dart @@ -29,9 +29,7 @@ class TabBarTheme with Diagnosticable { /// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme]. const TabBarTheme({ this.indicator, - this.indicatorColor, this.indicatorSize, - this.dividerColor, this.labelColor, this.labelPadding, this.labelStyle, @@ -45,15 +43,9 @@ class TabBarTheme with Diagnosticable { /// Overrides the default value for [TabBar.indicator]. final Decoration? indicator; - /// Overrides the default value for [TabBar.indicatorColor]. - final Color? indicatorColor; - /// Overrides the default value for [TabBar.indicatorSize]. final TabBarIndicatorSize? indicatorSize; - /// Overrides the default value for [TabBar.dividerColor]. - final Color? dividerColor; - /// Overrides the default value for [TabBar.labelColor]. final Color? labelColor; @@ -88,9 +80,7 @@ class TabBarTheme with Diagnosticable { /// new values. TabBarTheme copyWith({ Decoration? indicator, - Color? indicatorColor, TabBarIndicatorSize? indicatorSize, - Color? dividerColor, Color? labelColor, EdgeInsetsGeometry? labelPadding, TextStyle? labelStyle, @@ -102,9 +92,7 @@ class TabBarTheme with Diagnosticable { }) { return TabBarTheme( indicator: indicator ?? this.indicator, - indicatorColor: indicatorColor ?? this.indicatorColor, indicatorSize: indicatorSize ?? this.indicatorSize, - dividerColor: dividerColor ?? this.dividerColor, labelColor: labelColor ?? this.labelColor, labelPadding: labelPadding ?? this.labelPadding, labelStyle: labelStyle ?? this.labelStyle, @@ -132,15 +120,13 @@ class TabBarTheme with Diagnosticable { assert(t != null); return TabBarTheme( indicator: Decoration.lerp(a.indicator, b.indicator, t), - indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t), indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize, - dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t), labelColor: Color.lerp(a.labelColor, b.labelColor, t), labelPadding: EdgeInsetsGeometry.lerp(a.labelPadding, b.labelPadding, t), labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t), unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t), unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t), - overlayColor: MaterialStateProperty.lerp(a.overlayColor, b.overlayColor, t, Color.lerp), + overlayColor: _LerpColors(a.overlayColor, b.overlayColor, t), splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory, mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor, ); @@ -149,9 +135,7 @@ class TabBarTheme with Diagnosticable { @override int get hashCode => Object.hash( indicator, - indicatorColor, indicatorSize, - dividerColor, labelColor, labelPadding, labelStyle, @@ -172,9 +156,7 @@ class TabBarTheme with Diagnosticable { } return other is TabBarTheme && other.indicator == indicator - && other.indicatorColor == indicatorColor && other.indicatorSize == indicatorSize - && other.dividerColor == dividerColor && other.labelColor == labelColor && other.labelPadding == labelPadding && other.labelStyle == labelStyle @@ -185,3 +167,39 @@ class TabBarTheme with Diagnosticable { && other.mouseCursor == mouseCursor; } } + + +@immutable +class _LerpColors implements MaterialStateProperty { + const _LerpColors(this.a, this.b, this.t); + + final MaterialStateProperty? a; + final MaterialStateProperty? b; + final double t; + + @override + Color? resolve(Set states) { + final Color? resolvedA = a?.resolve(states); + final Color? resolvedB = b?.resolve(states); + return Color.lerp(resolvedA, resolvedB, t); + } + + @override + int get hashCode { + return Object.hash(a, b, t); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is _LerpColors + && other.a == a + && other.b == b + && other.t == t; + } +} diff --git a/packages/flutter/lib/src/material/tab_indicator.dart b/packages/flutter/lib/src/material/tab_indicator.dart index 276dc8429957..313a55a7e40f 100644 --- a/packages/flutter/lib/src/material/tab_indicator.dart +++ b/packages/flutter/lib/src/material/tab_indicator.dart @@ -20,18 +20,11 @@ class UnderlineTabIndicator extends Decoration { /// /// The [borderSide] and [insets] arguments must not be null. const UnderlineTabIndicator({ - this.borderRadius, this.borderSide = const BorderSide(width: 2.0, color: Colors.white), this.insets = EdgeInsets.zero, }) : assert(borderSide != null), assert(insets != null); - /// The radius of the indicator's corners. - /// - /// If this value is non-null, rounded rectangular tab indicator is - /// drawn, otherwise rectangular tab indictor is drawn. - final BorderRadius? borderRadius; - /// The color and weight of the horizontal line drawn below the selected tab. final BorderSide borderSide; @@ -67,7 +60,7 @@ class UnderlineTabIndicator extends Decoration { @override BoxPainter createBoxPainter([ VoidCallback? onChanged ]) { - return _UnderlinePainter(this, borderRadius, onChanged); + return _UnderlinePainter(this, onChanged); } Rect _indicatorRectFor(Rect rect, TextDirection textDirection) { @@ -84,25 +77,15 @@ class UnderlineTabIndicator extends Decoration { @override Path getClipPath(Rect rect, TextDirection textDirection) { - if (borderRadius != null) { - return Path()..addRRect( - borderRadius!.toRRect(_indicatorRectFor(rect, textDirection)) - ); - } return Path()..addRect(_indicatorRectFor(rect, textDirection)); } } class _UnderlinePainter extends BoxPainter { - _UnderlinePainter( - this.decoration, - this.borderRadius, - super.onChanged, - ) + _UnderlinePainter(this.decoration, super.onChanged) : assert(decoration != null); final UnderlineTabIndicator decoration; - final BorderRadius? borderRadius; @override void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { @@ -110,24 +93,8 @@ class _UnderlinePainter extends BoxPainter { assert(configuration.size != null); final Rect rect = offset & configuration.size!; final TextDirection textDirection = configuration.textDirection!; - final Paint paint; - if (borderRadius != null) { - paint = Paint()..color = decoration.borderSide.color; - final Rect indicator = decoration._indicatorRectFor(rect, textDirection) - .inflate(decoration.borderSide.width / 4.0); - final RRect rrect = RRect.fromRectAndCorners( - indicator, - topLeft: borderRadius!.topLeft, - topRight: borderRadius!.topRight, - bottomRight: borderRadius!.bottomRight, - bottomLeft: borderRadius!.bottomLeft, - ); - canvas.drawRRect(rrect, paint); - } else { - paint = decoration.borderSide.toPaint()..strokeCap = StrokeCap.square; - final Rect indicator = decoration._indicatorRectFor(rect, textDirection) - .deflate(decoration.borderSide.width / 2.0); - canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint); - } + final Rect indicator = decoration._indicatorRectFor(rect, textDirection).deflate(decoration.borderSide.width / 2.0); + final Paint paint = decoration.borderSide.toPaint()..strokeCap = StrokeCap.square; + canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint); } } diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 0599cf31d395..c7b51235cef6 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -11,7 +11,6 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'app_bar.dart'; -import 'color_scheme.dart'; import 'colors.dart'; import 'constants.dart'; import 'debug.dart'; @@ -22,7 +21,6 @@ import 'material_state.dart'; import 'tab_bar_theme.dart'; import 'tab_controller.dart'; import 'tab_indicator.dart'; -import 'text_theme.dart'; import 'theme.dart'; const double _kTabHeight = 46.0; @@ -185,19 +183,18 @@ class _TabStyle extends AnimatedWidget { Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context); final TabBarTheme tabBarTheme = TabBarTheme.of(context); - final TabBarTheme defaults = themeData.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context); final Animation animation = listenable as Animation; // To enable TextStyle.lerp(style1, style2, value), both styles must have // the same value of inherit. Force that to be inherit=true here. final TextStyle defaultStyle = (labelStyle ?? tabBarTheme.labelStyle - ?? defaults.labelStyle! + ?? themeData.primaryTextTheme.bodyLarge! ).copyWith(inherit: true); final TextStyle defaultUnselectedStyle = (unselectedLabelStyle ?? tabBarTheme.unselectedLabelStyle ?? labelStyle - ?? defaults.unselectedLabelStyle! + ?? themeData.primaryTextTheme.bodyLarge! ).copyWith(inherit: true); final TextStyle textStyle = selected ? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value)! @@ -205,10 +202,10 @@ class _TabStyle extends AnimatedWidget { final Color selectedColor = labelColor ?? tabBarTheme.labelColor - ?? defaults.labelColor!; + ?? themeData.primaryTextTheme.bodyLarge!.color!; final Color unselectedColor = unselectedLabelColor ?? tabBarTheme.unselectedLabelColor - ?? defaults.unselectedLabelColor!; + ?? selectedColor.withAlpha(0xB2); // 70% alpha final Color color = selected ? Color.lerp(selectedColor, unselectedColor, animation.value)! : Color.lerp(unselectedColor, selectedColor, animation.value)!; @@ -330,7 +327,6 @@ class _IndicatorPainter extends CustomPainter { required this.tabKeys, required _IndicatorPainter? old, required this.indicatorPadding, - this.dividerColor, }) : assert(controller != null), assert(indicator != null), super(repaint: controller.animation) { @@ -344,7 +340,6 @@ class _IndicatorPainter extends CustomPainter { final TabBarIndicatorSize? indicatorSize; final EdgeInsetsGeometry indicatorPadding; final List tabKeys; - final Color? dividerColor; // _currentTabOffsets and _currentTextDirection are set each time TabBar // layout is completed. These values can be null when TabBar contains no @@ -436,10 +431,6 @@ class _IndicatorPainter extends CustomPainter { size: _currentRect!.size, textDirection: _currentTextDirection, ); - if (dividerColor != null) { - final Paint dividerPaint = Paint()..color = dividerColor!..strokeWidth = 1; - canvas.drawLine(Offset(0, size.height), Offset(size.width, size.height), dividerPaint); - } _painter!.paint(canvas, _currentRect!.topLeft, configuration); } @@ -639,7 +630,6 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { this.indicatorPadding = EdgeInsets.zero, this.indicator, this.indicatorSize, - this.dividerColor, this.labelColor, this.labelStyle, this.labelPadding, @@ -754,13 +744,6 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { /// [indicator] properties. final TabBarIndicatorSize? indicatorSize; - /// The color of the divider. - /// - /// If null and [ThemeData.useMaterial3] is true, [TabBarTheme.dividerColor] - /// color is used. If that is null and [ThemeData.useMaterial3] is true, - /// [ColorScheme.surfaceVariant] will be used, otherwise divider will not be drawn. - final Color? dividerColor; - /// The color of selected tab labels. /// /// Unselected tab labels are rendered with the same color rendered at 70% @@ -956,22 +939,16 @@ class _TabBarState extends State { _tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList(); } - Decoration _getIndicator() { - final ThemeData theme = Theme.of(context); - final TabBarTheme tabBarTheme = TabBarTheme.of(context); - final TabBarTheme defaults = theme.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context); - + Decoration get _indicator { if (widget.indicator != null) { return widget.indicator!; } + final TabBarTheme tabBarTheme = TabBarTheme.of(context); if (tabBarTheme.indicator != null) { return tabBarTheme.indicator!; } - Color color = widget.indicatorColor - ?? (theme.useMaterial3 - ? tabBarTheme.indicatorColor ?? defaults.indicatorColor! - : Theme.of(context).indicatorColor); + Color color = widget.indicatorColor ?? Theme.of(context).indicatorColor; // ThemeData tries to avoid this by having indicatorColor avoid being the // primaryColor. However, it's possible that the tab bar is on a // Material that isn't the primaryColor. In that case, if the indicator @@ -991,16 +968,6 @@ class _TabBarState extends State { } return UnderlineTabIndicator( - borderRadius: theme.useMaterial3 - // TODO(tahatesser): Make sure this value matches Material 3 Tabs spec - // when `preferredSize`and `indicatorWeight` are updated to support Material 3 - // https://m3.material.io/components/tabs/specs#149a189f-9039-4195-99da-15c205d20e30, - // https://github.com/flutter/flutter/issues/116136 - ? const BorderRadius.only( - topLeft: Radius.circular(3.0), - topRight: Radius.circular(3.0), - ) - : null, borderSide: BorderSide( width: widget.indicatorWeight, color: color, @@ -1045,18 +1012,13 @@ class _TabBarState extends State { } void _initIndicatorPainter() { - final ThemeData theme = Theme.of(context); - final TabBarTheme tabBarTheme = TabBarTheme.of(context); - final TabBarTheme defaults = theme.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context); - _indicatorPainter = !_controllerIsValid ? null : _IndicatorPainter( controller: _controller!, - indicator: _getIndicator(), - indicatorSize: widget.indicatorSize ?? tabBarTheme.indicatorSize ?? defaults.indicatorSize!, + indicator: _indicator, + indicatorSize: widget.indicatorSize ?? TabBarTheme.of(context).indicatorSize, indicatorPadding: widget.indicatorPadding, tabKeys: _tabKeys, old: _indicatorPainter, - dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null, ); } @@ -1248,9 +1210,7 @@ class _TabBarState extends State { ); } - final ThemeData theme = Theme.of(context); final TabBarTheme tabBarTheme = TabBarTheme.of(context); - final TabBarTheme defaults = theme.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context); final List wrappedTabs = List.generate(widget.tabs.length, (int index) { const double verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/2.0; @@ -1315,26 +1275,20 @@ class _TabBarState extends State { // the same share of the tab bar's overall width. final int tabCount = widget.tabs.length; for (int index = 0; index < tabCount; index += 1) { - final Set selectedState = { + final Set states = { if (index == _currentIndex) MaterialState.selected, }; - final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(widget.mouseCursor, selectedState) - ?? tabBarTheme.mouseCursor?.resolve(selectedState) - ?? MaterialStateMouseCursor.clickable.resolve(selectedState); + final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(widget.mouseCursor, states) + ?? tabBarTheme.mouseCursor?.resolve(states) + ?? MaterialStateMouseCursor.clickable.resolve(states); - final MaterialStateProperty defaultOverlay = MaterialStateProperty.resolveWith( - (Set states) { - final Set effectiveStates = selectedState..addAll(states); - return defaults.overlayColor?.resolve(effectiveStates); - }, - ); wrappedTabs[index] = InkWell( mouseCursor: effectiveMouseCursor, onTap: () { _handleTap(index); }, enableFeedback: widget.enableFeedback ?? true, - overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor ?? defaultOverlay, - splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory ?? defaults.splashFactory, + overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor, + splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory, borderRadius: widget.splashBorderRadius, child: Padding( padding: EdgeInsets.only(bottom: widget.indicatorWeight), @@ -1864,99 +1818,3 @@ class TabPageSelector extends StatelessWidget { ); } } - -// Hand coded defaults based on Material Design 2. -class _TabsDefaultsM2 extends TabBarTheme { - _TabsDefaultsM2(this.context) - : super(indicatorSize: TabBarIndicatorSize.tab); - - final BuildContext context; - late final TextTheme _textTheme = Theme.of(context).textTheme; - - @override - Color? get indicatorColor => Theme.of(context).indicatorColor; - - @override - Color? get labelColor => Theme.of(context).primaryTextTheme.bodyLarge!.color!; - - @override - TextStyle? get labelStyle => _textTheme.bodyLarge; - - @override - Color? get unselectedLabelColor => Theme.of(context).primaryTextTheme.bodyLarge!.color!.withAlpha(0xB2); // 70% alpha - - @override - TextStyle? get unselectedLabelStyle => _textTheme.bodyLarge; - - @override - InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory; -} - -// BEGIN GENERATED TOKEN PROPERTIES - Tabs - -// Do not edit by hand. The code between the "BEGIN GENERATED" and -// "END GENERATED" comments are generated from data in the Material -// Design token database by the script: -// dev/tools/gen_defaults/bin/gen_defaults.dart. - -// Token database version: v0_143 - -class _TabsDefaultsM3 extends TabBarTheme { - _TabsDefaultsM3(this.context) - : super(indicatorSize: TabBarIndicatorSize.label); - - final BuildContext context; - late final ColorScheme _colors = Theme.of(context).colorScheme; - late final TextTheme _textTheme = Theme.of(context).textTheme; - - @override - Color? get dividerColor => _colors.surfaceVariant; - - @override - Color? get indicatorColor => _colors.primary; - - @override - Color? get labelColor => _colors.primary; - - @override - TextStyle? get labelStyle => _textTheme.titleSmall; - - @override - Color? get unselectedLabelColor => _colors.onSurfaceVariant; - - @override - TextStyle? get unselectedLabelStyle => _textTheme.titleSmall; - - @override - MaterialStateProperty get overlayColor { - return MaterialStateProperty.resolveWith((Set states) { - if (states.contains(MaterialState.selected)) { - if (states.contains(MaterialState.hovered)) { - return _colors.primary.withOpacity(0.08); - } - if (states.contains(MaterialState.focused)) { - return _colors.primary.withOpacity(0.12); - } - if (states.contains(MaterialState.pressed)) { - return _colors.primary.withOpacity(0.12); - } - return null; - } - if (states.contains(MaterialState.hovered)) { - return _colors.onSurface.withOpacity(0.08); - } - if (states.contains(MaterialState.focused)) { - return _colors.onSurface.withOpacity(0.12); - } - if (states.contains(MaterialState.pressed)) { - return _colors.primary.withOpacity(0.12); - } - return null; - }); - } - - @override - InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory; -} - -// END GENERATED TOKEN PROPERTIES - Tabs diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart index 18a63c4c7e2c..d6a4863f9bf6 100644 --- a/packages/flutter/test/material/tab_bar_theme_test.dart +++ b/packages/flutter/test/material/tab_bar_theme_test.dart @@ -11,8 +11,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../rendering/mock_canvas.dart'; - const String _tab1Text = 'tab 1'; const String _tab2Text = 'tab 2'; const String _tab3Text = 'tab 3'; @@ -34,10 +32,9 @@ Widget _withTheme( TabBarTheme? theme, { List tabs = _tabs, bool isScrollable = false, - bool useMaterial3 = false, }) { return MaterialApp( - theme: ThemeData(tabBarTheme: theme, useMaterial3: useMaterial3), + theme: ThemeData(tabBarTheme: theme), home: Scaffold( body: RepaintBoundary( key: _painterKey, @@ -63,9 +60,7 @@ void main() { expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode); expect(const TabBarTheme().indicator, null); - expect(const TabBarTheme().indicatorColor, null); expect(const TabBarTheme().indicatorSize, null); - expect(const TabBarTheme().dividerColor, null); expect(const TabBarTheme().labelColor, null); expect(const TabBarTheme().labelPadding, null); expect(const TabBarTheme().labelStyle, null); @@ -76,19 +71,18 @@ void main() { expect(const TabBarTheme().mouseCursor, null); }); - testWidgets('Tab bar defaults', (WidgetTester tester) async { + testWidgets('Tab bar defaults - label style and selected/unselected label colors', (WidgetTester tester) async { // tests for the default label color and label styles when tabBarTheme and tabBar do not provide any - await tester.pumpWidget(_withTheme(null, useMaterial3: true)); + await tester.pumpWidget(_withTheme(null)); - final ThemeData theme = ThemeData(useMaterial3: true); final RenderParagraph selectedRenderObject = tester.renderObject(find.text(_tab1Text)); - expect(selectedRenderObject.text.style!.fontFamily, equals(theme.textTheme.titleSmall!.fontFamily)); + expect(selectedRenderObject.text.style!.fontFamily, equals('Roboto')); expect(selectedRenderObject.text.style!.fontSize, equals(14.0)); - expect(selectedRenderObject.text.style!.color, equals(theme.colorScheme.primary)); + expect(selectedRenderObject.text.style!.color, equals(Colors.white)); final RenderParagraph unselectedRenderObject = tester.renderObject(find.text(_tab2Text)); - expect(unselectedRenderObject.text.style!.fontFamily, equals(theme.textTheme.titleSmall!.fontFamily)); + expect(unselectedRenderObject.text.style!.fontFamily, equals('Roboto')); expect(unselectedRenderObject.text.style!.fontSize, equals(14.0)); - expect(unselectedRenderObject.text.style!.color, equals(theme.colorScheme.onSurfaceVariant)); + expect(unselectedRenderObject.text.style!.color, equals(Colors.white.withAlpha(0xB2))); // tests for the default value of labelPadding when tabBarTheme and tabBar do not provide one await tester.pumpWidget(_withTheme(null, tabs: _sizedTabs, isScrollable: true)); @@ -110,16 +104,7 @@ void main() { // verify tabOne and tabTwo is separated by right padding of tabOne and left padding of tabTwo expect(tabOneRect.right, equals(tabTwoRect.left - kTabLabelPadding.left - kTabLabelPadding.right)); - - final RenderBox tabBarBox = tester.firstRenderObject(find.byType(TabBar)); - expect( - tabBarBox, - paints - ..line(color: theme.colorScheme.surfaceVariant) - ..rrect(color: theme.colorScheme.primary), - ); }); - testWidgets('Tab bar theme overrides label color (selected)', (WidgetTester tester) async { const Color labelColor = Colors.black; const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor); @@ -297,15 +282,6 @@ void main() { expect(iconRenderObject.text.style!.color, equals(unselectedLabelColor)); }); - testWidgets('Tab bar default tab indicator size', (WidgetTester tester) async { - await tester.pumpWidget(_withTheme(null, useMaterial3: true, isScrollable: true)); - - await expectLater( - find.byKey(_painterKey), - matchesGoldenFile('tab_bar.default.tab_indicator_size.png'), - ); - }); - testWidgets('Tab bar theme overrides tab indicator size (tab)', (WidgetTester tester) async { const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab); @@ -373,56 +349,4 @@ void main() { matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'), ); }); - - group('Material 2', () { - // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 - // is turned on by default, these tests can be removed. - - testWidgets('Tab bar defaults', (WidgetTester tester) async { - // tests for the default label color and label styles when tabBarTheme and tabBar do not provide any - await tester.pumpWidget(_withTheme(null)); - - final RenderParagraph selectedRenderObject = tester.renderObject(find.text(_tab1Text)); - expect(selectedRenderObject.text.style!.fontFamily, equals('Roboto')); - expect(selectedRenderObject.text.style!.fontSize, equals(14.0)); - expect(selectedRenderObject.text.style!.color, equals(Colors.white)); - final RenderParagraph unselectedRenderObject = tester.renderObject(find.text(_tab2Text)); - expect(unselectedRenderObject.text.style!.fontFamily, equals('Roboto')); - expect(unselectedRenderObject.text.style!.fontSize, equals(14.0)); - expect(unselectedRenderObject.text.style!.color, equals(Colors.white.withAlpha(0xB2))); - - // tests for the default value of labelPadding when tabBarTheme and tabBar do not provide one - await tester.pumpWidget(_withTheme(null, tabs: _sizedTabs, isScrollable: true)); - - const double indicatorWeight = 2.0; - final Rect tabBar = tester.getRect(find.byType(TabBar)); - final Rect tabOneRect = tester.getRect(find.byKey(_sizedTabs[0].key!)); - final Rect tabTwoRect = tester.getRect(find.byKey(_sizedTabs[1].key!)); - - // verify coordinates of tabOne - expect(tabOneRect.left, equals(kTabLabelPadding.left)); - expect(tabOneRect.top, equals(kTabLabelPadding.top)); - expect(tabOneRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight)); - - // verify coordinates of tabTwo - expect(tabTwoRect.right, equals(tabBar.width - kTabLabelPadding.right)); - expect(tabTwoRect.top, equals(kTabLabelPadding.top)); - expect(tabTwoRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight)); - - // verify tabOne and tabTwo is separated by right padding of tabOne and left padding of tabTwo - expect(tabOneRect.right, equals(tabTwoRect.left - kTabLabelPadding.left - kTabLabelPadding.right)); - - final RenderBox tabBarBox = tester.firstRenderObject(find.byType(TabBar)); - expect(tabBarBox, paints..line(color: const Color(0xff2196f3))); - }); - - testWidgets('Tab bar default tab indicator size', (WidgetTester tester) async { - await tester.pumpWidget(_withTheme(null)); - - await expectLater( - find.byKey(_painterKey), - matchesGoldenFile('tab_bar.m2.default.tab_indicator_size.png'), - ); - }); - }); } diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index d93ef8054e86..ef0074bd4248 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -5,7 +5,6 @@ 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'; import '../rendering/mock_canvas.dart'; @@ -5089,156 +5088,6 @@ void main() { expect(tester.takeException(), isAssertionError); }); - - testWidgets('Tab has correct selected/unselected hover color', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - final ThemeData theme = ThemeData(useMaterial3: true); - final List tabs = ['A', 'B', 'C']; - - await tester.pumpWidget(Theme( - data: theme, - child: buildFrame(tabs: tabs, value: 'C')), - ); - - await tester.pumpAndSettle(); - final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); - expect( - inkFeatures, - isNot(paints - ..rect( - color: theme.colorScheme.onSurface.withOpacity(0.08), - )) - ); - expect( - inkFeatures, - isNot(paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.08), - )) - ); - - // Start hovering unselected tab. - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(); - await gesture.moveTo(tester.getCenter(find.byType(Tab).first)); - await tester.pumpAndSettle(); - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.onSurface.withOpacity(0.08), - ) - ); - - // Start hovering selected tab. - await gesture.moveTo(tester.getCenter(find.byType(Tab).last)); - await tester.pumpAndSettle(); - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.08), - ), - ); - }); - - testWidgets('Tab has correct selected/unselected focus color', (WidgetTester tester) async { - tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional; - final ThemeData theme = ThemeData(useMaterial3: true); - final List tabs = ['A', 'B', 'C']; - - await tester.pumpWidget(MaterialApp( - theme: theme, - home: buildFrame(tabs: tabs, value: 'B'), - ), - ); - - await tester.pumpAndSettle(); - final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); - expect( - inkFeatures, - isNot(paints - ..rect( - color: theme.colorScheme.onSurface.withOpacity(0.12), - )) - ); - expect( - inkFeatures, - isNot(paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.12), - )) - ); - - await tester.sendKeyEvent(LogicalKeyboardKey.tab); - await tester.pumpAndSettle(); - expect(tester.binding.focusManager.primaryFocus?.hasPrimaryFocus, isTrue); - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.onSurface.withOpacity(0.12), - ), - ); - - await tester.sendKeyEvent(LogicalKeyboardKey.tab); - await tester.pumpAndSettle(); - expect(tester.binding.focusManager.primaryFocus?.hasPrimaryFocus, isTrue); - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.12), - ), - ); - }); - - testWidgets('Tab has correct selected/unselected pressed color', (WidgetTester tester) async { - final ThemeData theme = ThemeData(useMaterial3: true); - final List tabs = ['A', 'B', 'C']; - - await tester.pumpWidget(MaterialApp( - theme: theme, - home: buildFrame(tabs: tabs, value: 'B'), - ), - ); - - await tester.pumpAndSettle(); - final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); - expect( - inkFeatures, - isNot(paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.12), - )) - ); - - // Press unselected tab. - TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('A'))); - await tester.pumpAndSettle(); // Let the press highlight animation finish. - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.12), - ), - ); - - // Release pressed gesture. - await gesture.up(); - await tester.pumpAndSettle(); - - // Press selected tab. - gesture = await tester.startGesture(tester.getCenter(find.text('B'))); - await tester.pumpAndSettle(); // Let the press highlight animation finish. - expect( - inkFeatures, - paints - ..rect( - color: theme.colorScheme.primary.withOpacity(0.12), - ), - ); - }); } class KeepAliveInk extends StatefulWidget {