mirror of
https://github.com/flutter/flutter
synced 2024-10-06 00:09:53 +00:00
This reverts commit 900b395451
.
This commit is contained in:
parent
02de12947a
commit
b2672fe835
|
@ -46,7 +46,6 @@ import 'package:gen_defaults/segmented_button_template.dart';
|
||||||
import 'package:gen_defaults/slider_template.dart';
|
import 'package:gen_defaults/slider_template.dart';
|
||||||
import 'package:gen_defaults/surface_tint.dart';
|
import 'package:gen_defaults/surface_tint.dart';
|
||||||
import 'package:gen_defaults/switch_template.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/text_field_template.dart';
|
||||||
import 'package:gen_defaults/typography_template.dart';
|
import 'package:gen_defaults/typography_template.dart';
|
||||||
|
|
||||||
|
@ -166,6 +165,5 @@ Future<void> main(List<String> args) async {
|
||||||
SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile();
|
SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile();
|
||||||
SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile();
|
SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile();
|
||||||
TextFieldTemplate('TextField', '$materialLib/text_field.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();
|
TypographyTemplate('Typography', '$materialLib/typography.dart', tokens).updateFile();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Color?> get overlayColor {
|
|
||||||
return MaterialStateProperty.resolveWith((Set<MaterialState> 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;
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
}
|
|
|
@ -29,9 +29,7 @@ class TabBarTheme with Diagnosticable {
|
||||||
/// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme].
|
/// Creates a tab bar theme that can be used with [ThemeData.tabBarTheme].
|
||||||
const TabBarTheme({
|
const TabBarTheme({
|
||||||
this.indicator,
|
this.indicator,
|
||||||
this.indicatorColor,
|
|
||||||
this.indicatorSize,
|
this.indicatorSize,
|
||||||
this.dividerColor,
|
|
||||||
this.labelColor,
|
this.labelColor,
|
||||||
this.labelPadding,
|
this.labelPadding,
|
||||||
this.labelStyle,
|
this.labelStyle,
|
||||||
|
@ -45,15 +43,9 @@ class TabBarTheme with Diagnosticable {
|
||||||
/// Overrides the default value for [TabBar.indicator].
|
/// Overrides the default value for [TabBar.indicator].
|
||||||
final Decoration? indicator;
|
final Decoration? indicator;
|
||||||
|
|
||||||
/// Overrides the default value for [TabBar.indicatorColor].
|
|
||||||
final Color? indicatorColor;
|
|
||||||
|
|
||||||
/// Overrides the default value for [TabBar.indicatorSize].
|
/// Overrides the default value for [TabBar.indicatorSize].
|
||||||
final TabBarIndicatorSize? indicatorSize;
|
final TabBarIndicatorSize? indicatorSize;
|
||||||
|
|
||||||
/// Overrides the default value for [TabBar.dividerColor].
|
|
||||||
final Color? dividerColor;
|
|
||||||
|
|
||||||
/// Overrides the default value for [TabBar.labelColor].
|
/// Overrides the default value for [TabBar.labelColor].
|
||||||
final Color? labelColor;
|
final Color? labelColor;
|
||||||
|
|
||||||
|
@ -88,9 +80,7 @@ class TabBarTheme with Diagnosticable {
|
||||||
/// new values.
|
/// new values.
|
||||||
TabBarTheme copyWith({
|
TabBarTheme copyWith({
|
||||||
Decoration? indicator,
|
Decoration? indicator,
|
||||||
Color? indicatorColor,
|
|
||||||
TabBarIndicatorSize? indicatorSize,
|
TabBarIndicatorSize? indicatorSize,
|
||||||
Color? dividerColor,
|
|
||||||
Color? labelColor,
|
Color? labelColor,
|
||||||
EdgeInsetsGeometry? labelPadding,
|
EdgeInsetsGeometry? labelPadding,
|
||||||
TextStyle? labelStyle,
|
TextStyle? labelStyle,
|
||||||
|
@ -102,9 +92,7 @@ class TabBarTheme with Diagnosticable {
|
||||||
}) {
|
}) {
|
||||||
return TabBarTheme(
|
return TabBarTheme(
|
||||||
indicator: indicator ?? this.indicator,
|
indicator: indicator ?? this.indicator,
|
||||||
indicatorColor: indicatorColor ?? this.indicatorColor,
|
|
||||||
indicatorSize: indicatorSize ?? this.indicatorSize,
|
indicatorSize: indicatorSize ?? this.indicatorSize,
|
||||||
dividerColor: dividerColor ?? this.dividerColor,
|
|
||||||
labelColor: labelColor ?? this.labelColor,
|
labelColor: labelColor ?? this.labelColor,
|
||||||
labelPadding: labelPadding ?? this.labelPadding,
|
labelPadding: labelPadding ?? this.labelPadding,
|
||||||
labelStyle: labelStyle ?? this.labelStyle,
|
labelStyle: labelStyle ?? this.labelStyle,
|
||||||
|
@ -132,15 +120,13 @@ class TabBarTheme with Diagnosticable {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
return TabBarTheme(
|
return TabBarTheme(
|
||||||
indicator: Decoration.lerp(a.indicator, b.indicator, t),
|
indicator: Decoration.lerp(a.indicator, b.indicator, t),
|
||||||
indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
|
|
||||||
indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize,
|
indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize,
|
||||||
dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t),
|
|
||||||
labelColor: Color.lerp(a.labelColor, b.labelColor, t),
|
labelColor: Color.lerp(a.labelColor, b.labelColor, t),
|
||||||
labelPadding: EdgeInsetsGeometry.lerp(a.labelPadding, b.labelPadding, t),
|
labelPadding: EdgeInsetsGeometry.lerp(a.labelPadding, b.labelPadding, t),
|
||||||
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
|
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
|
||||||
unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t),
|
unselectedLabelColor: Color.lerp(a.unselectedLabelColor, b.unselectedLabelColor, t),
|
||||||
unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t),
|
unselectedLabelStyle: TextStyle.lerp(a.unselectedLabelStyle, b.unselectedLabelStyle, t),
|
||||||
overlayColor: MaterialStateProperty.lerp<Color?>(a.overlayColor, b.overlayColor, t, Color.lerp),
|
overlayColor: _LerpColors(a.overlayColor, b.overlayColor, t),
|
||||||
splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
|
splashFactory: t < 0.5 ? a.splashFactory : b.splashFactory,
|
||||||
mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor,
|
mouseCursor: t < 0.5 ? a.mouseCursor : b.mouseCursor,
|
||||||
);
|
);
|
||||||
|
@ -149,9 +135,7 @@ class TabBarTheme with Diagnosticable {
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(
|
||||||
indicator,
|
indicator,
|
||||||
indicatorColor,
|
|
||||||
indicatorSize,
|
indicatorSize,
|
||||||
dividerColor,
|
|
||||||
labelColor,
|
labelColor,
|
||||||
labelPadding,
|
labelPadding,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
|
@ -172,9 +156,7 @@ class TabBarTheme with Diagnosticable {
|
||||||
}
|
}
|
||||||
return other is TabBarTheme
|
return other is TabBarTheme
|
||||||
&& other.indicator == indicator
|
&& other.indicator == indicator
|
||||||
&& other.indicatorColor == indicatorColor
|
|
||||||
&& other.indicatorSize == indicatorSize
|
&& other.indicatorSize == indicatorSize
|
||||||
&& other.dividerColor == dividerColor
|
|
||||||
&& other.labelColor == labelColor
|
&& other.labelColor == labelColor
|
||||||
&& other.labelPadding == labelPadding
|
&& other.labelPadding == labelPadding
|
||||||
&& other.labelStyle == labelStyle
|
&& other.labelStyle == labelStyle
|
||||||
|
@ -185,3 +167,39 @@ class TabBarTheme with Diagnosticable {
|
||||||
&& other.mouseCursor == mouseCursor;
|
&& other.mouseCursor == mouseCursor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class _LerpColors implements MaterialStateProperty<Color?> {
|
||||||
|
const _LerpColors(this.a, this.b, this.t);
|
||||||
|
|
||||||
|
final MaterialStateProperty<Color?>? a;
|
||||||
|
final MaterialStateProperty<Color?>? b;
|
||||||
|
final double t;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color? resolve(Set<MaterialState> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,18 +20,11 @@ class UnderlineTabIndicator extends Decoration {
|
||||||
///
|
///
|
||||||
/// The [borderSide] and [insets] arguments must not be null.
|
/// The [borderSide] and [insets] arguments must not be null.
|
||||||
const UnderlineTabIndicator({
|
const UnderlineTabIndicator({
|
||||||
this.borderRadius,
|
|
||||||
this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
|
this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
|
||||||
this.insets = EdgeInsets.zero,
|
this.insets = EdgeInsets.zero,
|
||||||
}) : assert(borderSide != null),
|
}) : assert(borderSide != null),
|
||||||
assert(insets != 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.
|
/// The color and weight of the horizontal line drawn below the selected tab.
|
||||||
final BorderSide borderSide;
|
final BorderSide borderSide;
|
||||||
|
|
||||||
|
@ -67,7 +60,7 @@ class UnderlineTabIndicator extends Decoration {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BoxPainter createBoxPainter([ VoidCallback? onChanged ]) {
|
BoxPainter createBoxPainter([ VoidCallback? onChanged ]) {
|
||||||
return _UnderlinePainter(this, borderRadius, onChanged);
|
return _UnderlinePainter(this, onChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
|
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
|
||||||
|
@ -84,25 +77,15 @@ class UnderlineTabIndicator extends Decoration {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Path getClipPath(Rect rect, TextDirection textDirection) {
|
Path getClipPath(Rect rect, TextDirection textDirection) {
|
||||||
if (borderRadius != null) {
|
|
||||||
return Path()..addRRect(
|
|
||||||
borderRadius!.toRRect(_indicatorRectFor(rect, textDirection))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Path()..addRect(_indicatorRectFor(rect, textDirection));
|
return Path()..addRect(_indicatorRectFor(rect, textDirection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UnderlinePainter extends BoxPainter {
|
class _UnderlinePainter extends BoxPainter {
|
||||||
_UnderlinePainter(
|
_UnderlinePainter(this.decoration, super.onChanged)
|
||||||
this.decoration,
|
|
||||||
this.borderRadius,
|
|
||||||
super.onChanged,
|
|
||||||
)
|
|
||||||
: assert(decoration != null);
|
: assert(decoration != null);
|
||||||
|
|
||||||
final UnderlineTabIndicator decoration;
|
final UnderlineTabIndicator decoration;
|
||||||
final BorderRadius? borderRadius;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
|
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
|
||||||
|
@ -110,24 +93,8 @@ class _UnderlinePainter extends BoxPainter {
|
||||||
assert(configuration.size != null);
|
assert(configuration.size != null);
|
||||||
final Rect rect = offset & configuration.size!;
|
final Rect rect = offset & configuration.size!;
|
||||||
final TextDirection textDirection = configuration.textDirection!;
|
final TextDirection textDirection = configuration.textDirection!;
|
||||||
final Paint paint;
|
final Rect indicator = decoration._indicatorRectFor(rect, textDirection).deflate(decoration.borderSide.width / 2.0);
|
||||||
if (borderRadius != null) {
|
final Paint paint = decoration.borderSide.toPaint()..strokeCap = StrokeCap.square;
|
||||||
paint = Paint()..color = decoration.borderSide.color;
|
canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'app_bar.dart';
|
import 'app_bar.dart';
|
||||||
import 'color_scheme.dart';
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
import 'debug.dart';
|
import 'debug.dart';
|
||||||
|
@ -22,7 +21,6 @@ import 'material_state.dart';
|
||||||
import 'tab_bar_theme.dart';
|
import 'tab_bar_theme.dart';
|
||||||
import 'tab_controller.dart';
|
import 'tab_controller.dart';
|
||||||
import 'tab_indicator.dart';
|
import 'tab_indicator.dart';
|
||||||
import 'text_theme.dart';
|
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
const double _kTabHeight = 46.0;
|
const double _kTabHeight = 46.0;
|
||||||
|
@ -185,19 +183,18 @@ class _TabStyle extends AnimatedWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData themeData = Theme.of(context);
|
final ThemeData themeData = Theme.of(context);
|
||||||
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
||||||
final TabBarTheme defaults = themeData.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context);
|
|
||||||
final Animation<double> animation = listenable as Animation<double>;
|
final Animation<double> animation = listenable as Animation<double>;
|
||||||
|
|
||||||
// To enable TextStyle.lerp(style1, style2, value), both styles must have
|
// To enable TextStyle.lerp(style1, style2, value), both styles must have
|
||||||
// the same value of inherit. Force that to be inherit=true here.
|
// the same value of inherit. Force that to be inherit=true here.
|
||||||
final TextStyle defaultStyle = (labelStyle
|
final TextStyle defaultStyle = (labelStyle
|
||||||
?? tabBarTheme.labelStyle
|
?? tabBarTheme.labelStyle
|
||||||
?? defaults.labelStyle!
|
?? themeData.primaryTextTheme.bodyLarge!
|
||||||
).copyWith(inherit: true);
|
).copyWith(inherit: true);
|
||||||
final TextStyle defaultUnselectedStyle = (unselectedLabelStyle
|
final TextStyle defaultUnselectedStyle = (unselectedLabelStyle
|
||||||
?? tabBarTheme.unselectedLabelStyle
|
?? tabBarTheme.unselectedLabelStyle
|
||||||
?? labelStyle
|
?? labelStyle
|
||||||
?? defaults.unselectedLabelStyle!
|
?? themeData.primaryTextTheme.bodyLarge!
|
||||||
).copyWith(inherit: true);
|
).copyWith(inherit: true);
|
||||||
final TextStyle textStyle = selected
|
final TextStyle textStyle = selected
|
||||||
? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value)!
|
? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value)!
|
||||||
|
@ -205,10 +202,10 @@ class _TabStyle extends AnimatedWidget {
|
||||||
|
|
||||||
final Color selectedColor = labelColor
|
final Color selectedColor = labelColor
|
||||||
?? tabBarTheme.labelColor
|
?? tabBarTheme.labelColor
|
||||||
?? defaults.labelColor!;
|
?? themeData.primaryTextTheme.bodyLarge!.color!;
|
||||||
final Color unselectedColor = unselectedLabelColor
|
final Color unselectedColor = unselectedLabelColor
|
||||||
?? tabBarTheme.unselectedLabelColor
|
?? tabBarTheme.unselectedLabelColor
|
||||||
?? defaults.unselectedLabelColor!;
|
?? selectedColor.withAlpha(0xB2); // 70% alpha
|
||||||
final Color color = selected
|
final Color color = selected
|
||||||
? Color.lerp(selectedColor, unselectedColor, animation.value)!
|
? Color.lerp(selectedColor, unselectedColor, animation.value)!
|
||||||
: Color.lerp(unselectedColor, selectedColor, animation.value)!;
|
: Color.lerp(unselectedColor, selectedColor, animation.value)!;
|
||||||
|
@ -330,7 +327,6 @@ class _IndicatorPainter extends CustomPainter {
|
||||||
required this.tabKeys,
|
required this.tabKeys,
|
||||||
required _IndicatorPainter? old,
|
required _IndicatorPainter? old,
|
||||||
required this.indicatorPadding,
|
required this.indicatorPadding,
|
||||||
this.dividerColor,
|
|
||||||
}) : assert(controller != null),
|
}) : assert(controller != null),
|
||||||
assert(indicator != null),
|
assert(indicator != null),
|
||||||
super(repaint: controller.animation) {
|
super(repaint: controller.animation) {
|
||||||
|
@ -344,7 +340,6 @@ class _IndicatorPainter extends CustomPainter {
|
||||||
final TabBarIndicatorSize? indicatorSize;
|
final TabBarIndicatorSize? indicatorSize;
|
||||||
final EdgeInsetsGeometry indicatorPadding;
|
final EdgeInsetsGeometry indicatorPadding;
|
||||||
final List<GlobalKey> tabKeys;
|
final List<GlobalKey> tabKeys;
|
||||||
final Color? dividerColor;
|
|
||||||
|
|
||||||
// _currentTabOffsets and _currentTextDirection are set each time TabBar
|
// _currentTabOffsets and _currentTextDirection are set each time TabBar
|
||||||
// layout is completed. These values can be null when TabBar contains no
|
// layout is completed. These values can be null when TabBar contains no
|
||||||
|
@ -436,10 +431,6 @@ class _IndicatorPainter extends CustomPainter {
|
||||||
size: _currentRect!.size,
|
size: _currentRect!.size,
|
||||||
textDirection: _currentTextDirection,
|
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);
|
_painter!.paint(canvas, _currentRect!.topLeft, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +630,6 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
|
||||||
this.indicatorPadding = EdgeInsets.zero,
|
this.indicatorPadding = EdgeInsets.zero,
|
||||||
this.indicator,
|
this.indicator,
|
||||||
this.indicatorSize,
|
this.indicatorSize,
|
||||||
this.dividerColor,
|
|
||||||
this.labelColor,
|
this.labelColor,
|
||||||
this.labelStyle,
|
this.labelStyle,
|
||||||
this.labelPadding,
|
this.labelPadding,
|
||||||
|
@ -754,13 +744,6 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
|
||||||
/// [indicator] properties.
|
/// [indicator] properties.
|
||||||
final TabBarIndicatorSize? indicatorSize;
|
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.
|
/// The color of selected tab labels.
|
||||||
///
|
///
|
||||||
/// Unselected tab labels are rendered with the same color rendered at 70%
|
/// Unselected tab labels are rendered with the same color rendered at 70%
|
||||||
|
@ -956,22 +939,16 @@ class _TabBarState extends State<TabBar> {
|
||||||
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
|
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Decoration _getIndicator() {
|
Decoration get _indicator {
|
||||||
final ThemeData theme = Theme.of(context);
|
|
||||||
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
|
||||||
final TabBarTheme defaults = theme.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context);
|
|
||||||
|
|
||||||
if (widget.indicator != null) {
|
if (widget.indicator != null) {
|
||||||
return widget.indicator!;
|
return widget.indicator!;
|
||||||
}
|
}
|
||||||
|
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
||||||
if (tabBarTheme.indicator != null) {
|
if (tabBarTheme.indicator != null) {
|
||||||
return tabBarTheme.indicator!;
|
return tabBarTheme.indicator!;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color color = widget.indicatorColor
|
Color color = widget.indicatorColor ?? Theme.of(context).indicatorColor;
|
||||||
?? (theme.useMaterial3
|
|
||||||
? tabBarTheme.indicatorColor ?? defaults.indicatorColor!
|
|
||||||
: Theme.of(context).indicatorColor);
|
|
||||||
// ThemeData tries to avoid this by having indicatorColor avoid being the
|
// ThemeData tries to avoid this by having indicatorColor avoid being the
|
||||||
// primaryColor. However, it's possible that the tab bar is on a
|
// primaryColor. However, it's possible that the tab bar is on a
|
||||||
// Material that isn't the primaryColor. In that case, if the indicator
|
// Material that isn't the primaryColor. In that case, if the indicator
|
||||||
|
@ -991,16 +968,6 @@ class _TabBarState extends State<TabBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return UnderlineTabIndicator(
|
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(
|
borderSide: BorderSide(
|
||||||
width: widget.indicatorWeight,
|
width: widget.indicatorWeight,
|
||||||
color: color,
|
color: color,
|
||||||
|
@ -1045,18 +1012,13 @@ class _TabBarState extends State<TabBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initIndicatorPainter() {
|
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(
|
_indicatorPainter = !_controllerIsValid ? null : _IndicatorPainter(
|
||||||
controller: _controller!,
|
controller: _controller!,
|
||||||
indicator: _getIndicator(),
|
indicator: _indicator,
|
||||||
indicatorSize: widget.indicatorSize ?? tabBarTheme.indicatorSize ?? defaults.indicatorSize!,
|
indicatorSize: widget.indicatorSize ?? TabBarTheme.of(context).indicatorSize,
|
||||||
indicatorPadding: widget.indicatorPadding,
|
indicatorPadding: widget.indicatorPadding,
|
||||||
tabKeys: _tabKeys,
|
tabKeys: _tabKeys,
|
||||||
old: _indicatorPainter,
|
old: _indicatorPainter,
|
||||||
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1248,9 +1210,7 @@ class _TabBarState extends State<TabBar> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ThemeData theme = Theme.of(context);
|
|
||||||
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
|
||||||
final TabBarTheme defaults = theme.useMaterial3 ? _TabsDefaultsM3(context) : _TabsDefaultsM2(context);
|
|
||||||
|
|
||||||
final List<Widget> wrappedTabs = List<Widget>.generate(widget.tabs.length, (int index) {
|
final List<Widget> wrappedTabs = List<Widget>.generate(widget.tabs.length, (int index) {
|
||||||
const double verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/2.0;
|
const double verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/2.0;
|
||||||
|
@ -1315,26 +1275,20 @@ class _TabBarState extends State<TabBar> {
|
||||||
// the same share of the tab bar's overall width.
|
// the same share of the tab bar's overall width.
|
||||||
final int tabCount = widget.tabs.length;
|
final int tabCount = widget.tabs.length;
|
||||||
for (int index = 0; index < tabCount; index += 1) {
|
for (int index = 0; index < tabCount; index += 1) {
|
||||||
final Set<MaterialState> selectedState = <MaterialState>{
|
final Set<MaterialState> states = <MaterialState>{
|
||||||
if (index == _currentIndex) MaterialState.selected,
|
if (index == _currentIndex) MaterialState.selected,
|
||||||
};
|
};
|
||||||
|
|
||||||
final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor?>(widget.mouseCursor, selectedState)
|
final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs<MouseCursor?>(widget.mouseCursor, states)
|
||||||
?? tabBarTheme.mouseCursor?.resolve(selectedState)
|
?? tabBarTheme.mouseCursor?.resolve(states)
|
||||||
?? MaterialStateMouseCursor.clickable.resolve(selectedState);
|
?? MaterialStateMouseCursor.clickable.resolve(states);
|
||||||
|
|
||||||
final MaterialStateProperty<Color?> defaultOverlay = MaterialStateProperty.resolveWith<Color?>(
|
|
||||||
(Set<MaterialState> states) {
|
|
||||||
final Set<MaterialState> effectiveStates = selectedState..addAll(states);
|
|
||||||
return defaults.overlayColor?.resolve(effectiveStates);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
wrappedTabs[index] = InkWell(
|
wrappedTabs[index] = InkWell(
|
||||||
mouseCursor: effectiveMouseCursor,
|
mouseCursor: effectiveMouseCursor,
|
||||||
onTap: () { _handleTap(index); },
|
onTap: () { _handleTap(index); },
|
||||||
enableFeedback: widget.enableFeedback ?? true,
|
enableFeedback: widget.enableFeedback ?? true,
|
||||||
overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor ?? defaultOverlay,
|
overlayColor: widget.overlayColor ?? tabBarTheme.overlayColor,
|
||||||
splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory ?? defaults.splashFactory,
|
splashFactory: widget.splashFactory ?? tabBarTheme.splashFactory,
|
||||||
borderRadius: widget.splashBorderRadius,
|
borderRadius: widget.splashBorderRadius,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: widget.indicatorWeight),
|
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<Color?> get overlayColor {
|
|
||||||
return MaterialStateProperty.resolveWith((Set<MaterialState> 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
|
|
||||||
|
|
|
@ -11,8 +11,6 @@ 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';
|
||||||
|
|
||||||
import '../rendering/mock_canvas.dart';
|
|
||||||
|
|
||||||
const String _tab1Text = 'tab 1';
|
const String _tab1Text = 'tab 1';
|
||||||
const String _tab2Text = 'tab 2';
|
const String _tab2Text = 'tab 2';
|
||||||
const String _tab3Text = 'tab 3';
|
const String _tab3Text = 'tab 3';
|
||||||
|
@ -34,10 +32,9 @@ Widget _withTheme(
|
||||||
TabBarTheme? theme, {
|
TabBarTheme? theme, {
|
||||||
List<Widget> tabs = _tabs,
|
List<Widget> tabs = _tabs,
|
||||||
bool isScrollable = false,
|
bool isScrollable = false,
|
||||||
bool useMaterial3 = false,
|
|
||||||
}) {
|
}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
theme: ThemeData(tabBarTheme: theme, useMaterial3: useMaterial3),
|
theme: ThemeData(tabBarTheme: theme),
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: RepaintBoundary(
|
body: RepaintBoundary(
|
||||||
key: _painterKey,
|
key: _painterKey,
|
||||||
|
@ -63,9 +60,7 @@ void main() {
|
||||||
expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode);
|
expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode);
|
||||||
|
|
||||||
expect(const TabBarTheme().indicator, null);
|
expect(const TabBarTheme().indicator, null);
|
||||||
expect(const TabBarTheme().indicatorColor, null);
|
|
||||||
expect(const TabBarTheme().indicatorSize, null);
|
expect(const TabBarTheme().indicatorSize, null);
|
||||||
expect(const TabBarTheme().dividerColor, null);
|
|
||||||
expect(const TabBarTheme().labelColor, null);
|
expect(const TabBarTheme().labelColor, null);
|
||||||
expect(const TabBarTheme().labelPadding, null);
|
expect(const TabBarTheme().labelPadding, null);
|
||||||
expect(const TabBarTheme().labelStyle, null);
|
expect(const TabBarTheme().labelStyle, null);
|
||||||
|
@ -76,19 +71,18 @@ void main() {
|
||||||
expect(const TabBarTheme().mouseCursor, null);
|
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
|
// 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<RenderParagraph>(find.text(_tab1Text));
|
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(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!.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<RenderParagraph>(find.text(_tab2Text));
|
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(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!.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
|
// tests for the default value of labelPadding when tabBarTheme and tabBar do not provide one
|
||||||
await tester.pumpWidget(_withTheme(null, tabs: _sizedTabs, isScrollable: true));
|
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
|
// 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));
|
expect(tabOneRect.right, equals(tabTwoRect.left - kTabLabelPadding.left - kTabLabelPadding.right));
|
||||||
|
|
||||||
final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(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 {
|
testWidgets('Tab bar theme overrides label color (selected)', (WidgetTester tester) async {
|
||||||
const Color labelColor = Colors.black;
|
const Color labelColor = Colors.black;
|
||||||
const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor);
|
const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor);
|
||||||
|
@ -297,15 +282,6 @@ void main() {
|
||||||
expect(iconRenderObject.text.style!.color, equals(unselectedLabelColor));
|
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 {
|
testWidgets('Tab bar theme overrides tab indicator size (tab)', (WidgetTester tester) async {
|
||||||
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab);
|
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab);
|
||||||
|
|
||||||
|
@ -373,56 +349,4 @@ void main() {
|
||||||
matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'),
|
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<RenderParagraph>(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<RenderParagraph>(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<RenderBox>(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'),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import '../rendering/mock_canvas.dart';
|
import '../rendering/mock_canvas.dart';
|
||||||
|
@ -5089,156 +5088,6 @@ void main() {
|
||||||
|
|
||||||
expect(tester.takeException(), isAssertionError);
|
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<String> tabs = <String>['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<String> tabs = <String>['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<String> tabs = <String>['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 {
|
class KeepAliveInk extends StatefulWidget {
|
||||||
|
|
Loading…
Reference in a new issue