Compare commits

...

12 Commits

123 changed files with 22552 additions and 661 deletions

View File

@ -12,18 +12,17 @@ jobs:
with:
channel: 'stable'
- run: flutter pub get
- run: flutter pub run tool/publish/comment_dependency_overrides.dart
- run: flutter pub get
- run: flutter pub run tool/publish/check_version.dart ${GITHUB_REF}
- name: Setup credentials
run: |
cat <<EOF > $PUB_CACHE/credentials.json
mkdir -p $XDG_CONFIG_HOME/dart
cat <<EOF > $XDG_CONFIG_HOME/dart/pub-credentials.json
{
"accessToken":"${{ secrets.OAUTH_ACCESS_TOKEN }}",
"refreshToken":"${{ secrets.OAUTH_REFRESH_TOKEN }}",
"tokenEndpoint":"https://accounts.google.com/o/oauth2/token",
"scopes": [ "openid", "https://www.googleapis.com/auth/userinfo.email" ],
"expiration": 1580681402856
"expiration": 1691492965565
}
EOF
- name: Publish package

View File

@ -1,3 +1,15 @@
## 2.6.0
- Accept `List<int>` instead of `Uint8List` in `LottieComposition.fromBytes`
- Stroke line cap defaults to butt instead of square
## 2.5.0
- Add layer-level opacity option to LottieOptions
- Fix TextLayer opacity calculation
## 2.4.0
- Require minimum Dart 3.0.0 and Flutter 3.10.0
- Fix a parsing bug when the name property in RoundedCorner was null
## 2.3.2
- Fix a bug when running on the web due to [bitwise operations difference](https://dart.dev/guides/language/numbers#bitwise-operations).

View File

@ -8,26 +8,52 @@ linter:
avoid_print: false
always_declare_return_types: true
avoid_bool_literals_in_conditional_expressions: true
avoid_double_and_int_checks: true
avoid_dynamic_calls: true
avoid_equals_and_hash_code_on_mutable_classes: true
avoid_escaping_inner_quotes: true
avoid_final_parameters: true
avoid_function_literals_in_foreach_calls: true
avoid_js_rounded_ints: true
avoid_positional_boolean_parameters: true
avoid_redundant_argument_values: true
avoid_returning_null_for_future: true
avoid_setters_without_getters: true
avoid_type_to_string: true
avoid_unused_constructor_parameters: true
cancel_subscriptions: true
cast_nullable_to_non_nullable: true
close_sinks: true
collection_methods_unrelated_type: true
combinators_ordering: true
conditional_uri_does_not_exist: true
dangling_library_doc_comments: true
deprecated_consistency: true
implicit_reopen: true
invalid_case_patterns: true
leading_newlines_in_multiline_strings: true
library_annotations: true
no_adjacent_strings_in_list: true
no_default_cases: true
noop_primitive_operations: true
omit_local_variable_types: true
only_throw_errors: true
prefer_if_elements_to_conditional_expressions: true
prefer_relative_imports: true
prefer_single_quotes: true
sort_child_properties_last: true
sort_pub_dependencies: true
test_types_in_equals: true
unawaited_futures: true
unnecessary_breaks: true
unnecessary_library_directive: true
unnecessary_parenthesis: true
unnecessary_statements: true
unnecessary_to_list_in_spreads: true
unsafe_html: true
use_enums: true
use_if_null_to_convert_nulls_to_bools: true
use_named_constants: true
use_raw_strings: true
use_super_parameters: true

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{"v":"5.1.20","fr":30,"ip":0,"op":100,"w":600,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"test Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[100],"e":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":30,"s":[0],"e":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":50,"s":[0],"e":[100]},{"t":80}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,200,0],"ix":2},"a":{"a":0,"k":[400,300,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-66.274],[66.274,0],[0,66.274],[-66.274,0]],"o":[[0,66.274],[-66.274,0],[0,-66.274],[66.274,0]],"v":[[120,0],[0,120],[-120,0],[0,-120]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[488,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-66.274],[66.274,0],[0,66.274],[-66.274,0]],"o":[[0,66.274],[-66.274,0],[0,-66.274],[66.274,0]],"v":[[120,0],[0,120],[-120,0],[0,-120]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40800000359,0.40800000359,0.40800000359,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[312,300],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"bm":0}],"markers":[]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,130 @@
import 'dart:ui';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/material.dart' as material;
import 'package:lottie/lottie.dart';
/// This example shows how to cache the animation as a List<Image>.
/// After the initial cache of each frame, drawing the animation is almost free
/// in term of CPU usage.
/// The animation will run at a specific framerate (not FrameRate.max) and specific size
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Pre-load the animation for simplicity in this example
var animation = await AssetLottie('assets/AndroidWave.json').load();
// Pick a specific size for our cache.
// In a real app, we may want to defer choosing the size after an initial
// Layout (ie. using LayoutBuilder)
var cachedAnimation = CachedLottie(const Size(150, 200), animation);
runApp(_Example(
lottie: cachedAnimation,
));
}
class _Example extends StatelessWidget {
final CachedLottie lottie;
const _Example({required this.lottie});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Cache'),
),
body: ListView(
children: [
for (var i = 0; i < 20; i++)
Stack(
children: [
for (var j = 0; j < 50; j++)
Transform.translate(
offset: Offset(j.toDouble() * 20, 0),
child: CachedLottiePlayer(
lottie: lottie,
),
)
],
),
],
),
),
);
}
}
class CachedLottie {
final Size size;
final LottieComposition composition;
final List<Image?> images;
late final _drawable = LottieDrawable(composition);
CachedLottie(this.size, this.composition)
: images = List.filled(composition.durationFrames.ceil(), null);
Duration get duration => composition.duration;
Image imageAt(BuildContext context, double progress) {
var index = (images.length * progress).round() % images.length;
return images[index] ??= _takeImage(context, progress);
}
Image _takeImage(BuildContext context, double progress) {
var recorder = PictureRecorder();
var canvas = Canvas(recorder);
var devicePixelRatio = View.of(context).devicePixelRatio;
_drawable
..setProgress(progress)
..draw(canvas, Offset.zero & (size * devicePixelRatio));
var picture = recorder.endRecording();
return picture.toImageSync((size.width * devicePixelRatio).round(),
(size.height * devicePixelRatio).round());
}
}
class CachedLottiePlayer extends StatefulWidget {
final CachedLottie lottie;
final AnimationController? controller;
const CachedLottiePlayer({
super.key,
required this.lottie,
this.controller,
});
@override
State<CachedLottiePlayer> createState() => _CachedLottiePlayerState();
}
class _CachedLottiePlayerState extends State<CachedLottiePlayer>
with TickerProviderStateMixin {
late final AnimationController _autoController =
AnimationController(vsync: this, duration: widget.lottie.duration)
..repeat();
@override
Widget build(BuildContext context) {
var controller = widget.controller ?? _autoController;
return AnimatedBuilder(
animation: controller,
builder: (context, _) {
var image = widget.lottie.imageAt(context, controller.value);
return material.RawImage(
image: image,
width: widget.lottie.size.width,
height: widget.lottie.size.height,
);
},
);
}
@override
void dispose() {
_autoController.dispose();
super.dispose();
}
}

View File

@ -16,8 +16,8 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
COCOAPODS: 1.12.0
COCOAPODS: 1.12.1

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
url: "https://pub.dev"
source: hosted
version: "3.3.6"
version: "3.3.7"
async:
dependency: transitive
description:
@ -61,10 +61,10 @@ packages:
dependency: transitive
description:
name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
fake_async:
dependency: transitive
description:
@ -77,18 +77,10 @@ packages:
dependency: transitive
description:
name: ffi
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
url: "https://pub.dev"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "2.0.2"
flutter:
dependency: "direct main"
description: flutter
@ -106,10 +98,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.0.2"
flutter_test:
dependency: "direct dev"
description: flutter
@ -127,10 +119,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
version: "1.1.0"
http_parser:
dependency: transitive
description:
@ -151,25 +143,25 @@ packages:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.1.1"
logging:
dependency: "direct main"
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
lottie:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "2.3.2"
version: "2.6.0"
matcher:
dependency: transitive
description:
@ -206,50 +198,50 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
url: "https://pub.dev"
source: hosted
version: "2.0.14"
version: "2.0.15"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7"
sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8"
url: "https://pub.dev"
source: hosted
version: "2.0.24"
version: "2.1.0"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "818b2dc38b0f178e0ea3f7cf3b28146faab11375985d815942a68eee11c2d0f7"
sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.3.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.2.0"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.1.0"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
platform:
dependency: transitive
description:
@ -262,26 +254,18 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.2"
process:
dependency: transitive
description:
name: process
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
version: "3.7.3"
sky_engine:
dependency: transitive
description: flutter
@ -339,10 +323,10 @@ packages:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
vector_math:
dependency: transitive
description:
@ -355,18 +339,18 @@ packages:
dependency: transitive
description:
name: win32
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "5.0.6"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.0.1"
sdks:
dart: ">=3.0.0-0 <4.0.0"
flutter: ">=3.3.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"

View File

@ -3,7 +3,7 @@ description: A sample app for the Lottie player
publish_to: none
environment:
sdk: ">=2.18.0 <3.0.0"
sdk: "^3.0.0"
dependencies:
flutter:

View File

@ -10,7 +10,7 @@ export 'src/options.dart' show LottieOptions;
export 'src/providers/asset_provider.dart' show AssetLottie;
export 'src/providers/file_provider.dart' show FileLottie;
export 'src/providers/load_image.dart' show LottieImageProviderFactory;
export 'src/providers/lottie_provider.dart' show LottieProvider, LottieCache;
export 'src/providers/lottie_provider.dart' show LottieCache, LottieProvider;
export 'src/providers/memory_provider.dart' show MemoryLottie;
export 'src/providers/network_provider.dart' show NetworkLottie;
export 'src/raw_lottie.dart' show RawLottie;

View File

@ -144,7 +144,7 @@ abstract class BaseStrokeContent
}
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
paint.setAlpha(alpha.clamp(0, 255).toInt());
paint.setAlpha(alpha.clamp(0, 255));
paint.strokeWidth = _widthAnimation.value * parentMatrix.getScale();
if (paint.strokeWidth <= 0) {
// Android draws a hairline stroke for 0, After Effects doesn't.

View File

@ -58,12 +58,14 @@ class ContentGroup implements DrawingContent, PathContent, KeyPathElement {
lottieDrawable,
layer,
shapeGroup.name,
shapeGroup.hidden,
contentsFromModels(lottieDrawable, layer, shapeGroup.items),
findTransform(shapeGroup.items));
findTransform(shapeGroup.items),
hidden: shapeGroup.hidden);
ContentGroup.copy(this._lottieDrawable, BaseLayer layer, this.name,
this._hidden, this._contents, AnimatableTransform? transform) {
this._contents, AnimatableTransform? transform,
{required bool hidden})
: _hidden = hidden {
if (transform != null) {
_transformAnimation = transform.createAnimation()
..addAnimationsToLayer(layer)

View File

@ -89,7 +89,7 @@ class FillContent implements DrawingContent, KeyPathElementContent {
_paint.color = _colorAnimation.value;
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
_paint.setAlpha(alpha.clamp(0, 255).toInt());
_paint.setAlpha(alpha.clamp(0, 255));
if (lottieDrawable.antiAliasingSuggested) {
_paint.isAntiAlias = true;
}

View File

@ -135,7 +135,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
_paint.setAlpha(alpha.clamp(0, 255).toInt());
_paint.setAlpha(alpha.clamp(0, 255));
if (lottieDrawable.antiAliasingSuggested) {
_paint.isAntiAlias = true;
}
@ -175,8 +175,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var gradientColor = _colorAnimation.value;
var colors = _applyDynamicColorsIfNeeded(gradientColor.colors);
var positions = gradientColor.positions;
gradient = Gradient.linear(
startPoint, endPoint, colors, positions, TileMode.clamp);
gradient = Gradient.linear(startPoint, endPoint, colors, positions);
_linearGradientCache[gradientHash] = gradient;
return gradient;
}
@ -200,8 +199,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
if (radius <= 0) {
radius = 0.001;
}
gradient =
Gradient.radial(startPoint, radius, colors, positions, TileMode.clamp);
gradient = Gradient.radial(startPoint, radius, colors, positions);
_radialGradientCache[gradientHash] = gradient;
return gradient;
}

View File

@ -50,19 +50,14 @@ class MergePathsContent implements PathContent, GreedyContent {
switch (_mergePaths.mode) {
case MergePathsMode.merge:
_addPaths();
break;
case MergePathsMode.add:
_opFirstPathWithRest(PathOperation.union);
break;
case MergePathsMode.substract:
_opFirstPathWithRest(PathOperation.reverseDifference);
break;
case MergePathsMode.intersect:
_opFirstPathWithRest(PathOperation.intersect);
break;
case MergePathsMode.excludeIntersections:
_opFirstPathWithRest(PathOperation.xor);
break;
}
return _path;

View File

@ -108,10 +108,8 @@ class PolystarContent implements PathContent, KeyPathElementContent {
switch (_polystarShape.type) {
case PolystarShapeType.star:
_createStarPath();
break;
case PolystarShapeType.polygon:
_createPolygonPath();
break;
}
_path.close();
@ -253,7 +251,7 @@ class PolystarContent implements PathContent, KeyPathElementContent {
_path.moveTo(x, y);
currentAngle += anglePerPoint;
var numPoints = points.ceil().toDouble();
var numPoints = points.toDouble();
for (var i = 0; i < numPoints; i++) {
previousX = x;
previousY = y;

View File

@ -79,7 +79,8 @@ class RepeaterContent
newContents = newContents.reversed.toList();
_contentGroup = ContentGroup.copy(
lottieDrawable, layer, 'Repeater', _repeater.hidden, newContents, null);
lottieDrawable, layer, 'Repeater', newContents, null,
hidden: _repeater.hidden);
}
@override

View File

@ -216,7 +216,7 @@ class RoundedCornersContent implements ShapeModifierContent {
this.shapeData = shapeData =
ShapeData(newCurves, initialPoint: Offset.zero, closed: false);
}
shapeData.setClosed(isClosed);
shapeData.isClosed = isClosed;
return shapeData;
}

View File

@ -41,7 +41,7 @@ class LottieComposition {
name: name, imageProviderFactory: imageProviderFactory);
}
static Future<LottieComposition> fromBytes(Uint8List bytes,
static Future<LottieComposition> fromBytes(List<int> bytes,
{String? name, LottieImageProviderFactory? imageProviderFactory}) async {
Archive? archive;
if (bytes[0] == 0x50 && bytes[1] == 0x4B) {

View File

@ -302,6 +302,7 @@ class Lottie extends StatefulWidget {
/// Some options to enable/disable some feature of Lottie
/// - enableMergePaths: Enable merge path support
/// - enableApplyingOpacityToLayers: Enable layer-level opacity
final LottieOptions? options;
/// Indicate to automatically add a `RepaintBoundary` widget around the animation.

View File

@ -219,6 +219,7 @@ class LottieBuilder extends StatefulWidget {
/// Some options to enable/disable some feature of Lottie
/// - enableMergePaths: Enable merge path support
/// - enableApplyingOpacityToLayers: Enable layer-level opacity
final LottieOptions? options;
/// A builder function responsible for creating the widget that represents

View File

@ -5,12 +5,12 @@ import '../cubic_curve_data.dart';
class ShapeData {
final List<CubicCurveData> curves;
Offset _initialPoint;
bool _closed;
bool isClosed;
ShapeData(List<CubicCurveData> curves, {Offset? initialPoint, bool? closed})
: curves = curves.toList(),
_initialPoint = initialPoint ?? Offset.zero,
_closed = closed ?? false;
isClosed = closed ?? false;
ShapeData.empty() : this([]);
@ -22,17 +22,9 @@ class ShapeData {
return _initialPoint;
}
void setClosed(bool closed) {
_closed = closed;
}
bool get isClosed {
return _closed;
}
void interpolateBetween(
ShapeData shapeData1, ShapeData shapeData2, double percentage) {
_closed = shapeData1.isClosed || shapeData2.isClosed;
isClosed = shapeData1.isClosed || shapeData2.isClosed;
if (shapeData1.curves.length != shapeData2.curves.length) {
// TODO(xha): decide what to do? We don't have access to the LottieDrawble
@ -86,7 +78,7 @@ class ShapeData {
String toString() {
return 'ShapeData{'
'numCurves=${curves.length}'
'closed=$_closed'
'closed=$isClosed'
'}';
}
}

View File

@ -18,7 +18,7 @@ ui.StrokeCap lineCapTypeToPaintCap(LineCapType? cap) {
return ui.StrokeCap.round;
case LineCapType.unknown:
case null:
return ui.StrokeCap.square;
return ui.StrokeCap.butt;
}
}

View File

@ -1,7 +1,9 @@
import 'dart:ui';
import 'package:flutter/foundation.dart';
enum Justification { leftAlign, rightAlign, center }
@immutable
class DocumentData {
final String text;
final String? fontName;
@ -19,7 +21,7 @@ class DocumentData {
final Offset? boxPosition;
final Offset? boxSize;
DocumentData({
const DocumentData({
required this.text,
this.fontName,
required this.size,

View File

@ -1,5 +1,7 @@
import 'package:flutter/foundation.dart';
import 'content/shape_group.dart';
@immutable
class FontCharacter {
static int hashFor(String character, String fontFamily, String style) {
var result = character.hashCode;
@ -15,7 +17,7 @@ class FontCharacter {
final String style;
final String fontFamily;
FontCharacter(
const FontCharacter(
{required this.shapes,
required this.character,
required this.size,

View File

@ -363,7 +363,6 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
_contentPaint.setAlpha(255);
canvas.drawRect(bounds, _contentPaint);
}
break;
case MaskMode.maskModeAdd:
if (mask.isInverted) {
_applyInvertedAddMask(
@ -372,7 +371,6 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
_applyAddMask(
canvas, matrix, mask, maskAnimation, opacityAnimation);
}
break;
case MaskMode.maskModeSubstract:
if (i == 0) {
_contentPaint.color = const ui.Color(0xFF000000);
@ -385,7 +383,6 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
_applySubtractMask(
canvas, matrix, mask, maskAnimation, opacityAnimation);
}
break;
case MaskMode.maskModeIntersect:
if (mask.isInverted) {
_applyInvertedIntersectMask(
@ -394,7 +391,6 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
_applyIntersectMask(
canvas, bounds, matrix, mask, maskAnimation, opacityAnimation);
}
break;
}
}
L.beginSection('Layer#restoreLayer');

View File

@ -50,7 +50,6 @@ class CompositionLayer extends BaseLayer {
case MatteType.add:
case MatteType.invert:
mattedLayer = layer;
break;
case MatteType.luma:
case MatteType.lumaInverted:
case MatteType.none:

View File

@ -100,7 +100,7 @@ class TextLayer extends BaseLayer {
canvas.save();
canvas.transform(parentMatrix.storage);
_configurePaint(documentData, parentMatrix);
_configurePaint(documentData, parentAlpha);
if (lottieDrawable.useTextGlyphs) {
_drawTextWithGlyphs(documentData, parentMatrix, font, canvas);
@ -111,7 +111,7 @@ class TextLayer extends BaseLayer {
canvas.restore();
}
void _configurePaint(DocumentData documentData, Matrix4 parentMatrix) {
void _configurePaint(DocumentData documentData, int parentAlpha) {
Color fillPaintColor;
if (_colorCallbackAnimation != null) {
fillPaintColor = _colorCallbackAnimation!.value;
@ -133,7 +133,7 @@ class TextLayer extends BaseLayer {
_strokePaint.color = strokePaintColor.withAlpha(_strokePaint.color.alpha);
var opacity = transform.opacity?.value ?? 100;
var alpha = (opacity * 255 / 100).round();
var alpha = opacity * 255 / 100 * parentAlpha ~/ 255;
_fillPaint.setAlpha(alpha);
_strokePaint.setAlpha(alpha);
@ -195,8 +195,7 @@ class TextLayer extends BaseLayer {
void _drawGlyphTextLine(Characters text, DocumentData documentData, Font font,
Canvas canvas, double parentScale, double fontScale, double tracking) {
for (var c in text) {
var characterHash =
FontCharacter.hashFor(c.toString(), font.family, font.style);
var characterHash = FontCharacter.hashFor(c, font.family, font.style);
var character = _composition.characters[characterHash];
if (character == null) {
// Something is wrong. Potentially, they didn't export the text as a glyph.
@ -269,14 +268,11 @@ class TextLayer extends BaseLayer {
switch (documentData.justification) {
case Justification.leftAlign:
canvas.translate(lineStart, lineOffset);
break;
case Justification.rightAlign:
canvas.translate(lineStart + boxWidth - lineWidth, lineOffset);
break;
case Justification.center:
canvas.translate(
lineStart + boxWidth / 2.0 - lineWidth / 2.0, lineOffset);
break;
}
}

View File

@ -6,6 +6,16 @@ class LottieOptions {
/// instead of using merge paths.
final bool enableMergePaths;
LottieOptions({bool? enableMergePaths})
: enableMergePaths = enableMergePaths ?? false;
/// Enable layer-level opacity.
///
/// Add the ability to render opacity on the layer level rather than the shape level.
/// Opacity is normally applied directly to a shape. In cases where translucent shapes overlap,
/// applying opacity to a layer will be more accurate at the expense of performance.
/// Details: https://github.com/airbnb/lottie-android/issues/902
final bool enableApplyingOpacityToLayers;
LottieOptions({
this.enableMergePaths = false,
this.enableApplyingOpacityToLayers = false,
});
}

View File

@ -47,7 +47,6 @@ class AnimatablePathValueParser {
switch (reader.selectName(_names)) {
case 0:
pathAnimation = AnimatablePathValueParser.parse(reader, composition);
break;
case 1:
if (reader.peek() == Token.string) {
hasExpressions = true;
@ -55,7 +54,6 @@ class AnimatablePathValueParser {
} else {
xAnimation = AnimatableValueParser.parseFloat(reader, composition);
}
break;
case 2:
if (reader.peek() == Token.string) {
hasExpressions = true;
@ -63,7 +61,6 @@ class AnimatablePathValueParser {
} else {
yAnimation = AnimatableValueParser.parseFloat(reader, composition);
}
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -21,7 +21,6 @@ class AnimatableTextPropertiesParser {
switch (reader.selectName(_propertiesNames)) {
case 0:
anim = _parseAnimatableTextProperties(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();
@ -47,16 +46,12 @@ class AnimatableTextPropertiesParser {
switch (reader.selectName(_animatablePropertiesNames)) {
case 0:
color = AnimatableValueParser.parseColor(reader, composition);
break;
case 1:
stroke = AnimatableValueParser.parseColor(reader, composition);
break;
case 2:
strokeWidth = AnimatableValueParser.parseFloat(reader, composition);
break;
case 3:
tracking = AnimatableValueParser.parseFloat(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -45,21 +45,17 @@ class AnimatableTransformParser {
case 0:
anchorPoint =
AnimatablePathValueParser.parse(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
break;
case 1:
position =
AnimatablePathValueParser.parseSplitPath(reader, composition);
break;
case 2:
scale = AnimatableValueParser.parseScale(reader, composition);
break;
case 3:
case 4:
if (name == 3) {
@ -79,33 +75,25 @@ class AnimatableTransformParser {
rotation.keyframes.add(Keyframe(composition,
startValue: 0.0,
endValue: 0.0,
interpolator: null,
startFrame: 0.0,
endFrame: composition.endFrame));
} else if (rotation.keyframes.first.startValue == null) {
rotation.keyframes.first = Keyframe(composition,
startValue: 0.0,
endValue: 0.0,
interpolator: null,
startFrame: 0.0,
endFrame: composition.endFrame);
}
break;
case 5:
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 6:
startOpacity = AnimatableValueParser.parseFloat(reader, composition);
break;
case 7:
endOpacity = AnimatableValueParser.parseFloat(reader, composition);
break;
case 8:
skew = AnimatableValueParser.parseFloat(reader, composition);
break;
case 9:
skewAngle = AnimatableValueParser.parseFloat(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -22,7 +22,6 @@ class BlurEffectParser {
}
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
@ -40,7 +39,6 @@ class BlurEffectParser {
switch (reader.selectName(_innerBlurEffectNames)) {
case 0:
isCorrectType = reader.nextInt() == 0;
break;
case 1:
if (isCorrectType) {
blurEffect = BlurEffect(
@ -48,7 +46,6 @@ class BlurEffectParser {
} else {
reader.skipValue();
}
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -25,21 +25,16 @@ class CircleShapeParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
position =
AnimatablePathValueParser.parseSplitPath(reader, composition);
break;
case 2:
size = AnimatableValueParser.parsePoint(reader, composition);
break;
case 3:
hidden = reader.nextBoolean();
break;
case 4:
// "d" is 2 for normal and 3 for reversed.
reversed = reader.nextInt() == 3;
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -37,7 +37,6 @@ class ContentModelParser {
break typeLoop;
case 1:
d = reader.nextInt();
break;
default:
reader.skipName();
reader.skipValue();
@ -52,46 +51,32 @@ class ContentModelParser {
switch (type) {
case 'gr':
model = ShapeGroupParser.parse(reader, composition);
break;
case 'st':
model = ShapeStrokeParser.parse(reader, composition);
break;
case 'gs':
model = GradientStrokeParser.parse(reader, composition);
break;
case 'fl':
model = ShapeFillParser.parse(reader, composition);
break;
case 'gf':
model = GradientFillParser.parse(reader, composition);
break;
case 'tr':
model = AnimatableTransformParser.parse(reader, composition);
break;
case 'sh':
model = ShapePathParser.parse(reader, composition);
break;
case 'el':
model = CircleShapeParser.parse(reader, composition, d);
break;
case 'rc':
model = RectangleShapeParser.parse(reader, composition);
break;
case 'tm':
model = ShapeTrimPathParser.parse(reader, composition);
break;
case 'sr':
model = PolystarShapeParser.parse(reader, composition, d: d);
break;
case 'mm':
model = MergePathsParser.parse(reader);
break;
case 'rp':
model = RepeaterParser.parse(reader, composition);
break;
case 'rd':
model = RoundedCornersParser.parse(reader, composition);
break;
default:
composition.addWarning('Unknown shape type $type');
}

View File

@ -39,13 +39,10 @@ DocumentData documentDataParser(JsonReader reader) {
switch (reader.selectName(_names)) {
case 0:
text = reader.nextString();
break;
case 1:
fontName = reader.nextString();
break;
case 2:
size = reader.nextDouble();
break;
case 3:
var justificationInt = reader.nextInt();
if (justificationInt > Justification.center.index ||
@ -54,38 +51,28 @@ DocumentData documentDataParser(JsonReader reader) {
} else {
justification = Justification.values[justificationInt];
}
break;
case 4:
tracking = reader.nextInt();
break;
case 5:
lineHeight = reader.nextDouble();
break;
case 6:
baselineShift = reader.nextDouble();
break;
case 7:
fillColor = JsonUtils.jsonToColor(reader);
break;
case 8:
strokeColor = JsonUtils.jsonToColor(reader);
break;
case 9:
strokeWidth = reader.nextDouble();
break;
case 10:
strokeOverFill = reader.nextBoolean();
break;
case 11:
reader.beginArray();
boxPosition = Offset(reader.nextDouble(), reader.nextDouble());
reader.endArray();
break;
case 12:
reader.beginArray();
boxSize = Offset(reader.nextDouble(), reader.nextDouble());
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -27,7 +27,6 @@ class DropShadowEffectParser {
_maybeParseInnerEffect(reader, composition);
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
@ -62,30 +61,23 @@ class DropShadowEffectParser {
switch (reader.selectName(_innerEffectNames)) {
case 0:
currentEffectName = reader.nextString();
break;
case 1:
switch (currentEffectName) {
case 'Shadow Color':
_color = AnimatableValueParser.parseColor(reader, composition);
break;
case 'Opacity':
_opacity = AnimatableValueParser.parseFloat(reader, composition);
break;
case 'Direction':
_direction =
AnimatableValueParser.parseFloat(reader, composition);
break;
case 'Distance':
_distance = AnimatableValueParser.parseFloat(reader, composition);
break;
case 'Softness':
_radius = AnimatableValueParser.parseFloat(reader, composition);
break;
default:
reader.skipValue();
break;
}
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -24,19 +24,14 @@ class FontCharacterParser {
switch (reader.selectName(_names)) {
case 0:
character = reader.nextString();
break;
case 1:
size = reader.nextDouble();
break;
case 2:
width = reader.nextDouble();
break;
case 3:
style = reader.nextString();
break;
case 4:
fontFamily = reader.nextString();
break;
case 5:
reader.beginObject();
while (reader.hasNext()) {
@ -44,18 +39,18 @@ class FontCharacterParser {
case 0:
reader.beginArray();
while (reader.hasNext()) {
shapes.add(ContentModelParser.parse(reader, composition)!
as ShapeGroup);
if (ContentModelParser.parse(reader, composition)
case var shape?) {
shapes.add(shape as ShapeGroup);
}
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -18,16 +18,12 @@ class FontParser {
switch (reader.selectName(_names)) {
case 0:
family = reader.nextString();
break;
case 1:
name = reader.nextString();
break;
case 2:
style = reader.nextString();
break;
case 3:
ascent = reader.nextDouble();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -69,17 +69,13 @@ class GradientColorParser {
case 0:
// position
positions[colorIndex] = value;
break;
case 1:
r = (value * 255).round();
break;
case 2:
g = (value * 255).round();
break;
case 3:
var b = (value * 255).round();
colors[colorIndex] = Color.fromARGB(255, r, g, b);
break;
}
}

View File

@ -31,7 +31,6 @@ class GradientFillParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
var points = -1;
reader.beginObject();
@ -39,39 +38,30 @@ class GradientFillParser {
switch (reader.selectName(_gradientNames)) {
case 0:
points = reader.nextInt();
break;
case 1:
color = AnimatableValueParser.parseGradientColor(
reader, composition, points);
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
break;
case 2:
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 3:
gradientType =
reader.nextInt() == 1 ? GradientType.linear : GradientType.radial;
break;
case 4:
startPoint = AnimatableValueParser.parsePoint(reader, composition);
break;
case 5:
endPoint = AnimatableValueParser.parsePoint(reader, composition);
break;
case 6:
fillType = reader.nextInt() == 1
? PathFillType.nonZero
: PathFillType.evenOdd;
break;
case 7:
hidden = reader.nextBoolean();
break;
default:
reader.skipName();
reader.skipValue();
@ -90,8 +80,6 @@ class GradientFillParser {
opacity: opacity,
startPoint: startPoint!,
endPoint: endPoint!,
highlightLength: null,
highlightAngle: null,
hidden: hidden,
);
}

View File

@ -40,7 +40,6 @@ class GradientStrokeParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
var points = -1;
reader.beginObject();
@ -48,46 +47,34 @@ class GradientStrokeParser {
switch (reader.selectName(_gradientNames)) {
case 0:
points = reader.nextInt();
break;
case 1:
color = AnimatableValueParser.parseGradientColor(
reader, composition, points);
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
break;
case 2:
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 3:
gradientType =
reader.nextInt() == 1 ? GradientType.linear : GradientType.radial;
break;
case 4:
startPoint = AnimatableValueParser.parsePoint(reader, composition);
break;
case 5:
endPoint = AnimatableValueParser.parsePoint(reader, composition);
break;
case 6:
width = AnimatableValueParser.parseFloat(reader, composition);
break;
case 7:
capType = LineCapType.values[reader.nextInt() - 1];
break;
case 8:
joinType = LineJoinType.values[reader.nextInt() - 1];
break;
case 9:
miterLimit = reader.nextDouble();
break;
case 10:
hidden = reader.nextBoolean();
break;
case 11:
reader.beginArray();
while (reader.hasNext()) {
@ -98,10 +85,8 @@ class GradientStrokeParser {
switch (reader.selectName(_dashPatternNames)) {
case 0:
n = reader.nextString();
break;
case 1:
val = AnimatableValueParser.parseFloat(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();
@ -121,7 +106,6 @@ class GradientStrokeParser {
// If there is only 1 value then it is assumed to be equal parts on and off.
lineDashPattern.add(lineDashPattern[0]);
}
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -35,8 +35,14 @@ class JsonUtils {
return _jsonArrayToPoint(reader);
case Token.beginObject:
return _jsonObjectToPoint(reader);
// ignore: no_default_cases
default:
case Token.nullToken:
return Offset.zero;
case Token.endArray:
case Token.endObject:
case Token.name:
case Token.string:
case Token.boolean:
case Token.endDocument:
throw Exception('Unknown point starts with ${reader.peek()}');
}
}
@ -73,10 +79,8 @@ class JsonUtils {
switch (reader.selectName(_pointNames)) {
case 0:
x = valueFromObject(reader);
break;
case 1:
y = valueFromObject(reader);
break;
default:
reader.skipName();
reader.skipValue();
@ -99,8 +103,14 @@ class JsonUtils {
}
reader.endArray();
return val;
// ignore: no_default_cases
default:
case Token.endArray:
case Token.beginObject:
case Token.endObject:
case Token.name:
case Token.string:
case Token.boolean:
case Token.nullToken:
case Token.endDocument:
throw Exception('Unknown value for token of type $token');
}
}

View File

@ -58,28 +58,20 @@ class KeyframeParser {
switch (reader.selectName(_names)) {
case 0:
startFrame = reader.nextDouble();
break;
case 1:
startValue = valueParser(reader);
break;
case 2:
endValue = valueParser(reader);
break;
case 3:
cp1 = JsonUtils.jsonToPoint(reader);
break;
case 4:
cp2 = JsonUtils.jsonToPoint(reader);
break;
case 5:
hold = reader.nextInt() == 1;
break;
case 6:
pathCp1 = JsonUtils.jsonToPoint(reader);
break;
case 7:
pathCp2 = JsonUtils.jsonToPoint(reader);
break;
default:
reader.skipValue();
}
@ -96,12 +88,13 @@ class KeyframeParser {
interpolator = _linearInterpolator;
}
var keyframe = Keyframe<T>(composition,
startValue: startValue,
endValue: endValue,
interpolator: interpolator,
startFrame: startFrame,
endFrame: null);
var keyframe = Keyframe<T>(
composition,
startValue: startValue,
endValue: endValue,
interpolator: interpolator,
startFrame: startFrame,
);
keyframe.pathCp1 = pathCp1;
keyframe.pathCp2 = pathCp2;
return keyframe;
@ -136,13 +129,10 @@ class KeyframeParser {
switch (reader.selectName(_names)) {
case 0: // t
startFrame = reader.nextDouble();
break;
case 1: // s
startValue = valueParser(reader);
break;
case 2: // e
endValue = valueParser(reader);
break;
case 3: // o
if (reader.peek() == Token.beginObject) {
reader.beginObject();
@ -166,7 +156,6 @@ class KeyframeParser {
}
reader.endArray();
}
break;
case 1: // y
if (reader.peek() == Token.number) {
xCp1y = reader.nextDouble();
@ -181,7 +170,6 @@ class KeyframeParser {
}
reader.endArray();
}
break;
default:
reader.skipValue();
}
@ -192,7 +180,6 @@ class KeyframeParser {
} else {
cp1 = JsonUtils.jsonToPoint(reader);
}
break;
case 4: // i
if (reader.peek() == Token.beginObject) {
reader.beginObject();
@ -216,7 +203,6 @@ class KeyframeParser {
}
reader.endArray();
}
break;
case 1: // y
if (reader.peek() == Token.number) {
xCp2y = reader.nextDouble();
@ -231,7 +217,6 @@ class KeyframeParser {
}
reader.endArray();
}
break;
default:
reader.skipValue();
}
@ -242,16 +227,12 @@ class KeyframeParser {
} else {
cp2 = JsonUtils.jsonToPoint(reader);
}
break;
case 5: // h
hold = reader.nextInt() == 1;
break;
case 6: // to
pathCp1 = JsonUtils.jsonToPoint(reader);
break;
case 7: // ti
pathCp2 = JsonUtils.jsonToPoint(reader);
break;
default:
reader.skipValue();
}

View File

@ -44,7 +44,6 @@ class KeyframesParser {
keyframes.add(KeyframeParser.parse(reader, composition, valueParser,
animated: false, multiDimensional: multiDimensional));
}
break;
default:
reader.skipValue();
}

View File

@ -58,7 +58,6 @@ class LayerParser {
id: -1,
layerType: LayerType.preComp,
parentId: -1,
refId: null,
masks: <Mask>[],
transform: AnimatableTransform(),
solidWidth: 0,
@ -68,11 +67,8 @@ class LayerParser {
startFrame: 0,
preCompWidth: bounds.width,
preCompHeight: bounds.height,
text: null,
textProperties: null,
inOutKeyframes: <Keyframe<double>>[],
matteType: MatteType.none,
timeRemapping: null,
isHidden: false);
}
@ -117,13 +113,10 @@ class LayerParser {
switch (reader.selectName(_names)) {
case 0:
layerName = reader.nextString();
break;
case 1:
layerId = reader.nextInt();
break;
case 2:
refId = reader.nextString();
break;
case 3:
var layerTypeInt = reader.nextInt();
if (layerTypeInt < LayerType.unknown.index) {
@ -131,23 +124,17 @@ class LayerParser {
} else {
layerType = LayerType.unknown;
}
break;
case 4:
parentId = reader.nextInt();
break;
case 5:
solidWidth = reader.nextInt();
break;
case 6:
solidHeight = reader.nextInt();
break;
case 7:
solidColor = MiscUtils.parseColor(reader.nextString(),
warningCallback: composition.addWarning);
break;
case 8:
transform = AnimatableTransformParser.parse(reader, composition);
break;
case 9:
var matteTypeIndex = reader.nextInt();
if (matteTypeIndex >= MatteType.values.length) {
@ -161,7 +148,6 @@ class LayerParser {
composition.addWarning('Unsupported matte type: Luma Inverted');
}
composition.incrementMatteOrMaskCount(1);
break;
case 10:
reader.beginArray();
while (reader.hasNext()) {
@ -169,7 +155,6 @@ class LayerParser {
}
composition.incrementMatteOrMaskCount(masks.length);
reader.endArray();
break;
case 11:
reader.beginArray();
while (reader.hasNext()) {
@ -179,7 +164,6 @@ class LayerParser {
}
}
reader.endArray();
break;
case 12:
reader.beginObject();
while (reader.hasNext()) {
@ -187,7 +171,6 @@ class LayerParser {
case 0:
text = AnimatableValueParser.parseDocumentData(
reader, composition);
break;
case 1:
reader.beginArray();
if (reader.hasNext()) {
@ -198,14 +181,12 @@ class LayerParser {
reader.skipValue();
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
break;
case 13:
reader.beginArray();
var effectNames = <String>[];
@ -221,11 +202,9 @@ class LayerParser {
dropShadowEffect =
DropShadowEffectParser().parse(reader, composition);
}
break;
case 1:
var effectName = reader.nextString();
effectNames.add(effectName);
break;
default:
reader.skipName();
reader.skipValue();
@ -238,34 +217,24 @@ class LayerParser {
"Lottie doesn't support layer effects. If you are using them for "
' fills, strokes, trim paths etc. then try adding them directly as contents '
' in your shape. Found: $effectNames');
break;
case 14:
timeStretch = reader.nextDouble();
break;
case 15:
startFrame = reader.nextDouble();
break;
case 16:
preCompWidth = reader.nextInt();
break;
case 17:
preCompHeight = reader.nextInt();
break;
case 18:
inFrame = reader.nextDouble();
break;
case 19:
outFrame = reader.nextDouble();
break;
case 20:
timeRemapping = AnimatableValueParser.parseFloat(reader, composition);
break;
case 21:
cl = reader.nextString();
break;
case 22:
hidden = reader.nextBoolean();
break;
default:
reader.skipName();
reader.skipValue();
@ -277,11 +246,7 @@ class LayerParser {
// Before the in frame
if (inFrame > 0) {
var preKeyframe = Keyframe<double>(composition,
startValue: 0.0,
endValue: 0.0,
interpolator: null,
startFrame: 0.0,
endFrame: inFrame);
startValue: 0.0, endValue: 0.0, startFrame: 0.0, endFrame: inFrame);
inOutKeyframes.add(preKeyframe);
}
@ -289,7 +254,6 @@ class LayerParser {
var visibleKeyframe = Keyframe<double>(composition,
startValue: 1.0,
endValue: 1.0,
interpolator: null,
startFrame: inFrame,
endFrame: outFrame);
inOutKeyframes.add(visibleKeyframe);
@ -297,7 +261,6 @@ class LayerParser {
var outKeyframe = Keyframe<double>(composition,
startValue: 0.0,
endValue: 0.0,
interpolator: null,
startFrame: outFrame,
endFrame: double.maxFinite);
inOutKeyframes.add(outKeyframe);

View File

@ -33,20 +33,15 @@ class LottieCompositionParser {
while (reader.hasNext()) {
switch (reader.selectName(_names)) {
case 0:
parameters.bounds.width = reader.nextInt().round();
break;
parameters.bounds.width = reader.nextInt();
case 1:
parameters.bounds.height = reader.nextInt().round();
break;
parameters.bounds.height = reader.nextInt();
case 2:
parameters.startFrame = reader.nextDouble();
break;
case 3:
parameters.endFrame = reader.nextDouble() - 0.01;
break;
case 4:
parameters.frameRate = reader.nextDouble();
break;
case 5:
var version = reader.nextString();
var versions = version.split('.');
@ -57,24 +52,18 @@ class LottieCompositionParser {
majorVersion, minorVersion, patchVersion, 4, 4, 0)) {
composition.addWarning('Lottie only supports bodymovin >= 4.4.0');
}
break;
case 6:
_parseLayers(
reader, composition, parameters.layers, parameters.layerMap);
break;
case 7:
_parseAssets(
reader, composition, parameters.precomps, parameters.images);
break;
case 8:
_parseFonts(reader, parameters.fonts);
break;
case 9:
_parseChars(reader, composition, parameters.characters);
break;
case 10:
_parseMarkers(reader, composition, parameters.markers);
break;
default:
reader.skipName();
reader.skipValue();
@ -136,7 +125,6 @@ class LottieCompositionParser {
switch (reader.selectName(_assetsNames)) {
case 0:
id = reader.nextString();
break;
case 1:
reader.beginArray();
while (reader.hasNext()) {
@ -145,19 +133,14 @@ class LottieCompositionParser {
layers.add(layer);
}
reader.endArray();
break;
case 2:
width = reader.nextInt();
break;
case 3:
height = reader.nextInt();
break;
case 4:
imageFileName = reader.nextString();
break;
case 5:
relativeFolder = reader.nextString();
break;
default:
reader.skipName();
reader.skipValue();
@ -192,7 +175,6 @@ class LottieCompositionParser {
fonts[font.name] = font;
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
@ -226,13 +208,10 @@ class LottieCompositionParser {
switch (reader.selectName(_markerNames)) {
case 0:
comment = reader.nextString();
break;
case 1:
frame = reader.nextDouble();
break;
case 2:
durationFrames = reader.nextDouble();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -23,33 +23,25 @@ class MaskParser {
switch (modeName) {
case 'a':
maskMode = MaskMode.maskModeAdd;
break;
case 's':
maskMode = MaskMode.maskModeSubstract;
break;
case 'n':
maskMode = MaskMode.maskModeNone;
break;
case 'i':
composition.addWarning(
'Animation contains intersect masks. They are not supported but will be treated like add masks.');
maskMode = MaskMode.maskModeIntersect;
break;
default:
composition.addWarning(
'Unknown mask mode $modeName. Defaulting to Add.');
maskMode = MaskMode.maskModeAdd;
}
break;
case 'pt':
maskPath = AnimatableValueParser.parseShapeData(reader, composition);
break;
case 'o':
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 'inv':
inverted = reader.nextBoolean();
break;
default:
reader.skipValue();
}

View File

@ -16,13 +16,10 @@ class MergePathsParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
mode = MergePaths.modeForId(reader.nextInt());
break;
case 2:
hidden = reader.nextBoolean();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -1,11 +1,10 @@
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
class Buffer {
Buffer(this.buffer);
final Uint8List buffer;
final List<int> buffer;
int _start = 0, _size = 0;
int get size => _size;

View File

@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:typed_data';
import 'buffer.dart';
import 'json_scope.dart';
import 'json_utf8_reader.dart';
@ -167,7 +166,7 @@ abstract class JsonReader {
bool failOnUnknown = false;
/// Returns a new instance that reads UTF-8 encoded JSON from {@code source}.
static JsonReader fromBytes(Uint8List source) {
static JsonReader fromBytes(List<int> source) {
return JsonUtf8Reader(Buffer(source));
}

View File

@ -43,7 +43,6 @@ class JsonScope {
..write('[')
..write(pathIndices[i])
..write(']');
break;
case emptyObject:
case danglingName:
@ -52,7 +51,6 @@ class JsonScope {
if (pathNames[i] != null) {
result.write(pathNames[i]);
}
break;
case nonEmptyDocument:
case emptyDocument:

View File

@ -200,7 +200,6 @@ class JsonUtf8Reader extends JsonReader {
return _peeked = peekedEndArray;
case $semicolon:
_checkLenient();
break;
case $comma:
break;
default:
@ -218,7 +217,6 @@ class JsonUtf8Reader extends JsonReader {
return _peeked = peekedEndObject;
case $semicolon:
_checkLenient(); // fall-through
break;
case $comma:
break;
default:
@ -262,7 +260,6 @@ class JsonUtf8Reader extends JsonReader {
if (buffer.request(1) && buffer.getByte(0) == $greaterThan) {
buffer.readByte(); // Consume '>'.
}
break;
default:
throw syntaxError("Expected ':'");
}

View File

@ -31,41 +31,30 @@ class PolystarShapeParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
type = PolystarShapeType.forValue(reader.nextInt());
break;
case 2:
points = AnimatableValueParser.parseFloat(reader, composition);
break;
case 3:
position =
AnimatablePathValueParser.parseSplitPath(reader, composition);
break;
case 4:
rotation = AnimatableValueParser.parseFloat(reader, composition);
break;
case 5:
outerRadius = AnimatableValueParser.parseFloat(reader, composition);
break;
case 6:
outerRoundedness =
AnimatableValueParser.parseFloat(reader, composition);
break;
case 7:
innerRadius = AnimatableValueParser.parseFloat(reader, composition);
break;
case 8:
innerRoundedness =
AnimatableValueParser.parseFloat(reader, composition);
break;
case 9:
hidden = reader.nextBoolean();
break;
case 10:
// "d" is 2 for normal and 3 for reversed.
reversed = reader.nextInt() == 3;
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -25,20 +25,15 @@ class RectangleShapeParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
position =
AnimatablePathValueParser.parseSplitPath(reader, composition);
break;
case 2:
size = AnimatableValueParser.parsePoint(reader, composition);
break;
case 3:
roundedness = AnimatableValueParser.parseFloat(reader, composition);
break;
case 4:
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}

View File

@ -23,19 +23,14 @@ class RepeaterParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
copies = AnimatableValueParser.parseFloat(reader, composition);
break;
case 2:
offset = AnimatableValueParser.parseFloat(reader, composition);
break;
case 3:
transform = AnimatableTransformParser.parse(reader, composition);
break;
case 4:
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}

View File

@ -21,18 +21,15 @@ class RoundedCornersParser {
switch (reader.selectName(_names)) {
case 0: //nm
name = reader.nextString();
break;
case 1: // r
cornerRadius = AnimatableValueParser.parseFloat(reader, composition);
break;
case 2: // hd
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}
}
return hidden ? null : RoundedCorners(name!, cornerRadius!);
return hidden ? null : RoundedCorners(name ?? '', cornerRadius!);
}
}

View File

@ -23,16 +23,12 @@ ShapeData shapeDataParser(JsonReader reader) {
switch (reader.selectName(_names)) {
case 0:
closed = reader.nextBoolean();
break;
case 1:
pointsArray = JsonUtils.jsonToPoints(reader);
break;
case 2:
inTangents = JsonUtils.jsonToPoints(reader);
break;
case 3:
outTangents = JsonUtils.jsonToPoints(reader);
break;
default:
reader.skipName();
reader.skipValue();
@ -51,7 +47,7 @@ ShapeData shapeDataParser(JsonReader reader) {
if (pointsArray.isEmpty) {
return ShapeData(<CubicCurveData>[],
initialPoint: const Offset(0, 0), closed: false);
initialPoint: Offset.zero, closed: false);
}
var length = pointsArray.length;

View File

@ -25,22 +25,16 @@ class ShapeFillParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
color = AnimatableValueParser.parseColor(reader, composition);
break;
case 2:
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 3:
fillEnabled = reader.nextBoolean();
break;
case 4:
fillTypeInt = reader.nextInt();
break;
case 5:
hidden = reader.nextBoolean();
break;
default:
reader.skipName();
reader.skipValue();

View File

@ -18,10 +18,8 @@ class ShapeGroupParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
hidden = reader.nextBoolean();
break;
case 2:
reader.beginArray();
while (reader.hasNext()) {
@ -31,7 +29,6 @@ class ShapeGroupParser {
}
}
reader.endArray();
break;
default:
reader.skipValue();
}

View File

@ -20,16 +20,12 @@ class ShapePathParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
ind = reader.nextInt();
break;
case 2:
shape = AnimatableValueParser.parseShapeData(reader, composition);
break;
case 3:
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}

View File

@ -32,28 +32,20 @@ class ShapeStrokeParser {
switch (reader.selectName(_names)) {
case 0:
name = reader.nextString();
break;
case 1:
color = AnimatableValueParser.parseColor(reader, composition);
break;
case 2:
width = AnimatableValueParser.parseFloat(reader, composition);
break;
case 3:
opacity = AnimatableValueParser.parseInteger(reader, composition);
break;
case 4:
capType = LineCapType.values[reader.nextInt() - 1];
break;
case 5:
joinType = LineJoinType.values[reader.nextInt() - 1];
break;
case 6:
miterLimit = reader.nextDouble();
break;
case 7:
hidden = reader.nextBoolean();
break;
case 8:
reader.beginArray();
while (reader.hasNext()) {
@ -65,10 +57,8 @@ class ShapeStrokeParser {
switch (reader.selectName(_dashPatternNames)) {
case 0:
n = reader.nextString();
break;
case 1:
val = AnimatableValueParser.parseFloat(reader, composition);
break;
default:
reader.skipName();
reader.skipValue();
@ -79,12 +69,10 @@ class ShapeStrokeParser {
switch (n) {
case 'o':
offset = val;
break;
case 'd':
case 'g':
composition.hasDashPattern = true;
lineDashPattern.add(val!);
break;
}
}
reader.endArray();
@ -93,7 +81,6 @@ class ShapeStrokeParser {
// If there is only 1 value then it is assumed to be equal parts on and off.
lineDashPattern.add(lineDashPattern.first);
}
break;
default:
reader.skipValue();
}

View File

@ -20,22 +20,16 @@ class ShapeTrimPathParser {
switch (reader.selectName(_names)) {
case 0:
start = AnimatableValueParser.parseFloat(reader, composition);
break;
case 1:
end = AnimatableValueParser.parseFloat(reader, composition);
break;
case 2:
offset = AnimatableValueParser.parseFloat(reader, composition);
break;
case 3:
name = reader.nextString();
break;
case 4:
type = ShapeTrimPath.typeForId(reader.nextInt());
break;
case 5:
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}

View File

@ -8,6 +8,7 @@ import '../lottie_image_asset.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
@immutable
class AssetLottie extends LottieProvider {
AssetLottie(
this.assetName, {

View File

@ -1,4 +1,5 @@
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;
import '../composition.dart';
import '../lottie_image_asset.dart';
@ -6,6 +7,7 @@ import 'load_image.dart';
import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as io;
@immutable
class FileLottie extends LottieProvider {
FileLottie(this.file, {super.imageProviderFactory});

View File

@ -1,5 +1,5 @@
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:path/path.dart' as p;
import '../composition.dart';
@ -7,6 +7,7 @@ import '../lottie_image_asset.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
@immutable
class MemoryLottie extends LottieProvider {
MemoryLottie(this.bytes, {super.imageProviderFactory});

View File

@ -8,6 +8,7 @@ import 'load_image.dart';
import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as network;
@immutable
class NetworkLottie extends LottieProvider {
NetworkLottie(this.url, {this.headers, super.imageProviderFactory});

View File

@ -86,6 +86,7 @@ class RawLottie extends LeafRenderObjectWidget {
composition: composition,
delegates: delegates,
enableMergePaths: options?.enableMergePaths,
enableApplyingOpacityToLayers: options?.enableApplyingOpacityToLayers,
progress: progress,
frameRate: frameRate,
width: width,
@ -105,6 +106,7 @@ class RawLottie extends LeafRenderObjectWidget {
frameRate: frameRate,
delegates: delegates,
enableMergePaths: options?.enableMergePaths,
enableApplyingOpacityToLayers: options?.enableApplyingOpacityToLayers,
filterQuality: filterQuality,
)
..width = width

View File

@ -13,6 +13,7 @@ class RenderLottie extends RenderBox {
required LottieComposition? composition,
LottieDelegates? delegates,
bool? enableMergePaths,
bool? enableApplyingOpacityToLayers,
double progress = 0.0,
FrameRate? frameRate,
double? width,
@ -26,6 +27,8 @@ class RenderLottie extends RenderBox {
..setProgress(progress, frameRate: frameRate)
..delegates = delegates
..enableMergePaths = enableMergePaths ?? false
..isApplyingOpacityToLayersEnabled =
enableApplyingOpacityToLayers ?? false
..filterQuality = filterQuality)
: null,
_width = width,
@ -42,9 +45,11 @@ class RenderLottie extends RenderBox {
required FrameRate? frameRate,
required LottieDelegates? delegates,
bool? enableMergePaths,
bool? enableApplyingOpacityToLayers,
FilterQuality? filterQuality}) {
var drawable = _drawable;
enableMergePaths ??= false;
enableApplyingOpacityToLayers ??= false;
var needsLayout = false;
var needsPaint = false;
@ -71,6 +76,12 @@ class RenderLottie extends RenderBox {
drawable.enableMergePaths = enableMergePaths;
needsPaint = true;
}
if (enableApplyingOpacityToLayers !=
drawable.isApplyingOpacityToLayersEnabled) {
drawable.isApplyingOpacityToLayersEnabled =
enableApplyingOpacityToLayers;
needsPaint = true;
}
if (filterQuality != drawable.filterQuality) {
drawable.filterQuality = filterQuality;
needsPaint = true;
@ -91,6 +102,7 @@ class RenderLottie extends RenderBox {
/// aspect ratio.
double? get width => _width;
double? _width;
set width(double? value) {
if (value == _width) {
return;
@ -105,6 +117,7 @@ class RenderLottie extends RenderBox {
/// aspect ratio.
double? get height => _height;
double? _height;
set height(double? value) {
if (value == _height) {
return;
@ -116,6 +129,7 @@ class RenderLottie extends RenderBox {
/// How to inscribe the composition into the space allocated during layout.
BoxFit? get fit => _fit;
BoxFit? _fit;
set fit(BoxFit? value) {
if (value == _fit) {
return;
@ -130,6 +144,7 @@ class RenderLottie extends RenderBox {
/// not be null.
AlignmentGeometry get alignment => _alignment;
AlignmentGeometry _alignment;
set alignment(AlignmentGeometry value) {
if (value == _alignment) {
return;

View File

@ -1,189 +0,0 @@
import 'dart:ui';
import 'package:flutter/foundation.dart';
class DebugPath extends Path {
void _log(String methodName) {
debugPrint('Path.$methodName');
}
@override
void addArc(Rect oval, double startAngle, double sweepAngle) {
_log('addArc');
super.addArc(oval, startAngle, sweepAngle);
}
@override
void addOval(Rect oval) {
_log('addOval');
super.addOval(oval);
}
@override
void addPath(Path path, Offset offset, {Float64List? matrix4}) {
_log('addPath');
super.addPath(path, offset, matrix4: matrix4);
}
@override
void addPolygon(List<Offset> points, bool close) {
_log('addPolygon');
super.addPolygon(points, close);
}
@override
void addRRect(RRect rrect) {
_log('addRRect');
super.addRRect(rrect);
}
@override
void addRect(Rect rect) {
_log('addRect');
super.addRect(rect);
}
@override
void arcTo(
Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
_log('arcTo');
super.arcTo(rect, startAngle, sweepAngle, forceMoveTo);
}
@override
void arcToPoint(Offset arcEnd,
{Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true}) {
_log('arcToPoint');
super.arcToPoint(arcEnd,
radius: radius,
rotation: rotation,
largeArc: largeArc,
clockwise: clockwise);
}
@override
void close() {
_log('close');
super.close();
}
@override
PathMetrics computeMetrics({bool forceClosed = false}) {
_log('computeMetrics');
return super.computeMetrics();
}
@override
void conicTo(double x1, double y1, double x2, double y2, double w) {
_log('conicTo');
super.conicTo(x1, y1, x2, y2, w);
}
@override
bool contains(Offset point) {
_log('contains');
return super.contains(point);
}
@override
void cubicTo(
double x1, double y1, double x2, double y2, double x3, double y3) {
_log('cubicTo');
super.cubicTo(x1, y1, x2, y2, x3, y3);
}
@override
void extendWithPath(Path path, Offset offset, {Float64List? matrix4}) {
_log('extendWithPath');
super.extendWithPath(path, offset, matrix4: matrix4);
}
@override
Rect getBounds() {
_log('getBounds');
return super.getBounds();
}
@override
void lineTo(double x, double y) {
_log('lineTo');
super.lineTo(x, y);
}
@override
void moveTo(double x, double y) {
_log('moveTo');
super.moveTo(x, y);
}
@override
void quadraticBezierTo(double x1, double y1, double x2, double y2) {
_log('quadraticBezierTo');
super.quadraticBezierTo(x1, y1, x2, y2);
}
@override
void relativeArcToPoint(Offset arcEndDelta,
{Radius radius = Radius.zero,
double rotation = 0.0,
bool largeArc = false,
bool clockwise = true}) {
_log('relativeArcToPoint');
super.relativeArcToPoint(arcEndDelta,
radius: radius,
rotation: rotation,
largeArc: largeArc,
clockwise: clockwise);
}
@override
void relativeConicTo(double x1, double y1, double x2, double y2, double w) {
_log('relativeConicTo');
super.relativeConicTo(x1, y1, x2, y2, w);
}
@override
void relativeCubicTo(
double x1, double y1, double x2, double y2, double x3, double y3) {
_log('relativeCubicTo');
super.relativeCubicTo(x1, y1, x2, y2, x3, y3);
}
@override
void relativeLineTo(double dx, double dy) {
_log('relativeLineTo');
super.relativeLineTo(dx, dy);
}
@override
void relativeMoveTo(double dx, double dy) {
_log('relativeMoveTo');
super.relativeMoveTo(dx, dy);
}
@override
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) {
_log('relativeQuadraticBezierTo');
super.relativeQuadraticBezierTo(x1, y1, x2, y2);
}
@override
void reset() {
_log('reset');
super.reset();
}
@override
Path shift(Offset offset) {
_log('shift');
return super.shift(offset);
}
@override
Path transform(Float64List matrix4) {
_log('transform');
return super.transform(matrix4);
}
}

View File

@ -13,7 +13,7 @@ class GammaEvaluator {
// IEC 61966-2-1:1999
return linear <= 0.0031308
? linear * 12.92
: ((pow(linear, 1.0 / 2.4) * 1.055) - 0.055).toDouble();
: ((pow(linear, 1.0 / 2.4) * 1.055) - 0.055);
}
// Electro-optical conversion function for the sRGB color space

View File

@ -104,16 +104,13 @@ class Utils {
newStart -= length;
}
var tempPath =
pathMeasure.extractPath(newStart, newEnd, startWithMoveTo: true);
var tempPath = pathMeasure.extractPath(newStart, newEnd);
if (newEnd > length) {
var tempPath2 =
pathMeasure.extractPath(0, newEnd % length, startWithMoveTo: true);
var tempPath2 = pathMeasure.extractPath(0, newEnd % length);
tempPath.addPath(tempPath2, Offset.zero);
} else if (newStart < 0) {
var tempPath2 = pathMeasure.extractPath(length + newStart, length,
startWithMoveTo: true);
var tempPath2 = pathMeasure.extractPath(length + newStart, length);
tempPath.addPath(tempPath2, Offset.zero);
}
path.set(tempPath);

View File

@ -1,5 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
@immutable
class DropShadow {
final Color color;
final double direction;

View File

@ -5,34 +5,34 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: a36ec4843dc30ea6bf652bf25e3448db6c5e8bcf4aa55f063a5d1dad216d8214
sha256: "58826e40314219b223f4723dd4205845040161cdc2df3e6a1cdceed5d8165084"
url: "https://pub.dev"
source: hosted
version: "58.0.0"
version: "63.0.0"
analyzer:
dependency: "direct dev"
description:
name: analyzer
sha256: cc4242565347e98424ce9945c819c192ec0838cb9d1f6aa4a97cc96becbc5b27
sha256: f85566ec7b3d25cbea60f7dd4f157c5025f2f19233ca4feeed33b616c78a26a3
url: "https://pub.dev"
source: hosted
version: "5.10.0"
version: "6.1.0"
archive:
dependency: "direct main"
description:
name: archive
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
url: "https://pub.dev"
source: hosted
version: "3.3.6"
version: "3.3.7"
args:
dependency: transitive
description:
name: args
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.2"
async:
dependency: transitive
description:
@ -85,18 +85,18 @@ packages:
dependency: transitive
description:
name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
dart_style:
dependency: "direct dev"
description:
name: dart_style
sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352"
sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.3.2"
fake_async:
dependency: transitive
description:
@ -109,10 +109,10 @@ packages:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -122,10 +122,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.0.2"
flutter_test:
dependency: "direct dev"
description: flutter
@ -135,10 +135,10 @@ packages:
dependency: transitive
description:
name: glob
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
js:
dependency: transitive
description:
@ -151,10 +151,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "2.1.1"
matcher:
dependency: transitive
description:
@ -199,18 +199,18 @@ packages:
dependency: transitive
description:
name: pointycastle
sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.2"
version: "3.7.3"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
version: "2.1.4"
sky_engine:
dependency: transitive
description: flutter
@ -268,10 +268,10 @@ packages:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
vector_math:
dependency: "direct main"
description:
@ -284,18 +284,18 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
version: "1.1.0"
yaml:
dependency: "direct dev"
description:
name: yaml
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.2"
sdks:
dart: ">=3.0.0-0 <4.0.0"
flutter: ">=3.3.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"

View File

@ -1,11 +1,11 @@
name: lottie
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
version: 2.3.2
version: 2.6.0
repository: https://github.com/xvrh/lottie-flutter
environment:
sdk: '>=2.18.0 <3.0.0'
flutter: '>=3.3.0'
sdk: '^3.0.0'
flutter: '>=3.10.0'
dependencies:
archive: ^3.0.0

View File

@ -9,8 +9,8 @@ import 'utils.dart';
void main() {
testWidgets('Can specify ImageProvider with zip file ', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var callCount = 0;
ImageProvider imageProviderFactory(LottieImageAsset image) {
@ -32,8 +32,8 @@ void main() {
testWidgets('Can specify image delegate', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var image = await tester.runAsync(() async =>
loadImage(FileImage(File('example/assets/Images/WeAccept/img_0.png'))));

View File

@ -276,7 +276,7 @@ void main() {
testGolden(
'Null Color Filter',
ValueDelegate.colorFilter(['**'], value: null),
ValueDelegate.colorFilter(['**']),
);
testGolden(
@ -320,8 +320,8 @@ void main() {
testWidgets('warningShimmer', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var composition = await LottieComposition.fromBytes(
File('test/data/warningShimmer.json').readAsBytesSync());

View File

@ -10,8 +10,8 @@ void main() {
testWidgets('Mirror animation', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var composition = (await tester.runAsync(() =>
FileLottie(File('$root/Tests/MatteTimeStretchScan.json')).load()))!;

View File

@ -9,8 +9,8 @@ import 'utils.dart';
void main() {
testWidgets('Animations with stroke', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var composition = await LottieComposition.fromBytes(
File('example/assets/17297-fireworks.json').readAsBytesSync());

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -14,8 +14,8 @@ void main() {
.where((f) => const ['.json', '.zip'].contains(p.extension(f.path)))) {
testWidgets('Goldens ${asset.path}', (tester) async {
var size = const Size(500, 400);
tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0;
tester.view.physicalSize = size;
tester.view.devicePixelRatio = 1.0;
var composition =
(await tester.runAsync(() => FileLottie(asset).load()))!;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Some files were not shown because too many files have changed in this diff Show More