// 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 'dart:math' as math; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; bool _listDoubleMatches(List? x, List? y) { if (x == null && y == null) { return true; } if (x == null || y == null) { return false; } if (x.length != y.length) { return false; } for (var i = 0; i < x.length; i++) { if ((x[i] - y[i]).abs() >= 0.0001) { return false; } } return true; } bool _listColorMatches(List x, List y) { if (x.length != y.length) { return false; } const double limit = 1 / 255; for (var i = 0; i < x.length; i++) { if ((x[i].a - y[i].a).abs() >= limit || (x[i].r - y[i].r).abs() >= limit || (x[i].g - y[i].g).abs() >= limit || (x[i].b - y[i].b).abs() >= limit) { return false; } } return true; } class _LinearGradientMatcher extends Matcher { _LinearGradientMatcher(this._target); final LinearGradient _target; @override Description describe(Description description) { description.add('expected $_target'); return description; } @override bool matches(dynamic item, Map matchState) { return item is LinearGradient && item.begin == _target.begin && item.end == _target.end && item.tileMode == _target.tileMode && item.transform == _target.transform && _listColorMatches(item.colors, _target.colors) && _listDoubleMatches(item.stops, _target.stops); } } Matcher _matchesLinearGradient(LinearGradient target) => _LinearGradientMatcher(target); class _RadialGradientMatcher extends Matcher { _RadialGradientMatcher(this._target); final RadialGradient _target; @override Description describe(Description description) { description.add('expected $_target'); return description; } @override bool matches(dynamic item, Map matchState) { if (item is RadialGradient) { return item.center == _target.center && item.radius == _target.radius && item.tileMode == _target.tileMode && item.transform == _target.transform && item.focal == _target.focal && item.focalRadius == _target.focalRadius && _listColorMatches(item.colors, _target.colors) && _listDoubleMatches(item.stops, _target.stops); } else { return false; } } } Matcher _matchesRadialGradient(RadialGradient target) => _RadialGradientMatcher(target); class _SweepGradientMatcher extends Matcher { _SweepGradientMatcher(this._target); final SweepGradient _target; @override Description describe(Description description) { description.add('expected $_target'); return description; } @override bool matches(dynamic item, Map matchState) { if (item is SweepGradient) { return item.center == _target.center && item.startAngle == _target.startAngle && item.endAngle == _target.endAngle && item.tileMode == _target.tileMode && item.transform == _target.transform && _listColorMatches(item.colors, _target.colors) && _listDoubleMatches(item.stops, _target.stops); } else { return false; } } } Matcher _matchesSweepGradient(SweepGradient target) => _SweepGradientMatcher(target); void main() { test('LinearGradient scale test', () { const testGradient = LinearGradient( begin: Alignment.bottomRight, end: Alignment(0.7, 1.0), colors: [Color(0x00FFFFFF), Color(0x11777777), Color(0x44444444)], ); final LinearGradient? actual = LinearGradient.lerp(null, testGradient, 0.25); expect( actual, _matchesLinearGradient( const LinearGradient( begin: Alignment.bottomRight, end: Alignment(0.7, 1.0), colors: [Color(0x00FFFFFF), Color(0x04777777), Color(0x11444444)], ), ), ); }); test('LinearGradient lerp test', () { const testGradient1 = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomLeft, colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = LinearGradient( begin: Alignment.topRight, end: Alignment.topLeft, colors: [Color(0x44444444), Color(0x88888888)], ); final LinearGradient? actual = LinearGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesLinearGradient( const LinearGradient( begin: Alignment.topCenter, end: Alignment.centerLeft, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0, 1], ), ), ); }); test('LinearGradient.lerp identical a,b', () { expect(LinearGradient.lerp(null, null, 0), null); const gradient = LinearGradient(colors: [Color(0x33333333), Color(0x66666666)]); expect(identical(LinearGradient.lerp(gradient, gradient, 0.5), gradient), true); }); test('LinearGradient lerp test with stops', () { const testGradient1 = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomLeft, colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = LinearGradient( begin: Alignment.topRight, end: Alignment.topLeft, colors: [Color(0x44444444), Color(0x88888888)], stops: [0.5, 1.0], ); final LinearGradient? actual = LinearGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesLinearGradient( const LinearGradient( begin: Alignment.topCenter, end: Alignment.centerLeft, colors: [Color(0x3B3B3B3B), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); }); test('LinearGradient lerp test with unequal number of colors', () { const testGradient1 = LinearGradient(colors: [Color(0x22222222), Color(0x66666666)]); const testGradient2 = LinearGradient( colors: [Color(0x44444444), Color(0x66666666), Color(0x88888888)], ); final LinearGradient? actual = LinearGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesLinearGradient( const LinearGradient( colors: [Color(0x33333333), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); }); test('LinearGradient lerp test with stops and unequal number of colors', () { const testGradient1 = LinearGradient( colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = LinearGradient( colors: [Color(0x44444444), Color(0x48484848), Color(0x88888888)], stops: [0.5, 0.7, 1.0], ); final LinearGradient? actual = LinearGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesLinearGradient( const LinearGradient( colors: [ Color(0x3B3B3B3B), Color(0x55555555), Color(0x57575757), Color(0x77777777), ], stops: [0.0, 0.5, 0.7, 1.0], ), ), ); }); test('LinearGradient lerp test with transforms', () { const testGradient1 = LinearGradient( transform: GradientRotation(math.pi / 4), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); const testGradient2 = LinearGradient( transform: GradientRotation(math.pi / 2), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); final LinearGradient? actual0 = LinearGradient.lerp(testGradient1, testGradient2, 0.0); final LinearGradient? actual1 = LinearGradient.lerp(testGradient1, testGradient2, 1.0); final LinearGradient? actual2 = LinearGradient.lerp(testGradient1, testGradient2, 0.5); expect(testGradient1, equals(actual0)); expect(testGradient2, equals(actual1)); expect(testGradient2, equals(actual2)); }); test('LinearGradient toString', () { expect( const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomLeft, transform: GradientRotation(1.6), colors: [Color(0x33333333), Color(0x66666666)], ).toString(), equals( 'LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomLeft, colors: [${const Color(0x33333333)}, ${const Color(0x66666666)}], tileMode: TileMode.clamp, transform: GradientRotation(radians: 1.6))', ), ); }); test('LinearGradient with different transforms', () { const testGradient1 = LinearGradient( transform: GradientRotation(math.pi / 4), colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient1Copy = LinearGradient( transform: GradientRotation(math.pi / 4), colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = LinearGradient( transform: GradientRotation(math.pi / 2), colors: [Color(0x33333333), Color(0x66666666)], ); expect(testGradient1, equals(testGradient1Copy)); expect(testGradient1, isNot(equals(testGradient2))); }); test('LinearGradient with AlignmentDirectional', () { expect(() { return const LinearGradient( begin: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)); }, throwsAssertionError); expect(() { return const LinearGradient( begin: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0), textDirection: TextDirection.rtl); }, returnsNormally); expect(() { return const LinearGradient( begin: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0), textDirection: TextDirection.ltr); }, returnsNormally); expect(() { return const LinearGradient( begin: Alignment.topLeft, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)); }, returnsNormally); }); test('LinearGradient withOpacity test', () { const testGradient = LinearGradient( begin: Alignment.bottomRight, end: Alignment.topCenter, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], ); final LinearGradient actual = testGradient.withOpacity(0.5); expect( actual, const LinearGradient( begin: Alignment.bottomRight, end: Alignment.topCenter, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], ), ); }); test('LinearGradient withOpacity() preserves transform', () { const testGradient = LinearGradient( begin: Alignment.bottomRight, end: Alignment.topCenter, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], transform: GradientRotation(1), ); final LinearGradient actual = testGradient.withOpacity(0.5); expect( actual, const LinearGradient( begin: Alignment.bottomRight, end: Alignment.topCenter, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], transform: GradientRotation(1), ), ); }); test('LinearGradient.scale preserves transform', () { const testGradient = LinearGradient( begin: Alignment.bottomRight, end: Alignment.topLeft, stops: [0, 0.5, 1], colors: [Color(0xFFFF0000), Color(0xFF00FF00), Color(0xFF0000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ); final LinearGradient actual = testGradient.scale(0.5); expect( actual, _matchesLinearGradient( const LinearGradient( begin: Alignment.bottomRight, end: Alignment.topLeft, stops: [0, 0.5, 1], colors: [Color(0x80FF0000), Color(0x8000FF00), Color(0x800000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ), ), ); }); test('RadialGradient with AlignmentDirectional', () { expect(() { return const RadialGradient( center: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)); }, throwsAssertionError); expect(() { return const RadialGradient( center: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0), textDirection: TextDirection.rtl); }, returnsNormally); expect(() { return const RadialGradient( center: AlignmentDirectional.topStart, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0), textDirection: TextDirection.ltr); }, returnsNormally); expect(() { return const RadialGradient( center: Alignment.topLeft, colors: [Color(0xFFFFFFFF), Color(0xFFFFFFFF)], ).createShader(const Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)); }, returnsNormally); }); test('RadialGradient lerp test', () { const testGradient1 = RadialGradient( center: Alignment.topLeft, radius: 20.0, colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = RadialGradient( center: Alignment.topRight, radius: 10.0, colors: [Color(0x44444444), Color(0x88888888)], ); final RadialGradient? actual = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesRadialGradient( const RadialGradient( center: Alignment.topCenter, radius: 15.0, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0.0, 1.0], ), ), ); }); test('RadialGradient.lerp identical a,b', () { expect(RadialGradient.lerp(null, null, 0), null); const gradient = RadialGradient(colors: [Color(0x33333333), Color(0x66666666)]); expect(identical(RadialGradient.lerp(gradient, gradient, 0.5), gradient), true); }); test('RadialGradient lerp test with stops', () { const testGradient1 = RadialGradient( center: Alignment.topLeft, radius: 20.0, colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = RadialGradient( center: Alignment.topRight, radius: 10.0, colors: [Color(0x44444444), Color(0x88888888)], stops: [0.5, 1.0], ); final RadialGradient? actual = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesRadialGradient( const RadialGradient( center: Alignment.topCenter, radius: 15.0, colors: [Color(0x3B3B3B3B), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); expect(actual!.focal, isNull); }); test('RadialGradient lerp test with unequal number of colors', () { const testGradient1 = RadialGradient(colors: [Color(0x22222222), Color(0x66666666)]); const testGradient2 = RadialGradient( colors: [Color(0x44444444), Color(0x66666666), Color(0x88888888)], ); final RadialGradient? actual = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesRadialGradient( const RadialGradient( colors: [Color(0x33333333), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); }); test('RadialGradient lerp test with stops and unequal number of colors', () { const testGradient1 = RadialGradient( colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = RadialGradient( colors: [Color(0x44444444), Color(0x48484848), Color(0x88888888)], stops: [0.5, 0.7, 1.0], ); final RadialGradient? actual = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesRadialGradient( const RadialGradient( colors: [ Color(0x3B3B3B3B), Color(0x55555555), Color(0x57575757), Color(0x77777777), ], stops: [0.0, 0.5, 0.7, 1.0], ), ), ); }); test('RadialGradient lerp test with transforms', () { const testGradient1 = RadialGradient( transform: GradientRotation(math.pi / 4), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); const testGradient2 = RadialGradient( transform: GradientRotation(math.pi / 2), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); final RadialGradient? actual0 = RadialGradient.lerp(testGradient1, testGradient2, 0.0); final RadialGradient? actual1 = RadialGradient.lerp(testGradient1, testGradient2, 1.0); final RadialGradient? actual2 = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect(testGradient1, equals(actual0)); expect(testGradient2, equals(actual1)); expect(testGradient2, equals(actual2)); }); test('RadialGradient lerp test with focal', () { const testGradient1 = RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = RadialGradient( center: Alignment.topRight, focal: Alignment.centerRight, radius: 10.0, focalRadius: 5.0, colors: [Color(0x44444444), Color(0x88888888)], ); const testGradient3 = RadialGradient( center: Alignment.topRight, radius: 10.0, colors: [Color(0x44444444), Color(0x88888888)], ); final RadialGradient? actual = RadialGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesRadialGradient( const RadialGradient( center: Alignment.topCenter, focal: Alignment.center, radius: 15.0, focalRadius: 7.5, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0.0, 1.0], ), ), ); final RadialGradient? actual2 = RadialGradient.lerp(testGradient1, testGradient3, 0.5); expect( actual2, _matchesRadialGradient( const RadialGradient( center: Alignment.topCenter, focal: Alignment(-0.5, 0.0), radius: 15.0, focalRadius: 5.0, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0.0, 1.0], ), ), ); }); test('RadialGradient withOpacity test', () { const testGradient = RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], ); final RadialGradient actual = testGradient.withOpacity(0.5); expect( actual, const RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], ), ); }); test('RadialGradient withOpacity() preserves transform', () { const testGradient = RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], transform: GradientRotation(1), ); final RadialGradient actual = testGradient.withOpacity(0.5); expect( actual, const RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], transform: GradientRotation(1), ), ); }); test('RadialGradient.scale preserves transform', () { const testGradient = RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, stops: [0, 0.5, 1], colors: [Color(0xFFFF0000), Color(0xFF00FF00), Color(0xFF0000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ); final RadialGradient actual = testGradient.scale(0.5); expect( actual, _matchesRadialGradient( const RadialGradient( center: Alignment.topLeft, focal: Alignment.centerLeft, radius: 20.0, focalRadius: 10.0, stops: [0, 0.5, 1], colors: [Color(0x80FF0000), Color(0x8000FF00), Color(0x800000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ), ), ); }); test('SweepGradient lerp test', () { const testGradient1 = SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = SweepGradient( center: Alignment.topRight, startAngle: math.pi / 2, endAngle: math.pi, colors: [Color(0x44444444), Color(0x88888888)], ); final SweepGradient? actual = SweepGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesSweepGradient( const SweepGradient( center: Alignment.topCenter, startAngle: math.pi / 4, endAngle: math.pi * 3 / 4, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0.0, 1.0], ), ), ); }); test('SweepGradient.lerp identical a,b', () { expect(SweepGradient.lerp(null, null, 0), null); const gradient = SweepGradient(colors: [Color(0x33333333), Color(0x66666666)]); expect(identical(SweepGradient.lerp(gradient, gradient, 0.5), gradient), true); }); test('SweepGradient lerp test with stops', () { const testGradient1 = SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = SweepGradient( center: Alignment.topRight, startAngle: math.pi / 2, endAngle: math.pi, colors: [Color(0x44444444), Color(0x88888888)], stops: [0.5, 1.0], ); final SweepGradient? actual = SweepGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesSweepGradient( const SweepGradient( center: Alignment.topCenter, startAngle: math.pi / 4, endAngle: math.pi * 3 / 4, colors: [Color(0x3B3B3B3B), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); }); test('SweepGradient lerp test with unequal number of colors', () { const testGradient1 = SweepGradient(colors: [Color(0x22222222), Color(0x66666666)]); const testGradient2 = SweepGradient( colors: [Color(0x44444444), Color(0x66666666), Color(0x88888888)], ); final SweepGradient? actual = SweepGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesSweepGradient( const SweepGradient( colors: [Color(0x33333333), Color(0x55555555), Color(0x77777777)], stops: [0.0, 0.5, 1.0], ), ), ); }); test('SweepGradient lerp test with stops and unequal number of colors', () { const testGradient1 = SweepGradient( colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 0.5], ); const testGradient2 = SweepGradient( colors: [Color(0x44444444), Color(0x48484848), Color(0x88888888)], stops: [0.5, 0.7, 1.0], ); final SweepGradient? actual = SweepGradient.lerp(testGradient1, testGradient2, 0.5); expect( actual, _matchesSweepGradient( const SweepGradient( colors: [ Color(0x3B3B3B3B), Color(0x55555555), Color(0x57575757), Color(0x77777777), ], stops: [0.0, 0.5, 0.7, 1.0], ), ), ); }); test('SweepGradient lerp test with transforms', () { const testGradient1 = SweepGradient( transform: GradientRotation(math.pi / 4), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); const testGradient2 = SweepGradient( transform: GradientRotation(math.pi / 2), colors: [Color(0x33333333), Color(0x66666666)], stops: [0, 1], ); final SweepGradient? actual0 = SweepGradient.lerp(testGradient1, testGradient2, 0.0); final SweepGradient? actual1 = SweepGradient.lerp(testGradient1, testGradient2, 1.0); final SweepGradient? actual2 = SweepGradient.lerp(testGradient1, testGradient2, 0.5); expect(testGradient1, equals(actual0)); expect(testGradient2, equals(actual1)); expect(testGradient2, equals(actual2)); }); test('SweepGradient scale test)', () { const testGradient = SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0xff333333), Color(0xff666666)], ); final SweepGradient actual = testGradient.scale(0.5); expect( actual, _matchesSweepGradient( const SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0x80333333), Color(0x80666666)], ), ), ); }); test('SweepGradient withOpacity test', () { const testGradient = SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], ); final SweepGradient actual = testGradient.withOpacity(0.5); expect( actual, const SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], ), ); }); test('SweepGradient withOpacity() preserves transform', () { const testGradient = SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0xFFFFFFFF), Color(0xAF777777), Color(0x44444444)], transform: GradientRotation(1), ); final SweepGradient actual = testGradient.withOpacity(0.5); expect( actual, const SweepGradient( center: Alignment.topLeft, endAngle: math.pi / 2, colors: [Color(0x80FFFFFF), Color(0x80777777), Color(0x80444444)], transform: GradientRotation(1), ), ); }); test('SweepGradient.scale preserves transform', () { const testGradient = SweepGradient( center: Alignment.topLeft, startAngle: math.pi / 4, endAngle: math.pi * 3 / 4, stops: [0, 0.5, 1], colors: [Color(0xFFFF0000), Color(0xFF00FF00), Color(0xFF0000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ); final SweepGradient actual = testGradient.scale(0.5); expect( actual, _matchesSweepGradient( const SweepGradient( center: Alignment.topLeft, startAngle: math.pi / 4, endAngle: math.pi * 3 / 4, stops: [0, 0.5, 1], colors: [Color(0x80FF0000), Color(0x8000FF00), Color(0x800000FF)], tileMode: TileMode.decal, transform: GradientRotation(math.pi / 4), ), ), ); }); test('Gradient lerp test (with RadialGradient)', () { const testGradient1 = RadialGradient( center: Alignment.topLeft, radius: 20.0, colors: [Color(0x33333333), Color(0x66666666)], stops: [0.0, 1.0], ); const testGradient2 = RadialGradient( center: Alignment.topCenter, radius: 15.0, colors: [Color(0x3B3B3B3B), Color(0x77777777)], stops: [0.0, 1.0], ); const testGradient3 = RadialGradient( center: Alignment.topRight, radius: 10.0, colors: [Color(0x44444444), Color(0x88888888)], stops: [0.0, 1.0], ); expect(Gradient.lerp(testGradient1, testGradient3, 0.0), _matchesRadialGradient(testGradient1)); expect(Gradient.lerp(testGradient1, testGradient3, 0.5), _matchesRadialGradient(testGradient2)); expect(Gradient.lerp(testGradient1, testGradient3, 1.0), _matchesRadialGradient(testGradient3)); expect(Gradient.lerp(testGradient3, testGradient1, 0.0), _matchesRadialGradient(testGradient3)); expect(Gradient.lerp(testGradient3, testGradient1, 0.5), _matchesRadialGradient(testGradient2)); expect(Gradient.lerp(testGradient3, testGradient1, 1.0), _matchesRadialGradient(testGradient1)); }); test('Gradient lerp test (LinearGradient to RadialGradient)', () { const testGradient1 = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0x33333333), Color(0x66666666)], ); const testGradient2 = RadialGradient( radius: 20.0, colors: [Color(0x44444444), Color(0x88888888)], ); expect(Gradient.lerp(testGradient1, testGradient2, 0.0), testGradient1); expect(Gradient.lerp(testGradient1, testGradient2, 1.0), testGradient2); expect(Gradient.lerp(testGradient1, testGradient2, 0.5), testGradient2.scale(0.0)); }); test('Gradients can handle missing stops and report mismatched stops', () { const test1a = LinearGradient( colors: [Color(0x11111111), Color(0x22222222), Color(0x33333333)], ); const test1b = RadialGradient( colors: [Color(0x11111111), Color(0x22222222), Color(0x33333333)], ); const test2a = LinearGradient( colors: [Color(0x11111111), Color(0x22222222), Color(0x33333333)], stops: [0.0, 1.0], ); const test2b = RadialGradient( colors: [Color(0x11111111), Color(0x22222222), Color(0x33333333)], stops: [0.0, 1.0], ); const rect = Rect.fromLTWH(1.0, 2.0, 3.0, 4.0); expect(test1a.createShader(rect), isNotNull); expect(test1b.createShader(rect), isNotNull); expect(() { test2a.createShader(rect); }, throwsArgumentError); expect(() { test2b.createShader(rect); }, throwsArgumentError); }); group('Transforms', () { const colors = [Color(0xFFFFFFFF), Color(0xFF000088)]; const rect = Rect.fromLTWH(0.0, 0.0, 300.0, 400.0); const gradients45 = [ LinearGradient(colors: colors, transform: GradientRotation(math.pi / 4)), // A radial gradient won't be interesting to rotate unless the center is changed. RadialGradient( colors: colors, center: Alignment.topCenter, transform: GradientRotation(math.pi / 4), ), SweepGradient(colors: colors, transform: GradientRotation(math.pi / 4)), ]; const gradients90 = [ LinearGradient(colors: colors, transform: GradientRotation(math.pi / 2)), // A radial gradient won't be interesting to rotate unless the center is changed. RadialGradient( colors: colors, center: Alignment.topCenter, transform: GradientRotation(math.pi / 2), ), SweepGradient(colors: colors, transform: GradientRotation(math.pi / 2)), ]; const gradientSnakeCase = { LinearGradient: 'linear_gradient', RadialGradient: 'radial_gradient', SweepGradient: 'sweep_gradient', }; Future runTest(WidgetTester tester, Gradient gradient, double degrees) async { final goldenName = '${gradientSnakeCase[gradient.runtimeType]}_$degrees.png'; final Shader shader = gradient.createShader(rect); final Key painterKey = UniqueKey(); await tester.pumpWidget( Center( child: SizedBox.fromSize( size: rect.size, child: RepaintBoundary( key: painterKey, child: CustomPaint(painter: GradientPainter(shader, rect)), ), ), ), ); await expectLater(find.byKey(painterKey), matchesGoldenFile(goldenName)); } group('Gradients - 45 degrees', () { for (final gradient in gradients45) { testWidgets('$gradient', (WidgetTester tester) async { await runTest(tester, gradient, 45); }); } }); group('Gradients - 90 degrees', () { for (final gradient in gradients90) { testWidgets('$gradient', (WidgetTester tester) async { await runTest(tester, gradient, 90); }); } }); }); } class GradientPainter extends CustomPainter { const GradientPainter(this.shader, this.rect); final Shader shader; final Rect rect; @override void paint(Canvas canvas, Size size) { canvas.drawRect(rect, Paint()..shader = shader); } @override bool shouldRepaint(CustomPainter oldDelegate) => true; }