Unverified Commit 00f3f2b1 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

More beautiful linear_gradient sample (#99298)

parent 96426230
...@@ -11,33 +11,41 @@ void main() => runApp(const MyApp()); ...@@ -11,33 +11,41 @@ void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const MaterialApp( return const MaterialApp(home: MoodyGradient());
title: _title,
home: MyStatelessWidget(),
);
} }
} }
class MyStatelessWidget extends StatelessWidget { class MoodyGradient extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key); const MoodyGradient({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Material(
child: Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: end: Alignment(0.8, 1),
Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds.
colors: <Color>[ colors: <Color>[
Color(0xffee0000), Color(0xff1f005c),
Color(0xffeeee00) Color(0xff5b0060),
], // red to yellow Color(0xff870160),
tileMode: TileMode.repeated, // repeats the gradient over the canvas Color(0xffac255e),
Color(0xffca485c),
Color(0xffe16b5c),
Color(0xfff39060),
Color(0xffffb56b),
], // Gradient from https://learnui.design/tools/gradient-generator.html
tileMode: TileMode.mirror,
),
),
child: const Center(
child: Text(
'From Night to Day',
style: TextStyle(fontSize: 24, color: Colors.white),
),
), ),
), ),
); );
......
...@@ -25,6 +25,8 @@ dev_dependencies: ...@@ -25,6 +25,8 @@ dev_dependencies:
sdk: flutter sdk: flutter
flutter_driver: flutter_driver:
sdk: flutter sdk: flutter
flutter_goldens:
sdk: flutter
flutter_test: flutter_test:
sdk: flutter sdk: flutter
test: 1.20.1 test: 1.20.1
......
// 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.
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'goldens_io.dart' if (dart.library.html) 'goldens_web.dart' as flutter_goldens;
Future<void> testExecutable(FutureOr<void> Function() testMain) {
// Enable golden file testing using Skia Gold.
return flutter_goldens.testExecutable(testMain, namePrefix: 'api');
}
// 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.
export 'package:flutter_goldens/flutter_goldens.dart' show testExecutable;
// 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.
import 'dart:async';
// package:flutter_goldens is not used as part of the test process for web.
Future<void> testExecutable(FutureOr<void> Function() testMain) async => testMain();
// 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.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_api_samples/painting/gradient/linear_gradient.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('finds a gradient', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: example.MoodyGradient(),
),
);
expect(find.byType(example.MoodyGradient), findsOneWidget);
});
testWidgets('gradient matches golden', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: SizedBox(
width: 800,
height: 600,
child: RepaintBoundary(
child: example.MoodyGradient(),
),
),
),
);
await expectLater(
find.byType(example.MoodyGradient),
matchesGoldenFile('linear_gradient.0_test.png'),
);
});
}
...@@ -349,8 +349,9 @@ abstract class Gradient { ...@@ -349,8 +349,9 @@ abstract class Gradient {
/// To use a [LinearGradient] to paint on a canvas directly, see [createShader]. /// To use a [LinearGradient] to paint on a canvas directly, see [createShader].
/// ///
/// {@tool dartpad} /// {@tool dartpad}
/// This sample draws a picture that looks like vertical window shades by having /// This sample draws a picture with a gradient sweeping through different
/// a [Container] display a [BoxDecoration] with a [LinearGradient]. /// colors, by having a [Container] display a [BoxDecoration] with a
/// [LinearGradient].
/// ///
/// ** See code in examples/api/lib/painting/gradient/linear_gradient.0.dart ** /// ** See code in examples/api/lib/painting/gradient/linear_gradient.0.dart **
/// {@end-tool} /// {@end-tool}
......
...@@ -25,15 +25,18 @@ const String _kFlutterRootKey = 'FLUTTER_ROOT'; ...@@ -25,15 +25,18 @@ const String _kFlutterRootKey = 'FLUTTER_ROOT';
/// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that /// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that
/// works for the current test. _Which_ FlutterGoldenFileComparator is /// works for the current test. _Which_ FlutterGoldenFileComparator is
/// instantiated is based on the current testing environment. /// instantiated is based on the current testing environment.
Future<void> testExecutable(FutureOr<void> Function() testMain) async { ///
/// When set, the `namePrefix` is prepended to the names of all gold images.
Future<void> testExecutable(FutureOr<void> Function() testMain, {String? namePrefix}) async {
const Platform platform = LocalPlatform(); const Platform platform = LocalPlatform();
if (FlutterPostSubmitFileComparator.isAvailableForEnvironment(platform)) { if (FlutterPostSubmitFileComparator.isAvailableForEnvironment(platform)) {
goldenFileComparator = await FlutterPostSubmitFileComparator.fromDefaultComparator(platform); goldenFileComparator = await FlutterPostSubmitFileComparator.fromDefaultComparator(platform, namePrefix: namePrefix);
} else if (FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform)) { } else if (FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform)) {
goldenFileComparator = await FlutterPreSubmitFileComparator.fromDefaultComparator(platform); goldenFileComparator = await FlutterPreSubmitFileComparator.fromDefaultComparator(platform, namePrefix: namePrefix);
} else if (FlutterSkippingFileComparator.isAvailableForEnvironment(platform)) { } else if (FlutterSkippingFileComparator.isAvailableForEnvironment(platform)) {
goldenFileComparator = FlutterSkippingFileComparator.fromDefaultComparator( goldenFileComparator = FlutterSkippingFileComparator.fromDefaultComparator(
'Golden file testing is not executed on Cirrus, or LUCI environments outside of flutter/flutter.' 'Golden file testing is not executed on Cirrus, or LUCI environments outside of flutter/flutter.',
namePrefix: namePrefix
); );
} else { } else {
goldenFileComparator = await FlutterLocalFileComparator.fromDefaultComparator(platform); goldenFileComparator = await FlutterLocalFileComparator.fromDefaultComparator(platform);
...@@ -91,6 +94,7 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { ...@@ -91,6 +94,7 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
this.skiaClient, { this.skiaClient, {
this.fs = const LocalFileSystem(), this.fs = const LocalFileSystem(),
this.platform = const LocalPlatform(), this.platform = const LocalPlatform(),
this.namePrefix,
}); });
/// The directory to which golden file URIs will be resolved in [compare] and /// The directory to which golden file URIs will be resolved in [compare] and
...@@ -109,6 +113,9 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { ...@@ -109,6 +113,9 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
@visibleForTesting @visibleForTesting
final Platform platform; final Platform platform;
/// The prefix that is added to all golden names.
final String? namePrefix;
@override @override
Future<void> update(Uri golden, Uint8List imageBytes) async { Future<void> update(Uri golden, Uint8List imageBytes) async {
final File goldenFile = getGoldenFile(golden); final File goldenFile = getGoldenFile(golden);
...@@ -173,8 +180,12 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { ...@@ -173,8 +180,12 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
'Golden files in the Flutter framework must end with the file extension ' 'Golden files in the Flutter framework must end with the file extension '
'.png.' '.png.'
); );
final String prefix = basedir.pathSegments[basedir.pathSegments.length - 2]; return Uri.parse(<String>[
return Uri.parse('$prefix.$golden'); if (namePrefix != null)
namePrefix!,
basedir.pathSegments[basedir.pathSegments.length - 2],
golden.toString(),
].join('.'));
} }
} }
...@@ -205,11 +216,13 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -205,11 +216,13 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator {
final SkiaGoldClient skiaClient, { final SkiaGoldClient skiaClient, {
final FileSystem fs = const LocalFileSystem(), final FileSystem fs = const LocalFileSystem(),
final Platform platform = const LocalPlatform(), final Platform platform = const LocalPlatform(),
String? namePrefix,
}) : super( }) : super(
basedir, basedir,
skiaClient, skiaClient,
fs: fs, fs: fs,
platform: platform, platform: platform,
namePrefix: namePrefix,
); );
/// Creates a new [FlutterPostSubmitFileComparator] that mirrors the relative /// Creates a new [FlutterPostSubmitFileComparator] that mirrors the relative
...@@ -221,6 +234,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -221,6 +234,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator {
final Platform platform, { final Platform platform, {
SkiaGoldClient? goldens, SkiaGoldClient? goldens,
LocalFileComparator? defaultComparator, LocalFileComparator? defaultComparator,
String? namePrefix,
}) async { }) async {
defaultComparator ??= goldenFileComparator as LocalFileComparator; defaultComparator ??= goldenFileComparator as LocalFileComparator;
...@@ -233,7 +247,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -233,7 +247,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator {
goldens ??= SkiaGoldClient(baseDirectory); goldens ??= SkiaGoldClient(baseDirectory);
await goldens.auth(); await goldens.auth();
return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens); return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens, namePrefix: namePrefix);
} }
@override @override
...@@ -283,11 +297,13 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -283,11 +297,13 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
final SkiaGoldClient skiaClient, { final SkiaGoldClient skiaClient, {
final FileSystem fs = const LocalFileSystem(), final FileSystem fs = const LocalFileSystem(),
final Platform platform = const LocalPlatform(), final Platform platform = const LocalPlatform(),
final String? namePrefix,
}) : super( }) : super(
basedir, basedir,
skiaClient, skiaClient,
fs: fs, fs: fs,
platform: platform, platform: platform,
namePrefix: namePrefix,
); );
/// Creates a new [FlutterPreSubmitFileComparator] that mirrors the /// Creates a new [FlutterPreSubmitFileComparator] that mirrors the
...@@ -300,6 +316,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -300,6 +316,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
SkiaGoldClient? goldens, SkiaGoldClient? goldens,
LocalFileComparator? defaultComparator, LocalFileComparator? defaultComparator,
Directory? testBasedir, Directory? testBasedir,
String? namePrefix,
}) async { }) async {
defaultComparator ??= goldenFileComparator as LocalFileComparator; defaultComparator ??= goldenFileComparator as LocalFileComparator;
...@@ -318,6 +335,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -318,6 +335,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
return FlutterPreSubmitFileComparator( return FlutterPreSubmitFileComparator(
baseDirectory.uri, baseDirectory.uri,
goldens, platform: platform, goldens, platform: platform,
namePrefix: namePrefix,
); );
} }
...@@ -367,8 +385,9 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { ...@@ -367,8 +385,9 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator {
FlutterSkippingFileComparator( FlutterSkippingFileComparator(
final Uri basedir, final Uri basedir,
final SkiaGoldClient skiaClient, final SkiaGoldClient skiaClient,
this.reason, this.reason, {
) : super(basedir, skiaClient); String? namePrefix,
}) : super(basedir, skiaClient, namePrefix: namePrefix);
/// Describes the reason for using the [FlutterSkippingFileComparator]. /// Describes the reason for using the [FlutterSkippingFileComparator].
/// ///
...@@ -380,12 +399,13 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { ...@@ -380,12 +399,13 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator {
static FlutterSkippingFileComparator fromDefaultComparator( static FlutterSkippingFileComparator fromDefaultComparator(
String reason, { String reason, {
LocalFileComparator? defaultComparator, LocalFileComparator? defaultComparator,
String? namePrefix,
}) { }) {
defaultComparator ??= goldenFileComparator as LocalFileComparator; defaultComparator ??= goldenFileComparator as LocalFileComparator;
const FileSystem fs = LocalFileSystem(); const FileSystem fs = LocalFileSystem();
final Uri basedir = defaultComparator.basedir; final Uri basedir = defaultComparator.basedir;
final SkiaGoldClient skiaClient = SkiaGoldClient(fs.directory(basedir)); final SkiaGoldClient skiaClient = SkiaGoldClient(fs.directory(basedir));
return FlutterSkippingFileComparator(basedir, skiaClient, reason); return FlutterSkippingFileComparator(basedir, skiaClient, reason, namePrefix: namePrefix);
} }
@override @override
......
...@@ -466,6 +466,27 @@ void main() { ...@@ -466,6 +466,27 @@ void main() {
expect(key, Uri.parse('foo.png')); expect(key, Uri.parse('foo.png'));
}); });
test('adds namePrefix', () async {
const String libraryName = 'sidedishes';
const String namePrefix = 'tomatosalad';
const String fileName = 'lettuce.png';
final FakeSkiaGoldClient fakeSkiaClient = FakeSkiaGoldClient();
final Directory basedir = fs.directory('flutter/test/$libraryName/')
..createSync(recursive: true);
final FlutterGoldenFileComparator comparator = FlutterPostSubmitFileComparator(
basedir.uri,
fakeSkiaClient,
fs: fs,
platform: platform,
namePrefix: namePrefix,
);
await comparator.compare(
Uint8List.fromList(_kTestPngBytes),
Uri.parse(fileName),
);
expect(fakeSkiaClient.testNames.single, '$namePrefix.$libraryName.$fileName');
});
group('Post-Submit', () { group('Post-Submit', () {
late FakeSkiaGoldClient fakeSkiaClient; late FakeSkiaGoldClient fakeSkiaClient;
...@@ -926,11 +947,16 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient { ...@@ -926,11 +947,16 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient {
@override @override
Future<void> auth() async {} Future<void> auth() async {}
final List<String> testNames = <String>[];
int initCalls = 0; int initCalls = 0;
@override @override
Future<void> imgtestInit() async => initCalls += 1; Future<void> imgtestInit() async => initCalls += 1;
@override @override
Future<bool> imgtestAdd(String testName, File goldenFile) async => true; Future<bool> imgtestAdd(String testName, File goldenFile) async {
testNames.add(testName);
return true;
}
int tryInitCalls = 0; int tryInitCalls = 0;
@override @override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment