import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lottie/lottie.dart'; import 'utils.dart'; void main() { void testGolden( String description, ValueDelegate delegate, { double? progress, String? filePath, }) { filePath ??= 'Tests/Shapes.json'; var screenshotName = description .toLowerCase() .replaceAll(RegExp('[^a-z0-9 ]'), '') .replaceAll(' ', '_'); testWidgets(description, (tester) async { var composition = await LottieComposition.fromBytes( File('example/assets/$filePath').readAsBytesSync(), ); var animation = AnimationController( vsync: tester, duration: composition.duration, ); if (progress != null) { animation.value = progress; } await tester.pumpWidget( Lottie( composition: composition, controller: animation, delegates: LottieDelegates(values: [delegate]), addRepaintBoundary: false, ), ); await tester.pump(); await expectLater( find.byType(Lottie), matchesGoldenFile('goldens/dynamic/$screenshotName.png'), ); if (progress == null || progress == 0) { await tester.pumpWidget( Lottie( composition: composition, controller: animation, delegates: const LottieDelegates(values: []), addRepaintBoundary: false, ), ); await tester.pump(); } }); } testGolden( 'Fill color (Green)', ValueDelegate.color([ 'Shape Layer 1', 'Rectangle', 'Fill 1', ], value: Colors.green), ); testGolden( 'Fill color (Yellow)', ValueDelegate.color([ 'Shape Layer 1', 'Rectangle', 'Fill 1', ], value: Colors.yellow), ); testGolden( 'Fill opacity', ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Fill 1'], value: 50), ); testGolden( 'Stroke color', ValueDelegate.strokeColor([ 'Shape Layer 1', 'Rectangle', 'Stroke 1', ], value: Colors.green), ); testGolden( 'Stroke width', ValueDelegate.strokeWidth([ 'Shape Layer 1', 'Rectangle', 'Stroke 1', ], value: 50), ); testGolden( 'Stroke opacity', ValueDelegate.opacity([ 'Shape Layer 1', 'Rectangle', 'Stroke 1', ], value: 50), ); testGolden( 'Transform anchor point', ValueDelegate.transformAnchorPoint([ 'Shape Layer 1', 'Rectangle', ], value: const Offset(20, 20)), ); testGolden( 'Transform position', ValueDelegate.transformPosition([ 'Shape Layer 1', 'Rectangle', ], value: const Offset(20, 20)), ); testGolden( 'Transform position (relative)', ValueDelegate.transformPosition([ 'Shape Layer 1', 'Rectangle', ], relative: const Offset(20, 20)), ); testGolden( 'Transform opacity', ValueDelegate.transformOpacity(['Shape Layer 1', 'Rectangle'], value: 50), ); testGolden( 'Transform rotation', ValueDelegate.transformRotation(['Shape Layer 1', 'Rectangle'], value: 45), ); testGolden( 'Transform scale', ValueDelegate.transformScale([ 'Shape Layer 1', 'Rectangle', ], value: const Offset(0.5, 0.5)), ); testGolden( 'Rectangle corner roundedness', ValueDelegate.cornerRadius([ 'Shape Layer 1', 'Rectangle', 'Rectangle Path 1', ], value: 7), ); testGolden( 'Rectangle position', ValueDelegate.position([ 'Shape Layer 1', 'Rectangle', 'Rectangle Path 1', ], relative: const Offset(20, 20)), ); testGolden( 'Rectangle size', ValueDelegate.rectangleSize([ 'Shape Layer 1', 'Rectangle', 'Rectangle Path 1', ], relative: const Offset(30, 40)), ); testGolden( 'Ellipse position', ValueDelegate.position([ 'Shape Layer 1', 'Ellipse', 'Ellipse Path 1', ], relative: const Offset(20, 20)), ); testGolden( 'Ellipse size', ValueDelegate.ellipseSize([ 'Shape Layer 1', 'Ellipse', 'Ellipse Path 1', ], relative: const Offset(40, 60)), ); testGolden( 'Star points', ValueDelegate.polystarPoints([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 8), ); testGolden( 'Star rotation', ValueDelegate.polystarRotation([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 10), ); testGolden( 'Star position', ValueDelegate.position([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], relative: const Offset(20, 20)), ); testGolden( 'Star inner radius', ValueDelegate.polystarInnerRadius([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 10), ); testGolden( 'Star inner roundedness', ValueDelegate.polystarInnerRoundedness([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 100), ); testGolden( 'Star outer radius', ValueDelegate.polystarOuterRadius([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 60), ); testGolden( 'Star outer roundedness', ValueDelegate.polystarOuterRoundedness([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 100), ); testGolden( 'Polygon points', ValueDelegate.polystarPoints([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 8), ); testGolden( 'Polygon rotation', ValueDelegate.polystarRotation([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 10), ); testGolden( 'Polygon position', ValueDelegate.position([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], relative: const Offset(20, 20)), ); testGolden( 'Polygon radius', ValueDelegate.polystarOuterRadius([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], relative: 60), ); testGolden( 'Polygon roundedness', ValueDelegate.polystarOuterRoundedness([ 'Shape Layer 1', 'Star', 'Polystar Path 1', ], value: 100), ); testGolden( 'Repeater transform position', ValueDelegate.transformPosition([ 'Shape Layer 1', 'Repeater Shape', 'Repeater 1', ], relative: const Offset(100, 100)), ); testGolden( 'Repeater transform start opacity', ValueDelegate.transformStartOpacity([ 'Shape Layer 1', 'Repeater Shape', 'Repeater 1', ], value: 25), ); testGolden( 'Repeater transform end opacity', ValueDelegate.transformEndOpacity([ 'Shape Layer 1', 'Repeater Shape', 'Repeater 1', ], value: 25), ); testGolden( 'Repeater transform rotation', ValueDelegate.transformRotation([ 'Shape Layer 1', 'Repeater Shape', 'Repeater 1', ], value: 45), ); testGolden( 'Repeater transform scale', ValueDelegate.transformScale([ 'Shape Layer 1', 'Repeater Shape', 'Repeater 1', ], value: const Offset(2, 2)), ); testGolden( 'Time remapping', ValueDelegate.timeRemap(['Circle 1'], value: 1), progress: 0.1, ); testGolden( 'Color Filter', ValueDelegate.colorFilter([ '**', ], value: const ColorFilter.mode(Colors.green, BlendMode.srcATop)), ); testGolden('Null Color Filter', ValueDelegate.colorFilter(['**'])); testGolden( 'Matte property', ValueDelegate.rectangleSize([ 'Shape Layer 1', 'Rectangle 1', 'Rectangle Path 1', ], value: const Offset(50, 50)), filePath: 'Tests/TrackMattes.json', ); testGolden('Blur', ValueDelegate.blurRadius(['**'], value: 10)); testGolden( 'Drop shadow', ValueDelegate.dropShadow( ['Shape Layer 1', '**'], value: const DropShadow( color: Colors.green, direction: 150, distance: 20, radius: 10, ), ), ); testGolden( 'Solid Color', ValueDelegate.color(['Cyan Solid 1', '**'], value: Colors.yellow), filePath: 'Tests/SolidLayerTransform.json', ); for (var progress in [0.0, 0.5, 1.0]) { testGolden( 'Opacity interpolation ($progress)', ValueDelegate.transformOpacity( ['Shape Layer 1', 'Rectangle'], callback: (frameInfo) => lerpDouble( 10, 100, Curves.linear.transform(frameInfo.overallProgress), )!.round(), ), progress: progress, ); } testWidgets('warningShimmer', (tester) async { var size = const Size(500, 400); tester.view.physicalSize = size; tester.view.devicePixelRatio = 1.0; var composition = await LottieComposition.fromBytes( File('test/data/warningShimmer.json').readAsBytesSync(), ); var delegates = >{ '1': [ for (var i in ['1', '2', '5']) ValueDelegate.color(['Layer $i Outlines', '**'], value: Colors.red), for (var i in ['3', '4']) ValueDelegate.color([ 'Layer $i Outlines', '**', ], value: Colors.greenAccent), ], '2': [ for (var i in ['1', '2', '5']) ValueDelegate.color([ 'Layer $i Outlines', 'Group 1', '*', ], value: Colors.red), for (var i in ['3', '4']) ValueDelegate.color([ 'Layer $i Outlines', 'Group 1', '*', ], value: Colors.greenAccent), ], '3': [ for (var i in ['1', '2', '5']) ValueDelegate.color([ 'Layer $i Outlines', 'Group 1', 'Fill 1', ], value: Colors.red), for (var i in ['3', '4']) ValueDelegate.color([ 'Layer $i Outlines', 'Group 1', 'Fill 1', ], value: Colors.greenAccent), ], }; for (var variant in delegates.entries) { await tester.pumpWidget( FilmStrip( composition, size: size, delegates: LottieDelegates(values: variant.value), ), ); await expectLater( find.byType(FilmStrip), matchesGoldenFile('goldens/warningShimmer_${variant.key}.png'), ); } }); }