Fixed the color curve issue (#115188)
This commit is contained in:
parent
436fb4c6bd
commit
244990775a
@ -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
|
// During a drag we may have modified the curve, reset it if its possible
|
||||||
// to do without visual discontinuation.
|
// to do without visual discontinuation.
|
||||||
if (position.value == 0.0 || position.value == 1.0) {
|
if (position.value == 0.0 || position.value == 1.0) {
|
||||||
position
|
if (Theme.of(context).useMaterial3) {
|
||||||
..curve = Curves.easeIn
|
position
|
||||||
..reverseCurve = Curves.easeOut;
|
..curve = Curves.easeOutBack
|
||||||
|
..reverseCurve = Curves.easeOutBack.flipped;
|
||||||
|
} else {
|
||||||
|
position
|
||||||
|
..curve = Curves.easeIn
|
||||||
|
..reverseCurve = Curves.easeOut;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
animateToValue();
|
animateToValue();
|
||||||
}
|
}
|
||||||
@ -693,7 +699,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
|
|||||||
|
|
||||||
void _handleDragEnd(DragEndDetails details) {
|
void _handleDragEnd(DragEndDetails details) {
|
||||||
if (position.value >= 0.5 != widget.value) {
|
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
|
// Wait with finishing the animation until widget.value has changed to
|
||||||
// !widget.value as part of the widget.onChanged call above.
|
// !widget.value as part of the widget.onChanged call above.
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -709,7 +715,7 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
|
|||||||
void _handleChanged(bool? value) {
|
void _handleChanged(bool? value) {
|
||||||
assert(value != null);
|
assert(value != null);
|
||||||
assert(widget.onChanged != null);
|
assert(widget.onChanged != null);
|
||||||
widget.onChanged!(value!);
|
widget.onChanged?.call(value!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -727,11 +733,6 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
|
|||||||
final SwitchThemeData defaults = theme.useMaterial3 ? _SwitchDefaultsM3(context) : _SwitchDefaultsM2(context);
|
final SwitchThemeData defaults = theme.useMaterial3 ? _SwitchDefaultsM3(context) : _SwitchDefaultsM2(context);
|
||||||
|
|
||||||
positionController.duration = Duration(milliseconds: switchConfig.toggleDuration);
|
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
|
// Colors need to be resolved in selected and non selected states separately
|
||||||
// so that they can be lerped between.
|
// so that they can be lerped between.
|
||||||
@ -780,12 +781,20 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
|
|||||||
?? defaults.overlayColor!.resolve(hoveredStates)!;
|
?? defaults.overlayColor!.resolve(hoveredStates)!;
|
||||||
|
|
||||||
final Set<MaterialState> activePressedStates = activeStates..add(MaterialState.pressed);
|
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)
|
final Color effectiveActivePressedOverlayColor = widget.overlayColor?.resolve(activePressedStates)
|
||||||
?? switchTheme.overlayColor?.resolve(activePressedStates)
|
?? switchTheme.overlayColor?.resolve(activePressedStates)
|
||||||
?? activeThumbColor?.withAlpha(kRadialReactionAlpha)
|
?? activeThumbColor?.withAlpha(kRadialReactionAlpha)
|
||||||
?? defaults.overlayColor!.resolve(activePressedStates)!;
|
?? defaults.overlayColor!.resolve(activePressedStates)!;
|
||||||
|
|
||||||
final Set<MaterialState> inactivePressedStates = inactiveStates..add(MaterialState.pressed);
|
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)
|
final Color effectiveInactivePressedOverlayColor = widget.overlayColor?.resolve(inactivePressedStates)
|
||||||
?? switchTheme.overlayColor?.resolve(inactivePressedStates)
|
?? switchTheme.overlayColor?.resolve(inactivePressedStates)
|
||||||
?? inactiveThumbColor?.withAlpha(kRadialReactionAlpha)
|
?? inactiveThumbColor?.withAlpha(kRadialReactionAlpha)
|
||||||
@ -830,6 +839,8 @@ class _MaterialSwitchState extends State<_MaterialSwitch> with TickerProviderSta
|
|||||||
..isHovered = states.contains(MaterialState.hovered)
|
..isHovered = states.contains(MaterialState.hovered)
|
||||||
..activeColor = effectiveActiveThumbColor
|
..activeColor = effectiveActiveThumbColor
|
||||||
..inactiveColor = effectiveInactiveThumbColor
|
..inactiveColor = effectiveInactiveThumbColor
|
||||||
|
..activePressedColor = effectiveActivePressedThumbColor
|
||||||
|
..inactivePressedColor = effectiveInactivePressedThumbColor
|
||||||
..activeThumbImage = widget.activeThumbImage
|
..activeThumbImage = widget.activeThumbImage
|
||||||
..onActiveThumbImageError = widget.onActiveThumbImageError
|
..onActiveThumbImageError = widget.onActiveThumbImageError
|
||||||
..inactiveThumbImage = widget.inactiveThumbImage
|
..inactiveThumbImage = widget.inactiveThumbImage
|
||||||
@ -926,6 +937,28 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
notifyListeners();
|
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 get activeThumbRadius => _activeThumbRadius!;
|
||||||
double? _activeThumbRadius;
|
double? _activeThumbRadius;
|
||||||
set activeThumbRadius(double value) {
|
set activeThumbRadius(double value) {
|
||||||
@ -1180,7 +1213,7 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
visualPosition = currentValue;
|
visualPosition = currentValue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (reaction.status == AnimationStatus.reverse && _stopPressAnimation == false) {
|
if (reaction.status == AnimationStatus.reverse && !_stopPressAnimation) {
|
||||||
_stopPressAnimation = true;
|
_stopPressAnimation = true;
|
||||||
} else {
|
} else {
|
||||||
_stopPressAnimation = false;
|
_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
|
// To get the thumb radius when the press ends, the value can be any number
|
||||||
// between activeThumbRadius/inactiveThumbRadius and pressedThumbRadius.
|
// between activeThumbRadius/inactiveThumbRadius and pressedThumbRadius.
|
||||||
if (!_stopPressAnimation) {
|
if (!_stopPressAnimation) {
|
||||||
if (reaction.status == AnimationStatus.completed) {
|
if (reaction.isCompleted) {
|
||||||
// This happens when the thumb is dragged instead of being tapped.
|
// This happens when the thumb is dragged instead of being tapped.
|
||||||
_pressedInactiveThumbRadius = lerpDouble(inactiveThumbRadius, pressedThumbRadius, reaction.value);
|
_pressedInactiveThumbRadius = lerpDouble(inactiveThumbRadius, pressedThumbRadius, reaction.value);
|
||||||
_pressedActiveThumbRadius = lerpDouble(activeThumbRadius, pressedThumbRadius, reaction.value);
|
_pressedActiveThumbRadius = lerpDouble(activeThumbRadius, pressedThumbRadius, reaction.value);
|
||||||
@ -1248,10 +1281,10 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Size thumbSize;
|
Size thumbSize;
|
||||||
if (reaction.status == AnimationStatus.completed) {
|
if (reaction.isCompleted) {
|
||||||
thumbSize = Size.fromRadius(pressedThumbRadius);
|
thumbSize = Size.fromRadius(pressedThumbRadius);
|
||||||
} else {
|
} else {
|
||||||
if (position.status == AnimationStatus.dismissed || position.status == AnimationStatus.forward) {
|
if (position.isDismissed || position.status == AnimationStatus.forward) {
|
||||||
thumbSize = thumbSizeAnimation(true).value;
|
thumbSize = thumbSizeAnimation(true).value;
|
||||||
} else {
|
} else {
|
||||||
thumbSize = thumbSizeAnimation(false).value;
|
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;
|
final double inset = thumbOffset == null ? 0 : 1.0 - (currentValue - thumbOffset!).abs() * 2.0;
|
||||||
thumbSize = Size(thumbSize.width - inset, thumbSize.height - inset);
|
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
|
final Color? trackOutlineColor = inactiveTrackOutlineColor == null ? null
|
||||||
: Color.lerp(inactiveTrackOutlineColor, Colors.transparent, currentValue);
|
: Color.lerp(inactiveTrackOutlineColor, Colors.transparent, colorValue);
|
||||||
final Color lerpedThumbColor = Color.lerp(inactiveColor, activeColor, currentValue)!;
|
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
|
// 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
|
// thumbColor is not opaque. This way we do not see through the thumb to the
|
||||||
// track underneath.
|
// track underneath.
|
||||||
@ -1289,7 +1333,7 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
_paintThumbWith(
|
_paintThumbWith(
|
||||||
thumbPaintOffset,
|
thumbPaintOffset,
|
||||||
canvas,
|
canvas,
|
||||||
currentValue,
|
colorValue,
|
||||||
thumbColor,
|
thumbColor,
|
||||||
thumbImage,
|
thumbImage,
|
||||||
thumbErrorListener,
|
thumbErrorListener,
|
||||||
@ -1381,7 +1425,7 @@ class _SwitchPainter extends ToggleablePainter {
|
|||||||
|
|
||||||
thumbPainter.paint(
|
thumbPainter.paint(
|
||||||
canvas,
|
canvas,
|
||||||
thumbPaintOffset + Offset(0, inset),
|
thumbPaintOffset,
|
||||||
configuration.copyWith(size: thumbSize),
|
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 {
|
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 ThemeData themeData = ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xff6750a4), brightness: Brightness.light);
|
||||||
final ColorScheme colors = themeData.colorScheme;
|
final ColorScheme colors = themeData.colorScheme;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user