Skip to content

Commit 8ffe7c5

Browse files
authored
Fix painting material toggle (flutter#78733) (flutter#78744)
1 parent a1a360b commit 8ffe7c5

File tree

3 files changed

+109
-13
lines changed

3 files changed

+109
-13
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,4 @@ Ludwik Trammer <[email protected]>
8080
Marian Triebe <[email protected]>
8181
Alexis Rouillard <[email protected]>
8282
Mirko Mucaria <[email protected]>
83+
Karol Czeryna <[email protected]>

packages/flutter/lib/src/material/switch.dart

+57-13
Original file line numberDiff line numberDiff line change
@@ -912,26 +912,69 @@ class _SwitchPainter extends ToggleablePainter {
912912
? (currentValue < 0.5 ? onInactiveThumbImageError : onActiveThumbImageError)
913913
: onInactiveThumbImageError;
914914

915-
// Paint the track
916915
final Paint paint = Paint()
917916
..color = trackColor;
918-
const double trackHorizontalPadding = kRadialReactionRadius - _kTrackRadius;
917+
918+
final Offset trackPaintOffset = _computeTrackPaintOffset(size, _kTrackWidth, _kTrackHeight);
919+
final Offset thumbPaintOffset = _computeThumbPaintOffset(trackPaintOffset, visualPosition);
920+
final Offset radialReactionOrigin = Offset(thumbPaintOffset.dx + _kThumbRadius, size.height / 2);
921+
922+
_paintTrackWith(canvas, paint, trackPaintOffset);
923+
paintRadialReaction(canvas: canvas, origin: radialReactionOrigin);
924+
_paintThumbWith(
925+
thumbPaintOffset,
926+
canvas,
927+
currentValue,
928+
thumbColor,
929+
thumbImage,
930+
thumbErrorListener,
931+
);
932+
}
933+
934+
/// Computes canvas offset for track's upper left corner
935+
Offset _computeTrackPaintOffset(Size canvasSize, double trackWidth, double trackHeight) {
936+
final double horizontalOffset = (canvasSize.width - _kTrackWidth) / 2.0;
937+
final double verticalOffset = (canvasSize.height - _kTrackHeight) / 2.0;
938+
939+
return Offset(horizontalOffset, verticalOffset);
940+
}
941+
942+
/// Computes canvas offset for thumb's upper left corner as if it were a
943+
/// square
944+
Offset _computeThumbPaintOffset(Offset trackPaintOffset, double visualPosition) {
945+
// How much thumb radius extends beyond the track
946+
const double additionalThumbRadius = _kThumbRadius - _kTrackRadius;
947+
948+
final double horizontalProgress = visualPosition * trackInnerLength;
949+
final double thumbHorizontalOffset = trackPaintOffset.dx - additionalThumbRadius + horizontalProgress;
950+
final double thumbVerticalOffset = trackPaintOffset.dy - additionalThumbRadius;
951+
952+
return Offset(thumbHorizontalOffset, thumbVerticalOffset);
953+
}
954+
955+
void _paintTrackWith(Canvas canvas, Paint paint, Offset trackPaintOffset) {
919956
final Rect trackRect = Rect.fromLTWH(
920-
trackHorizontalPadding,
921-
(size.height - _kTrackHeight) / 2.0,
922-
size.width - 2.0 * trackHorizontalPadding,
957+
trackPaintOffset.dx,
958+
trackPaintOffset.dy,
959+
_kTrackWidth,
923960
_kTrackHeight,
924961
);
925-
final RRect trackRRect = RRect.fromRectAndRadius(trackRect, const Radius.circular(_kTrackRadius));
926-
canvas.drawRRect(trackRRect, paint);
927-
928-
final Offset thumbPosition = Offset(
929-
kRadialReactionRadius + visualPosition * trackInnerLength,
930-
size.height / 2.0,
962+
final RRect trackRRect = RRect.fromRectAndRadius(
963+
trackRect,
964+
const Radius.circular(_kTrackRadius),
931965
);
932966

933-
paintRadialReaction(canvas: canvas, origin: thumbPosition);
967+
canvas.drawRRect(trackRRect, paint);
968+
}
934969

970+
void _paintThumbWith(
971+
Offset thumbPaintOffset,
972+
Canvas canvas,
973+
double currentValue,
974+
Color thumbColor,
975+
ImageProvider? thumbImage,
976+
ImageErrorListener? thumbErrorListener,
977+
) {
935978
try {
936979
_isPainting = true;
937980
if (_cachedThumbPainter == null || thumbColor != _cachedThumbColor || thumbImage != _cachedThumbImage || thumbErrorListener != _cachedThumbErrorListener) {
@@ -946,9 +989,10 @@ class _SwitchPainter extends ToggleablePainter {
946989
// The thumb contracts slightly during the animation
947990
final double inset = 1.0 - (currentValue - 0.5).abs() * 2.0;
948991
final double radius = _kThumbRadius - inset;
992+
949993
thumbPainter.paint(
950994
canvas,
951-
thumbPosition - Offset(radius, radius),
995+
thumbPaintOffset + Offset(0, inset),
952996
configuration.copyWith(size: Size.fromRadius(radius)),
953997
);
954998
} finally {

packages/flutter/test/material/switch_test.dart

+51
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,57 @@ void main() {
9292
expect(tester.getSize(find.byType(Switch)), const Size(59.0, 40.0));
9393
});
9494

95+
testWidgets('Switch does not get distorted upon changing constraints with parent', (WidgetTester tester) async {
96+
const double maxWidth = 300;
97+
const double maxHeight = 100;
98+
99+
const ValueKey<String> boundaryKey = ValueKey<String>('switch container');
100+
101+
Widget buildSwitch({required double width, required double height}) {
102+
return MaterialApp(
103+
home: Scaffold(
104+
body: Directionality(
105+
textDirection: TextDirection.ltr,
106+
child: SizedBox(
107+
width: maxWidth,
108+
height: maxHeight,
109+
child: RepaintBoundary(
110+
key: boundaryKey,
111+
child: SizedBox(
112+
width: width,
113+
height: height,
114+
child: Switch(
115+
dragStartBehavior: DragStartBehavior.down,
116+
value: true,
117+
onChanged: (_) {},
118+
),
119+
),
120+
),
121+
),
122+
),
123+
),
124+
);
125+
}
126+
127+
await tester.pumpWidget(buildSwitch(
128+
width: maxWidth,
129+
height: maxHeight,
130+
));
131+
await expectLater(
132+
find.byKey(boundaryKey),
133+
matchesGoldenFile('switch_test.big.on.png'),
134+
);
135+
136+
await tester.pumpWidget(buildSwitch(
137+
width: 20,
138+
height: 10,
139+
));
140+
await expectLater(
141+
find.byKey(boundaryKey),
142+
matchesGoldenFile('switch_test.small.on.png'),
143+
);
144+
});
145+
95146
testWidgets('Switch can drag (LTR)', (WidgetTester tester) async {
96147
bool value = false;
97148

0 commit comments

Comments
 (0)