1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This file is run as part of a reduced test set in CI on Mac and Windows
6// machines.
7@Tags(<String>['reduced-test-set'])
8library;
9
10import 'package:flutter/cupertino.dart';
11import 'package:flutter/foundation.dart';
12import 'package:flutter_test/flutter_test.dart';
13
14class _FilterTest extends StatelessWidget {
15 const _FilterTest(Widget child, {this.brightness = Brightness.light}) : _child = child;
16 final Brightness brightness;
17 final Widget _child;
18
19 @override
20 Widget build(BuildContext context) {
21 final Size size = MediaQuery.sizeOf(context);
22 final double tileHeight = size.height / 4;
23 final double tileWidth = size.width / 8;
24 return CupertinoApp(
25 home: Stack(
26 fit: StackFit.expand,
27 children: <Widget>[
28 // 512 color tiles
29 // 4 alpha levels (0.416, 0.25, 0.5, 0.75)
30 for (int a = 0; a < 4; a++)
31 for (int h = 0; h < 8; h++) // 8 hues
32 for (int s = 0; s < 4; s++) // 4 saturation levels
33 for (int b = 0; b < 4; b++) // 4 brightness levels
34 Positioned(
35 left: h * tileWidth + b * tileWidth / 4,
36 top: a * tileHeight + s * tileHeight / 4,
37 height: tileHeight,
38 width: tileWidth,
39 child: ColoredBox(
40 color: HSVColor.fromAHSV(
41 0.5 + a / 8,
42 h * 45,
43 0.5 + s / 8,
44 0.5 + b / 8,
45 ).toColor(),
46 ),
47 ),
48 Padding(
49 padding: const EdgeInsets.all(32),
50 child: CupertinoTheme(
51 data: CupertinoThemeData(brightness: brightness),
52 child: _child,
53 ),
54 ),
55 ],
56 ),
57 );
58 }
59}
60
61void main() {
62 void disableVibranceForTest() {
63 CupertinoPopupSurface.debugIsVibrancePainted = false;
64 addTearDown(() {
65 CupertinoPopupSurface.debugIsVibrancePainted = true;
66 });
67 }
68
69 // Golden displays the color filter effect of the CupertinoPopupSurface
70 // when the ambient brightness is light.
71 testWidgets(
72 'Brightness.light color filter',
73 (WidgetTester tester) async {
74 await tester.pumpWidget(
75 const _FilterTest(
76 CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()),
77 ),
78 );
79
80 await expectLater(
81 find.byType(CupertinoApp),
82 matchesGoldenFile('cupertinoPopupSurface.color-filter.light.png'),
83 );
84 },
85 skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026
86 );
87
88 // Golden displays the color filter effect of the CupertinoPopupSurface
89 // when the ambient brightness is dark.
90 testWidgets(
91 'Brightness.dark color filter',
92 (WidgetTester tester) async {
93 await tester.pumpWidget(
94 const _FilterTest(
95 CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()),
96 brightness: Brightness.dark,
97 ),
98 );
99
100 await expectLater(
101 find.byType(CupertinoApp),
102 matchesGoldenFile('cupertinoPopupSurface.color-filter.dark.png'),
103 );
104 },
105 skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026
106 );
107
108 // Golden displays color tiles without CupertinoPopupSurface being
109 // displayed.
110 testWidgets('Setting debugIsVibrancePainted to false removes the color filter', (
111 WidgetTester tester,
112 ) async {
113 disableVibranceForTest();
114 await tester.pumpWidget(
115 const _FilterTest(
116 CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()),
117 ),
118 );
119
120 // The BackdropFilter widget should not be mounted when blurSigma is 0 and
121 // CupertinoPopupSurface.debugIsVibrancePainted is false.
122 expect(
123 find.descendant(
124 of: find.byType(CupertinoPopupSurface),
125 matching: find.byType(BackdropFilter),
126 ),
127 findsNothing,
128 );
129
130 await expectLater(
131 find.byType(CupertinoApp),
132 matchesGoldenFile('cupertinoPopupSurface.color-filter.removed.png'),
133 );
134 });
135
136 // Golden displays the surface color of the CupertinoPopupSurface
137 // in light mode.
138 testWidgets('Brightness.light surface color', (WidgetTester tester) async {
139 disableVibranceForTest();
140 await tester.pumpWidget(
141 const _FilterTest(CupertinoPopupSurface(blurSigma: 0, child: SizedBox())),
142 );
143
144 await expectLater(
145 find.byType(CupertinoApp),
146 matchesGoldenFile('cupertinoPopupSurface.surface-color.light.png'),
147 );
148 });
149
150 // Golden displays the surface color of the CupertinoPopupSurface
151 // in dark mode.
152 testWidgets('Brightness.dark surface color', (WidgetTester tester) async {
153 disableVibranceForTest();
154 await tester.pumpWidget(
155 const _FilterTest(
156 CupertinoPopupSurface(blurSigma: 0, child: SizedBox()),
157 brightness: Brightness.dark,
158 ),
159 );
160
161 await expectLater(
162 find.byType(CupertinoApp),
163 matchesGoldenFile('cupertinoPopupSurface.surface-color.dark.png'),
164 );
165 });
166
167 // Golden displays a CupertinoPopupSurface with the color removed. The result
168 // should only display color tiles.
169 testWidgets('Setting isSurfacePainted to false removes the surface color', (
170 WidgetTester tester,
171 ) async {
172 disableVibranceForTest();
173 await tester.pumpWidget(
174 const _FilterTest(
175 CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()),
176 brightness: Brightness.dark,
177 ),
178 );
179
180 await expectLater(
181 find.byType(CupertinoApp),
182 matchesGoldenFile('cupertinoPopupSurface.surface-color.removed.png'),
183 );
184 });
185
186 // Goldens display a CupertinoPopupSurface with no vibrance or surface
187 // color, with blur sigmas of 5 and 30 (default).
188 testWidgets(
189 'Positive blurSigma applies blur',
190 (WidgetTester tester) async {
191 disableVibranceForTest();
192 await tester.pumpWidget(
193 const _FilterTest(
194 CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 5, child: SizedBox()),
195 ),
196 );
197
198 await expectLater(
199 find.byType(CupertinoApp),
200 matchesGoldenFile('cupertinoPopupSurface.blur.5.png'),
201 );
202
203 await tester.pumpWidget(
204 const _FilterTest(CupertinoPopupSurface(isSurfacePainted: false, child: SizedBox())),
205 );
206
207 await expectLater(
208 find.byType(CupertinoApp),
209 // 30 is the default blur sigma
210 matchesGoldenFile('cupertinoPopupSurface.blur.30.png'),
211 );
212 },
213 skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026
214 );
215
216 // Golden displays a CupertinoPopupSurface with a blur sigma of 0. Because
217 // the blur sigma is 0 and vibrance and surface are not painted, no popup
218 // surface is displayed.
219 testWidgets('Setting blurSigma to zero removes blur', (WidgetTester tester) async {
220 disableVibranceForTest();
221 await tester.pumpWidget(
222 const _FilterTest(
223 CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()),
224 ),
225 );
226
227 // The BackdropFilter widget should not be mounted when blurSigma is 0 and
228 // CupertinoPopupSurface.isVibrancePainted is false.
229 expect(
230 find.descendant(
231 of: find.byType(CupertinoPopupSurface),
232 matching: find.byType(BackdropFilter),
233 ),
234 findsNothing,
235 );
236
237 await expectLater(
238 find.byType(CupertinoApp),
239 matchesGoldenFile('cupertinoPopupSurface.blur.0.png'),
240 );
241
242 await tester.pumpWidget(
243 const _FilterTest(
244 CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()),
245 ),
246 );
247 });
248
249 testWidgets('Setting a blurSigma to a negative number throws', (WidgetTester tester) async {
250 try {
251 disableVibranceForTest();
252 await tester.pumpWidget(
253 _FilterTest(
254 CupertinoPopupSurface(isSurfacePainted: false, blurSigma: -1, child: const SizedBox()),
255 ),
256 );
257
258 fail('CupertinoPopupSurface did not throw when provided a negative blur sigma.');
259 } on AssertionError catch (error) {
260 expect(
261 error.toString(),
262 contains('CupertinoPopupSurface requires a non-negative blur sigma.'),
263 );
264 }
265 });
266
267 // Regression test for https://github.com/flutter/flutter/issues/154887.
268 testWidgets(
269 "Applying a FadeTransition to the CupertinoPopupSurface doesn't cause transparency",
270 (WidgetTester tester) async {
271 final AnimationController controller = AnimationController(
272 duration: const Duration(milliseconds: 100),
273 vsync: const TestVSync(),
274 );
275 addTearDown(controller.dispose);
276 controller.forward();
277
278 await tester.pumpWidget(
279 _FilterTest(
280 FadeTransition(
281 opacity: controller,
282 child: const CupertinoPopupSurface(child: SizedBox()),
283 ),
284 ),
285 );
286
287 await tester.pump(const Duration(milliseconds: 50));
288
289 // Golden should display a CupertinoPopupSurface with no transparency
290 // directly underneath the surface. A small amount of transparency should be
291 // present on the upper-left corner of the screen.
292 //
293 // If transparency (gray and white grid) is present underneath the surface,
294 // the blendmode is being incorrectly applied.
295 await expectLater(
296 find.byType(CupertinoApp),
297 matchesGoldenFile('cupertinoPopupSurface.blendmode-fix.0.png'),
298 );
299
300 await tester.pumpAndSettle();
301 },
302 variant: TargetPlatformVariant.only(TargetPlatform.iOS),
303 );
304
305 // Golden displays a CupertinoPopupSurface with all enabled features.
306 //
307 // CupertinoPopupSurface uses ImageFilter.compose, which applies an inner
308 // filter first, followed by an outer filter (e.g. result =
309 // outer(inner(source))).
310 //
311 // For CupertinoPopupSurface, this means that the pixels underlying the
312 // surface are first saturated with a ColorFilter, and the resulting saturated
313 // pixels are blurred with an ImageFilter.blur. This test verifies that this
314 // order does not change.
315 testWidgets('Saturation is applied before blur', (WidgetTester tester) async {
316 await tester.pumpWidget(const _FilterTest(CupertinoPopupSurface(child: SizedBox())));
317
318 await expectLater(
319 find.byType(CupertinoApp),
320 matchesGoldenFile('cupertinoPopupSurface.composition.png'),
321 );
322
323 disableVibranceForTest();
324 await tester.pumpWidget(
325 const _FilterTest(
326 Stack(
327 fit: StackFit.expand,
328 children: <Widget>[
329 CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()),
330 CupertinoPopupSurface(child: SizedBox()),
331 ],
332 ),
333 ),
334 );
335
336 await expectLater(
337 find.byType(CupertinoApp),
338 matchesGoldenFile('cupertinoPopupSurface.composition.png'),
339 );
340 });
341}
342