// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file is run as part of a reduced test set in CI on Mac and Windows // machines. @Tags(['reduced-test-set']) library; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; class _FilterTest extends StatelessWidget { const _FilterTest(Widget child, {this.brightness = Brightness.light}) : _child = child; final Brightness brightness; final Widget _child; @override Widget build(BuildContext context) { final Size size = MediaQuery.sizeOf(context); final double tileHeight = size.height / 4; final double tileWidth = size.width / 8; return CupertinoApp( home: Stack( fit: StackFit.expand, children: [ // 512 color tiles // 4 alpha levels (0.416, 0.25, 0.5, 0.75) for (int a = 0; a < 4; a++) for (int h = 0; h < 8; h++) // 8 hues for (int s = 0; s < 4; s++) // 4 saturation levels for (int b = 0; b < 4; b++) // 4 brightness levels Positioned( left: h * tileWidth + b * tileWidth / 4, top: a * tileHeight + s * tileHeight / 4, height: tileHeight, width: tileWidth, child: ColoredBox( color: HSVColor.fromAHSV( 0.5 + a / 8, h * 45, 0.5 + s / 8, 0.5 + b / 8, ).toColor(), ), ), Padding( padding: const EdgeInsets.all(32), child: CupertinoTheme( data: CupertinoThemeData(brightness: brightness), child: _child, ), ), ], ), ); } } void main() { void disableVibranceForTest() { CupertinoPopupSurface.debugIsVibrancePainted = false; addTearDown(() { CupertinoPopupSurface.debugIsVibrancePainted = true; }); } // Golden displays the color filter effect of the CupertinoPopupSurface // when the ambient brightness is light. testWidgets( 'Brightness.light color filter', (WidgetTester tester) async { await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()), ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.color-filter.light.png'), ); }, skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026 ); // Golden displays the color filter effect of the CupertinoPopupSurface // when the ambient brightness is dark. testWidgets( 'Brightness.dark color filter', (WidgetTester tester) async { await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()), brightness: Brightness.dark, ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.color-filter.dark.png'), ); }, skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026 ); // Golden displays color tiles without CupertinoPopupSurface being // displayed. testWidgets('Setting debugIsVibrancePainted to false removes the color filter', ( WidgetTester tester, ) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()), ), ); // The BackdropFilter widget should not be mounted when blurSigma is 0 and // CupertinoPopupSurface.debugIsVibrancePainted is false. expect( find.descendant( of: find.byType(CupertinoPopupSurface), matching: find.byType(BackdropFilter), ), findsNothing, ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.color-filter.removed.png'), ); }); // Golden displays the surface color of the CupertinoPopupSurface // in light mode. testWidgets('Brightness.light surface color', (WidgetTester tester) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest(CupertinoPopupSurface(blurSigma: 0, child: SizedBox())), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.surface-color.light.png'), ); }); // Golden displays the surface color of the CupertinoPopupSurface // in dark mode. testWidgets('Brightness.dark surface color', (WidgetTester tester) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(blurSigma: 0, child: SizedBox()), brightness: Brightness.dark, ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.surface-color.dark.png'), ); }); // Golden displays a CupertinoPopupSurface with the color removed. The result // should only display color tiles. testWidgets('Setting isSurfacePainted to false removes the surface color', ( WidgetTester tester, ) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(blurSigma: 0, isSurfacePainted: false, child: SizedBox()), brightness: Brightness.dark, ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.surface-color.removed.png'), ); }); // Goldens display a CupertinoPopupSurface with no vibrance or surface // color, with blur sigmas of 5 and 30 (default). testWidgets( 'Positive blurSigma applies blur', (WidgetTester tester) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 5, child: SizedBox()), ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.blur.5.png'), ); await tester.pumpWidget( const _FilterTest(CupertinoPopupSurface(isSurfacePainted: false, child: SizedBox())), ); await expectLater( find.byType(CupertinoApp), // 30 is the default blur sigma matchesGoldenFile('cupertinoPopupSurface.blur.30.png'), ); }, skip: kIsWasm, // https://github.com/flutter/flutter/issues/152026 ); // Golden displays a CupertinoPopupSurface with a blur sigma of 0. Because // the blur sigma is 0 and vibrance and surface are not painted, no popup // surface is displayed. testWidgets('Setting blurSigma to zero removes blur', (WidgetTester tester) async { disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()), ), ); // The BackdropFilter widget should not be mounted when blurSigma is 0 and // CupertinoPopupSurface.isVibrancePainted is false. expect( find.descendant( of: find.byType(CupertinoPopupSurface), matching: find.byType(BackdropFilter), ), findsNothing, ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.blur.0.png'), ); await tester.pumpWidget( const _FilterTest( CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()), ), ); }); testWidgets('Setting a blurSigma to a negative number throws', (WidgetTester tester) async { try { disableVibranceForTest(); await tester.pumpWidget( _FilterTest( CupertinoPopupSurface(isSurfacePainted: false, blurSigma: -1, child: const SizedBox()), ), ); fail('CupertinoPopupSurface did not throw when provided a negative blur sigma.'); } on AssertionError catch (error) { expect( error.toString(), contains('CupertinoPopupSurface requires a non-negative blur sigma.'), ); } }); // Regression test for https://github.com/flutter/flutter/issues/154887. testWidgets( "Applying a FadeTransition to the CupertinoPopupSurface doesn't cause transparency", (WidgetTester tester) async { final controller = AnimationController( duration: const Duration(milliseconds: 100), vsync: const TestVSync(), ); addTearDown(controller.dispose); controller.forward(); await tester.pumpWidget( _FilterTest( FadeTransition( opacity: controller, child: const CupertinoPopupSurface(child: SizedBox()), ), ), ); await tester.pump(const Duration(milliseconds: 50)); // Golden should display a CupertinoPopupSurface with no transparency // directly underneath the surface. A small amount of transparency should be // present on the upper-left corner of the screen. // // If transparency (gray and white grid) is present underneath the surface, // the blendmode is being incorrectly applied. await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.blendmode-fix.0.png'), ); await tester.pumpAndSettle(); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS), ); // Golden displays a CupertinoPopupSurface with all enabled features. // // CupertinoPopupSurface uses ImageFilter.compose, which applies an inner // filter first, followed by an outer filter (e.g. result = // outer(inner(source))). // // For CupertinoPopupSurface, this means that the pixels underlying the // surface are first saturated with a ColorFilter, and the resulting saturated // pixels are blurred with an ImageFilter.blur. This test verifies that this // order does not change. testWidgets('Saturation is applied before blur', (WidgetTester tester) async { await tester.pumpWidget(const _FilterTest(CupertinoPopupSurface(child: SizedBox()))); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.composition.png'), ); disableVibranceForTest(); await tester.pumpWidget( const _FilterTest( Stack( fit: StackFit.expand, children: [ CupertinoPopupSurface(isSurfacePainted: false, blurSigma: 0, child: SizedBox()), CupertinoPopupSurface(child: SizedBox()), ], ), ), ); await expectLater( find.byType(CupertinoApp), matchesGoldenFile('cupertinoPopupSurface.composition.png'), ); }); }