Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add trackOutlineColor for Switch and SwitchListTile (#120140)
Browse files Browse the repository at this point in the history
* Add trackOutlineColor for Switch and SwitchListTile

* Update tests

* Update test

* Clean up unnecessary StatefulBUilder in tests

* Fix failed test

---------

Co-authored-by: Qun Cheng <quncheng@google.com>
  • Loading branch information
QuncCccccc and QuncCccccc committed Feb 8, 2023
1 parent 6e7f580 commit 3f98c0f
Show file tree
Hide file tree
Showing 6 changed files with 536 additions and 231 deletions.
93 changes: 74 additions & 19 deletions packages/flutter/lib/src/material/switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Switch extends StatelessWidget {
this.onInactiveThumbImageError,
this.thumbColor,
this.trackColor,
this.trackOutlineColor,
this.thumbIcon,
this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.start,
Expand Down Expand Up @@ -151,6 +152,7 @@ class Switch extends StatelessWidget {
this.materialTapTargetSize,
this.thumbColor,
this.trackColor,
this.trackOutlineColor,
this.thumbIcon,
this.dragStartBehavior = DragStartBehavior.start,
this.mouseCursor,
Expand Down Expand Up @@ -333,6 +335,40 @@ class Switch extends StatelessWidget {
/// | Disabled | `Colors.black12` | `Colors.white10` |
final MaterialStateProperty<Color?>? trackColor;

/// {@template flutter.material.switch.trackOutlineColor}
/// The outline color of this [Switch]'s track.
///
/// Resolved in the following states:
/// * [MaterialState.selected].
/// * [MaterialState.hovered].
/// * [MaterialState.focused].
/// * [MaterialState.disabled].
///
/// {@tool snippet}
/// This example resolves the [trackOutlineColor] based on the current
/// [MaterialState] of the [Switch], providing a different [Color] when it is
/// [MaterialState.disabled].
///
/// ```dart
/// Switch(
/// value: true,
/// onChanged: (_) => true,
/// trackOutlineColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
/// if (states.contains(MaterialState.disabled)) {
/// return Colors.orange.withOpacity(.48);
/// }
/// return null; // Use the default color.
/// }),
/// )
/// ```
/// {@end-tool}
/// {@endtemplate}
///
/// In Material 3, the outline color defaults to transparent in the selected
/// state and [ColorScheme.outline] in the unselected state. In Material 2,
/// the [Switch] track has no outline by default.
final MaterialStateProperty<Color?>? trackOutlineColor;

/// {@template flutter.material.switch.thumbIcon}
/// The icon to use on the thumb of this switch
///
Expand Down Expand Up @@ -519,6 +555,7 @@ class Switch extends StatelessWidget {
onInactiveThumbImageError: onInactiveThumbImageError,
thumbColor: thumbColor,
trackColor: trackColor,
trackOutlineColor: trackOutlineColor,
thumbIcon: thumbIcon,
materialTapTargetSize: materialTapTargetSize,
dragStartBehavior: dragStartBehavior,
Expand Down Expand Up @@ -578,6 +615,7 @@ class _MaterialSwitch extends StatefulWidget {
this.onInactiveThumbImageError,
this.thumbColor,
this.trackColor,
this.trackOutlineColor,
this.thumbIcon,
this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.start,
Expand All @@ -604,6 +642,7 @@ class _MaterialSwitch extends StatefulWidget {
final ImageErrorListener? onInactiveThumbImageError;
final MaterialStateProperty<Color?>? thumbColor;
final MaterialStateProperty<Color?>? trackColor;
final MaterialStateProperty<Color?>? trackOutlineColor;
final MaterialStateProperty<Icon?>? thumbIcon;
final MaterialTapTargetSize? materialTapTargetSize;
final DragStartBehavior dragStartBehavior;
Expand Down Expand Up @@ -765,11 +804,17 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
?? switchTheme.trackColor?.resolve(activeStates)
?? _widgetThumbColor.resolve(activeStates)?.withAlpha(0x80)
?? defaults.trackColor!.resolve(activeStates)!;
final Color effectiveActiveTrackOutlineColor = widget.trackOutlineColor?.resolve(activeStates)
?? switchTheme.trackOutlineColor?.resolve(activeStates)
?? Colors.transparent;

final Color effectiveInactiveTrackColor = widget.trackColor?.resolve(inactiveStates)
?? _widgetTrackColor.resolve(inactiveStates)
?? switchTheme.trackColor?.resolve(inactiveStates)
?? defaults.trackColor!.resolve(inactiveStates)!;
final Color? effectiveInactiveTrackOutlineColor = switchConfig.trackOutlineColor?.resolve(inactiveStates);
final Color? effectiveInactiveTrackOutlineColor = widget.trackOutlineColor?.resolve(inactiveStates)
?? switchTheme.trackOutlineColor?.resolve(inactiveStates)
?? defaults.trackOutlineColor?.resolve(inactiveStates);

final Icon? effectiveActiveIcon = widget.thumbIcon?.resolve(activeStates)
?? switchTheme.thumbIcon?.resolve(activeStates);
Expand Down Expand Up @@ -858,6 +903,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
..inactiveThumbImage = widget.inactiveThumbImage
..onInactiveThumbImageError = widget.onInactiveThumbImageError
..activeTrackColor = effectiveActiveTrackColor
..activeTrackOutlineColor = effectiveActiveTrackOutlineColor
..inactiveTrackColor = effectiveInactiveTrackColor
..inactiveTrackOutlineColor = effectiveInactiveTrackOutlineColor
..configuration = createLocalImageConfiguration(context)
Expand Down Expand Up @@ -1089,6 +1135,16 @@ class _SwitchPainter extends ToggleablePainter {
notifyListeners();
}

Color? get activeTrackOutlineColor => _activeTrackOutlineColor;
Color? _activeTrackOutlineColor;
set activeTrackOutlineColor(Color? value) {
if (value == _activeTrackOutlineColor) {
return;
}
_activeTrackOutlineColor = value;
notifyListeners();
}

Color? get inactiveTrackOutlineColor => _inactiveTrackOutlineColor;
Color? _inactiveTrackOutlineColor;
set inactiveTrackOutlineColor(Color? value) {
Expand Down Expand Up @@ -1297,7 +1353,7 @@ class _SwitchPainter extends ToggleablePainter {
final double colorValue = CurvedAnimation(parent: positionController, curve: Curves.easeOut, reverseCurve: Curves.easeIn).value;
final Color trackColor = Color.lerp(inactiveTrackColor, activeTrackColor, colorValue)!;
final Color? trackOutlineColor = inactiveTrackOutlineColor == null ? null
: Color.lerp(inactiveTrackOutlineColor, Colors.transparent, colorValue);
: Color.lerp(inactiveTrackOutlineColor, activeTrackOutlineColor, colorValue);
Color lerpedThumbColor;
if (!reaction.isDismissed) {
lerpedThumbColor = Color.lerp(inactivePressedColor, activePressedColor, colorValue)!;
Expand Down Expand Up @@ -1493,7 +1549,6 @@ mixin _SwitchConfig {
double get pressedThumbRadius;
double get thumbRadiusWithIcon;
List<BoxShadow>? get thumbShadow;
MaterialStateProperty<Color?>? get trackOutlineColor;
MaterialStateProperty<Color> get iconColor;
double? get thumbOffset;
Size get transitionalThumbSize;
Expand Down Expand Up @@ -1534,9 +1589,6 @@ class _SwitchConfigM2 with _SwitchConfig {
@override
double get trackHeight => 14.0;

@override
MaterialStateProperty<Color?>? get trackOutlineColor => null;

@override
double get trackWidth => 33.0;

Expand Down Expand Up @@ -1590,6 +1642,9 @@ class _SwitchDefaultsM2 extends SwitchThemeData {
});
}

@override
MaterialStateProperty<Color?>? get trackOutlineColor => null;

@override
MaterialTapTargetSize get materialTapTargetSize => _theme.materialTapTargetSize;

Expand Down Expand Up @@ -1700,6 +1755,19 @@ class _SwitchDefaultsM3 extends SwitchThemeData {
});
}

@override
MaterialStateProperty<Color?> get trackOutlineColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return Colors.transparent;
}
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.12);
}
return _colors.outline;
});
}

@override
MaterialStateProperty<Color?> get overlayColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
Expand Down Expand Up @@ -1802,19 +1870,6 @@ class _SwitchConfigM3 with _SwitchConfig {
@override
double get trackHeight => 32.0;

@override
MaterialStateProperty<Color?> get trackOutlineColor {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return null;
}
if (states.contains(MaterialState.disabled)) {
return _colors.onSurface.withOpacity(0.12);
}
return _colors.outline;
});
}

@override
double get trackWidth => 52.0;

Expand Down
14 changes: 14 additions & 0 deletions packages/flutter/lib/src/material/switch_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class SwitchListTile extends StatelessWidget {
this.onFocusChange,
this.enableFeedback,
this.hoverColor,
this.trackOutlineColor,
}) : _switchListTileType = _SwitchListTileType.material,
assert(!isThreeLine || subtitle != null);

Expand Down Expand Up @@ -228,6 +229,7 @@ class SwitchListTile extends StatelessWidget {
this.onFocusChange,
this.enableFeedback,
this.hoverColor,
this.trackOutlineColor,
}) : _switchListTileType = _SwitchListTileType.adaptive,
assert(!isThreeLine || subtitle != null);

Expand Down Expand Up @@ -382,6 +384,16 @@ class SwitchListTile extends StatelessWidget {
/// The color for the tile's [Material] when a pointer is hovering over it.
final Color? hoverColor;

/// {@macro flutter.material.switch.trackOutlineColor}
///
/// The [ListTile] will be focused when this [SwitchListTile] requests focus,
/// so the focused outline color of the switch will be ignored.
///
/// In Material 3, the outline color defaults to transparent in the selected
/// state and [ColorScheme.outline] in the unselected state. In Material 2,
/// the [Switch] track has no outline.
final MaterialStateProperty<Color?>? trackOutlineColor;

@override
Widget build(BuildContext context) {
final Widget control;
Expand All @@ -399,6 +411,7 @@ class SwitchListTile extends StatelessWidget {
inactiveThumbColor: inactiveThumbColor,
autofocus: autofocus,
onFocusChange: onFocusChange,
trackOutlineColor: trackOutlineColor,
);
break;

Expand All @@ -415,6 +428,7 @@ class SwitchListTile extends StatelessWidget {
inactiveThumbColor: inactiveThumbColor,
autofocus: autofocus,
onFocusChange: onFocusChange,
trackOutlineColor: trackOutlineColor,
);
}

Expand Down
12 changes: 12 additions & 0 deletions packages/flutter/lib/src/material/switch_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class SwitchThemeData with Diagnosticable {
const SwitchThemeData({
this.thumbColor,
this.trackColor,
this.trackOutlineColor,
this.materialTapTargetSize,
this.mouseCursor,
this.overlayColor,
Expand All @@ -56,6 +57,11 @@ class SwitchThemeData with Diagnosticable {
/// If specified, overrides the default value of [Switch.trackColor].
final MaterialStateProperty<Color?>? trackColor;

/// {@macro flutter.material.switch.trackOutlineColor}
///
/// If specified, overrides the default value of [Switch.trackOutlineColor].
final MaterialStateProperty<Color?>? trackOutlineColor;

/// {@macro flutter.material.switch.materialTapTargetSize}
///
/// If specified, overrides the default value of
Expand Down Expand Up @@ -87,6 +93,7 @@ class SwitchThemeData with Diagnosticable {
SwitchThemeData copyWith({
MaterialStateProperty<Color?>? thumbColor,
MaterialStateProperty<Color?>? trackColor,
MaterialStateProperty<Color?>? trackOutlineColor,
MaterialTapTargetSize? materialTapTargetSize,
MaterialStateProperty<MouseCursor?>? mouseCursor,
MaterialStateProperty<Color?>? overlayColor,
Expand All @@ -96,6 +103,7 @@ class SwitchThemeData with Diagnosticable {
return SwitchThemeData(
thumbColor: thumbColor ?? this.thumbColor,
trackColor: trackColor ?? this.trackColor,
trackOutlineColor: trackOutlineColor ?? this.trackOutlineColor,
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
mouseCursor: mouseCursor ?? this.mouseCursor,
overlayColor: overlayColor ?? this.overlayColor,
Expand All @@ -111,6 +119,7 @@ class SwitchThemeData with Diagnosticable {
return SwitchThemeData(
thumbColor: MaterialStateProperty.lerp<Color?>(a?.thumbColor, b?.thumbColor, t, Color.lerp),
trackColor: MaterialStateProperty.lerp<Color?>(a?.trackColor, b?.trackColor, t, Color.lerp),
trackOutlineColor: MaterialStateProperty.lerp<Color?>(a?.trackOutlineColor, b?.trackOutlineColor, t, Color.lerp),
materialTapTargetSize: t < 0.5 ? a?.materialTapTargetSize : b?.materialTapTargetSize,
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
overlayColor: MaterialStateProperty.lerp<Color?>(a?.overlayColor, b?.overlayColor, t, Color.lerp),
Expand All @@ -123,6 +132,7 @@ class SwitchThemeData with Diagnosticable {
int get hashCode => Object.hash(
thumbColor,
trackColor,
trackOutlineColor,
materialTapTargetSize,
mouseCursor,
overlayColor,
Expand All @@ -141,6 +151,7 @@ class SwitchThemeData with Diagnosticable {
return other is SwitchThemeData
&& other.thumbColor == thumbColor
&& other.trackColor == trackColor
&& other.trackOutlineColor == trackOutlineColor
&& other.materialTapTargetSize == materialTapTargetSize
&& other.mouseCursor == mouseCursor
&& other.overlayColor == overlayColor
Expand All @@ -153,6 +164,7 @@ class SwitchThemeData with Diagnosticable {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('thumbColor', thumbColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('trackColor', trackColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('trackOutlineColor', trackOutlineColor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Color?>>('overlayColor', overlayColor, defaultValue: null));
Expand Down
Loading

0 comments on commit 3f98c0f

Please sign in to comment.