Skip to content

Commit a7e8eef

Browse files
justinmcazatech
authored andcommitted
Fix mutating Paint bug (flutter#171180)
A tricky bug was encountered in https://github.com/flutter/flutter/pull/170321/files#r2138619243 where mutating Paint caused [paints](https://main-api.flutter.dev/flutter/flutter_test/paints.html) to behave strangely. Originally this PR just added something to the docs for `paints` to give people a heads up, but now it fixes the root cause. Yak shave 🪒 . Fixes flutter#171194
1 parent 384308d commit a7e8eef

File tree

4 files changed

+844
-11
lines changed

4 files changed

+844
-11
lines changed

packages/flutter/test/cupertino/activity_indicator_test.dart

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,14 @@ void main() {
132132

133133
testWidgets('Can specify color', (WidgetTester tester) async {
134134
final Key key = UniqueKey();
135+
const Color color = Color(0xFF5D3FD3);
135136
await tester.pumpWidget(
136137
Center(
137138
child: RepaintBoundary(
138139
key: key,
139140
child: const ColoredBox(
140141
color: CupertinoColors.white,
141-
child: CupertinoActivityIndicator(
142-
animating: false,
143-
color: Color(0xFF5D3FD3),
144-
radius: 100,
145-
),
142+
child: CupertinoActivityIndicator(animating: false, color: color, radius: 100),
146143
),
147144
),
148145
),
@@ -152,7 +149,9 @@ void main() {
152149
find.byType(CupertinoActivityIndicator),
153150
paints..rrect(
154151
rrect: const RRect.fromLTRBXY(-10, -100 / 3, 10, -100, 10, 10),
155-
color: const Color(0x935d3fd3),
152+
// The value of 47 comes from the alpha that is applied to the first
153+
// tick.
154+
color: color.withAlpha(47),
156155
),
157156
);
158157
});

packages/flutter/test/material/progress_indicator_test.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,13 +1161,12 @@ void main() {
11611161
testWidgets(
11621162
'Adaptive CircularProgressIndicator can use backgroundColor to change tick color for iOS',
11631163
(WidgetTester tester) async {
1164+
const Color color = Color(0xFF5D3FD3);
11641165
await tester.pumpWidget(
11651166
MaterialApp(
11661167
theme: ThemeData(),
11671168
home: const Scaffold(
1168-
body: Material(
1169-
child: CircularProgressIndicator.adaptive(backgroundColor: Color(0xFF5D3FD3)),
1170-
),
1169+
body: Material(child: CircularProgressIndicator.adaptive(backgroundColor: color)),
11711170
),
11721171
),
11731172
);
@@ -1176,7 +1175,9 @@ void main() {
11761175
find.byType(CupertinoActivityIndicator),
11771176
paints..rrect(
11781177
rrect: const RRect.fromLTRBXY(-1, -10 / 3, 1, -10, 1, 1),
1179-
color: const Color(0x935D3FD3),
1178+
// The value of 47 comes from the alpha that is applied to the first
1179+
// tick.
1180+
color: color.withAlpha(47),
11801181
),
11811182
);
11821183
},

packages/flutter_test/lib/src/recording_canvas.dart

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
/// @docImport 'mock_canvas.dart';
66
library;
77

8+
import 'dart:ui' show Image, PointMode, Vertices;
9+
810
import 'package:flutter/foundation.dart';
911
import 'package:flutter/rendering.dart';
1012

@@ -80,7 +82,7 @@ class TestRecordingCanvas implements Canvas {
8082
_saveCount += 1;
8183
invocations.add(
8284
RecordedInvocation(
83-
_MethodCall(#saveLayer, <dynamic>[bounds, paint]),
85+
_MethodCall(#saveLayer, <dynamic>[bounds, Paint.from(paint)]),
8486
stack: StackTrace.current,
8587
),
8688
);
@@ -97,6 +99,224 @@ class TestRecordingCanvas implements Canvas {
9799
void noSuchMethod(Invocation invocation) {
98100
invocations.add(RecordedInvocation(invocation, stack: StackTrace.current));
99101
}
102+
103+
@override
104+
void drawCircle(Offset c, double radius, Paint paint) {
105+
invocations.add(
106+
RecordedInvocation(
107+
_MethodCall(#drawCircle, <dynamic>[c, radius, Paint.from(paint)]),
108+
stack: StackTrace.current,
109+
),
110+
);
111+
}
112+
113+
@override
114+
void drawRect(Rect rect, Paint paint) {
115+
invocations.add(
116+
RecordedInvocation(
117+
_MethodCall(#drawRect, <dynamic>[rect, Paint.from(paint)]),
118+
stack: StackTrace.current,
119+
),
120+
);
121+
}
122+
123+
@override
124+
void drawRRect(RRect rrect, Paint paint) {
125+
invocations.add(
126+
RecordedInvocation(
127+
_MethodCall(#drawRRect, <dynamic>[rrect, Paint.from(paint)]),
128+
stack: StackTrace.current,
129+
),
130+
);
131+
}
132+
133+
@override
134+
void drawDRRect(RRect outer, RRect inner, Paint paint) {
135+
invocations.add(
136+
RecordedInvocation(
137+
_MethodCall(#drawDRRect, <dynamic>[outer, inner, Paint.from(paint)]),
138+
stack: StackTrace.current,
139+
),
140+
);
141+
}
142+
143+
@override
144+
void drawPath(Path path, Paint paint) {
145+
invocations.add(
146+
RecordedInvocation(
147+
_MethodCall(#drawPath, <dynamic>[path, Paint.from(paint)]),
148+
stack: StackTrace.current,
149+
),
150+
);
151+
}
152+
153+
@override
154+
void drawLine(Offset p1, Offset p2, Paint paint) {
155+
invocations.add(
156+
RecordedInvocation(
157+
_MethodCall(#drawLine, <dynamic>[p1, p2, Paint.from(paint)]),
158+
stack: StackTrace.current,
159+
),
160+
);
161+
}
162+
163+
@override
164+
void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) {
165+
invocations.add(
166+
RecordedInvocation(
167+
_MethodCall(#drawArc, <dynamic>[
168+
rect,
169+
startAngle,
170+
sweepAngle,
171+
useCenter,
172+
Paint.from(paint),
173+
]),
174+
stack: StackTrace.current,
175+
),
176+
);
177+
}
178+
179+
@override
180+
void drawPaint(Paint paint) {
181+
invocations.add(
182+
RecordedInvocation(
183+
_MethodCall(#drawPaint, <dynamic>[Paint.from(paint)]),
184+
stack: StackTrace.current,
185+
),
186+
);
187+
}
188+
189+
@override
190+
void drawRSuperellipse(RSuperellipse rsuperellipse, Paint paint) {
191+
invocations.add(
192+
RecordedInvocation(
193+
_MethodCall(#drawRSuperellipse, <dynamic>[rsuperellipse, Paint.from(paint)]),
194+
stack: StackTrace.current,
195+
),
196+
);
197+
}
198+
199+
@override
200+
void drawOval(Rect rect, Paint paint) {
201+
invocations.add(
202+
RecordedInvocation(
203+
_MethodCall(#drawOval, <dynamic>[rect, Paint.from(paint)]),
204+
stack: StackTrace.current,
205+
),
206+
);
207+
}
208+
209+
@override
210+
void drawImage(Image image, Offset offset, Paint paint) {
211+
invocations.add(
212+
RecordedInvocation(
213+
_MethodCall(#drawImage, <dynamic>[image, offset, Paint.from(paint)]),
214+
stack: StackTrace.current,
215+
),
216+
);
217+
}
218+
219+
@override
220+
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
221+
invocations.add(
222+
RecordedInvocation(
223+
_MethodCall(#drawImageRect, <dynamic>[image, src, dst, Paint.from(paint)]),
224+
stack: StackTrace.current,
225+
),
226+
);
227+
}
228+
229+
@override
230+
void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
231+
invocations.add(
232+
RecordedInvocation(
233+
_MethodCall(#drawImageNine, <dynamic>[image, center, dst, Paint.from(paint)]),
234+
stack: StackTrace.current,
235+
),
236+
);
237+
}
238+
239+
@override
240+
void drawPoints(PointMode pointMode, List<Offset> points, Paint paint) {
241+
invocations.add(
242+
RecordedInvocation(
243+
_MethodCall(#drawPoints, <dynamic>[pointMode, points, Paint.from(paint)]),
244+
stack: StackTrace.current,
245+
),
246+
);
247+
}
248+
249+
@override
250+
void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) {
251+
invocations.add(
252+
RecordedInvocation(
253+
_MethodCall(#drawRawPoints, <dynamic>[pointMode, points, Paint.from(paint)]),
254+
stack: StackTrace.current,
255+
),
256+
);
257+
}
258+
259+
@override
260+
void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) {
261+
invocations.add(
262+
RecordedInvocation(
263+
_MethodCall(#drawVertices, <dynamic>[vertices, blendMode, Paint.from(paint)]),
264+
stack: StackTrace.current,
265+
),
266+
);
267+
}
268+
269+
@override
270+
void drawAtlas(
271+
Image atlas,
272+
List<RSTransform> transforms,
273+
List<Rect> rects,
274+
List<Color>? colors,
275+
BlendMode? blendMode,
276+
Rect? cullRect,
277+
Paint paint,
278+
) {
279+
invocations.add(
280+
RecordedInvocation(
281+
_MethodCall(#drawAtlas, <dynamic>[
282+
atlas,
283+
transforms,
284+
rects,
285+
colors,
286+
blendMode,
287+
cullRect,
288+
Paint.from(paint),
289+
]),
290+
stack: StackTrace.current,
291+
),
292+
);
293+
}
294+
295+
@override
296+
void drawRawAtlas(
297+
Image atlas,
298+
Float32List rstTransforms,
299+
Float32List rects,
300+
Int32List? colors,
301+
BlendMode? blendMode,
302+
Rect? cullRect,
303+
Paint paint,
304+
) {
305+
invocations.add(
306+
RecordedInvocation(
307+
_MethodCall(#drawRawAtlas, <dynamic>[
308+
atlas,
309+
rstTransforms,
310+
rects,
311+
colors,
312+
blendMode,
313+
cullRect,
314+
Paint.from(paint),
315+
]),
316+
stack: StackTrace.current,
317+
),
318+
);
319+
}
100320
}
101321

102322
/// A [PaintingContext] for tests that use [TestRecordingCanvas].

0 commit comments

Comments
 (0)