Unverified Commit 24499077 authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Fixed the color curve issue (#115188)

parent 436fb4c6
......@@ -620,9 +620,15 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
// During a drag we may have modified the curve, reset it if its possible
// to do without visual discontinuation.
if (position.value == 0.0 || position.value == 1.0) {
position
..curve = Curves.easeIn
..reverseCurve = Curves.easeOut;
if (Theme.of(context).useMaterial3) {
position
..curve = Curves.easeOutBack
..reverseCurve = Curves.easeOutBack.flipped;
} else {
position
..curve = Curves.easeIn
..reverseCurve = Curves.easeOut;
}
}
animateToValue();
}
......@@ -693,7 +699,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
void _handleDragEnd(DragEndDetails details) {
if (position.value >= 0.5 != widget.value) {
widget.onChanged!(!widget.value);
widget.onChanged?.call(!widget.value);
// Wait with finishing the animation until widget.value has changed to
// !widget.value as part of the widget.onChanged call above.
setState(() {
......@@ -709,7 +715,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
void _handleChanged(bool? value) {
assert(value != null);
assert(widget.onChanged != null);
widget.onChanged!(value!);
widget.onChanged?.call(value!);
}
@override
......@@ -727,11 +733,6 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
final SwitchThemeData defaults = theme.useMaterial3 ? _SwitchDefaultsM3(context) : _SwitchDefaultsM2(context);
positionController.duration = Duration(milliseconds: switchConfig.toggleDuration);
if (theme.useMaterial3) {
position
..curve = Curves.easeOutBack
..reverseCurve = Curves.easeOutBack.flipped;
}
// Colors need to be resolved in selected and non selected states separately
// so that they can be lerped between.
......@@ -780,12 +781,20 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
?? defaults.overlayColor!.resolve(hoveredStates)!;
final Set<MaterialState> activePressedStates = activeStates..add(MaterialState.pressed);
final Color effectiveActivePressedThumbColor = widget.thumbColor?.resolve(activePressedStates)
?? _widgetThumbColor.resolve(activePressedStates)
?? switchTheme.thumbColor?.resolve(activePressedStates)
?? defaults.thumbColor!.resolve(activePressedStates)!;
final Color effectiveActivePressedOverlayColor = widget.overlayColor?.resolve(activePressedStates)
?? switchTheme.overlayColor?.resolve(activePressedStates)
?? activeThumbColor?.withAlpha(kRadialReactionAlpha)
?? defaults.overlayColor!.resolve(activePressedStates)!;
final Set<MaterialState> inactivePressedStates = inactiveStates..add(MaterialState.pressed);
final Color effectiveInactivePressedThumbColor = widget.thumbColor?.resolve(inactivePressedStates)
?? _widgetThumbColor.resolve(inactivePressedStates)
?? switchTheme.thumbColor?.resolve(inactivePressedStates)
?? defaults.thumbColor!.resolve(inactivePressedStates)!;
final Color effectiveInactivePressedOverlayColor = widget.overlayColor?.resolve(inactivePressedStates)
?? switchTheme.overlayColor?.resolve(inactivePressedStates)
?? inactiveThumbColor?.withAlpha(kRadialReactionAlpha)
......@@ -830,6 +839,8 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
..isHovered = states.contains(MaterialState.hovered)
..activeColor = effectiveActiveThumbColor
..inactiveColor = effectiveInactiveThumbColor
..activePressedColor = effectiveActivePressedThumbColor
..inactivePressedColor = effectiveInactivePressedThumbColor
..activeThumbImage = widget.activeThumbImage
..onActiveThumbImageError = widget.onActiveThumbImageError
..inactiveThumbImage = widget.inactiveThumbImage
......@@ -926,6 +937,28 @@ class _SwitchPainter extends ToggleablePainter {
notifyListeners();
}
Color get activePressedColor => _activePressedColor!;
Color? _activePressedColor;
set activePressedColor(Color? value) {
assert(value != null);
if (value == _activePressedColor) {
return;
}
_activePressedColor = value;
notifyListeners();
}
Color get inactivePressedColor => _inactivePressedColor!;
Color? _inactivePressedColor;
set inactivePressedColor(Color? value) {
assert(value != null);
if (value == _inactivePressedColor) {
return;
}
_inactivePressedColor = value;
notifyListeners();
}
double get activeThumbRadius => _activeThumbRadius!;
double? _activeThumbRadius;
set activeThumbRadius(double value) {
......@@ -1180,7 +1213,7 @@ class _SwitchPainter extends ToggleablePainter {
visualPosition = currentValue;
break;
}
if (reaction.status == AnimationStatus.reverse && _stopPressAnimation == false) {
if (reaction.status == AnimationStatus.reverse && !_stopPressAnimation) {
_stopPressAnimation = true;
} else {
_stopPressAnimation = false;
......@@ -1189,7 +1222,7 @@ class _SwitchPainter extends ToggleablePainter {
// To get the thumb radius when the press ends, the value can be any number
// between activeThumbRadius/inactiveThumbRadius and pressedThumbRadius.
if (!_stopPressAnimation) {
if (reaction.status == AnimationStatus.completed) {
if (reaction.isCompleted) {
// This happens when the thumb is dragged instead of being tapped.
_pressedInactiveThumbRadius = lerpDouble(inactiveThumbRadius, pressedThumbRadius, reaction.value);
_pressedActiveThumbRadius = lerpDouble(activeThumbRadius, pressedThumbRadius, reaction.value);
......@@ -1248,10 +1281,10 @@ class _SwitchPainter extends ToggleablePainter {
}
Size thumbSize;
if (reaction.status == AnimationStatus.completed) {
if (reaction.isCompleted) {
thumbSize = Size.fromRadius(pressedThumbRadius);
} else {
if (position.status == AnimationStatus.dismissed || position.status == AnimationStatus.forward) {
if (position.isDismissed || position.status == AnimationStatus.forward) {
thumbSize = thumbSizeAnimation(true).value;
} else {
thumbSize = thumbSizeAnimation(false).value;
......@@ -1262,10 +1295,21 @@ class _SwitchPainter extends ToggleablePainter {
final double inset = thumbOffset == null ? 0 : 1.0 - (currentValue - thumbOffset!).abs() * 2.0;
thumbSize = Size(thumbSize.width - inset, thumbSize.height - inset);
final Color trackColor = Color.lerp(inactiveTrackColor, activeTrackColor, currentValue)!;
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, currentValue);
final Color lerpedThumbColor = Color.lerp(inactiveColor, activeColor, currentValue)!;
: Color.lerp(inactiveTrackOutlineColor, Colors.transparent, colorValue);
Color lerpedThumbColor;
if (!reaction.isDismissed) {
lerpedThumbColor = Color.lerp(inactivePressedColor, activePressedColor, colorValue)!;
} else if (positionController.status == AnimationStatus.forward) {
lerpedThumbColor = Color.lerp(inactivePressedColor, activeColor, colorValue)!;
} else if (positionController.status == AnimationStatus.reverse) {
lerpedThumbColor = Color.lerp(inactiveColor, activePressedColor, colorValue)!;
} else {
lerpedThumbColor = Color.lerp(inactiveColor, activeColor, colorValue)!;
}
// Blend the thumb color against a `surfaceColor` background in case the
// thumbColor is not opaque. This way we do not see through the thumb to the
// track underneath.
......@@ -1289,7 +1333,7 @@ class _SwitchPainter extends ToggleablePainter {
_paintThumbWith(
thumbPaintOffset,
canvas,
currentValue,
colorValue,
thumbColor,
thumbImage,
thumbErrorListener,
......@@ -1381,7 +1425,7 @@ class _SwitchPainter extends ToggleablePainter {
thumbPainter.paint(
canvas,
thumbPaintOffset + Offset(0, inset),
thumbPaintOffset,
configuration.copyWith(size: thumbSize),
);
......
......@@ -2438,6 +2438,89 @@ void main() {
);
});
testWidgets('Switch thumb shows correct pressed color - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true);
final ColorScheme colors = themeData.colorScheme;
Widget buildApp({bool enabled = true, bool value = true}) {
return MaterialApp(
theme: themeData,
home: Material(
child: Center(
child: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Switch(
value: value,
onChanged: enabled ? (bool newValue) {
setState(() {
value = newValue;
});
} : null,
);
}),
),
),
);
}
await tester.pumpWidget(buildApp());
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.primary, // track color
style: PaintingStyle.fill,
)..rrect(
color: Colors.transparent, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.primaryContainer, rrect: RRect.fromLTRBR(26.0, 10.0, 54.0, 38.0, const Radius.circular(14.0))),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(value: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.surfaceVariant, // track color
style: PaintingStyle.fill
)..rrect(
color: colors.outline, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.onSurfaceVariant),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(enabled: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.onSurface.withOpacity(0.12), // track color
style: PaintingStyle.fill,
)..rrect(
color: Colors.transparent, // track outline color
style: PaintingStyle.stroke,
)..rrect(color: colors.surface.withOpacity(1.0)),
);
await tester.pumpWidget(Container());
await tester.pumpWidget(buildApp(enabled: false, value: false));
await tester.press(find.byType(Switch));
await tester.pumpAndSettle();
expect(Material.of(tester.element(find.byType(Switch))),
paints..rrect(
color: colors.surfaceVariant.withOpacity(0.12), // track color
style: PaintingStyle.fill,
)..rrect(
color: colors.onSurface.withOpacity(0.12), // track outline color
style: PaintingStyle.stroke,
)..rrect(color: Color.alphaBlend(colors.onSurface.withOpacity(0.38), colors.surface)),
);
}, variant: TargetPlatformVariant.mobile());
testWidgets('Switch thumb color resolves in active/enabled states - M3', (WidgetTester tester) async {
final ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
final ColorScheme colors = themeData.colorScheme;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment