Compare commits

...

2 Commits

Author SHA1 Message Date
8dcb052fe1 Rework the cache so animation can be loaded without flickering 1 frame (#246)
Now, the cache can return a `SynchronousFuture` when the composition is
already available.
2022-12-14 14:06:34 +01:00
a333a42f01 Enable use_super_parameters lint (#242) 2022-11-10 14:14:28 +01:00
62 changed files with 390 additions and 190 deletions

View File

@ -1,3 +1,18 @@
## [2.1.0]
- Improve the cache to ensure that there is not an empty frame each time we load an animation.
The method `AssetLottie('anim.json').load()` returns a `SynchronousFuture` if it has been loaded previously.
- Expose the `LottieCache` singleton.
It allows to change the cache behaviour and clear the entries.
```dart
void main() {
Lottie.cache.maximumSize = 10;
Lottie.cache.clear();
Lottie.cache.evict(NetworkLottie('https://lottie.com/anim.json'));
}
```
## [2.0.0] ## [2.0.0]
- **Breaking change**: the lottie widget will be smaller if it relies on the intrinsic size of the composition. - **Breaking change**: the lottie widget will be smaller if it relies on the intrinsic size of the composition.

View File

@ -23,7 +23,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -61,7 +61,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
@ -136,7 +136,7 @@ This example shows how to load and parse a Lottie composition from a json file.
```dart ```dart
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
State<MyWidget> createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();
@ -182,7 +182,7 @@ a specific position and size.
class CustomDrawer extends StatelessWidget { class CustomDrawer extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const CustomDrawer(this.composition, {Key? key}) : super(key: key); const CustomDrawer(this.composition, {super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -10,14 +10,16 @@ linter:
always_declare_return_types: true always_declare_return_types: true
avoid_dynamic_calls: true avoid_dynamic_calls: true
avoid_escaping_inner_quotes: true avoid_escaping_inner_quotes: true
avoid_returning_null_for_future: true
avoid_setters_without_getters: true avoid_setters_without_getters: true
cancel_subscriptions: true
cast_nullable_to_non_nullable: true cast_nullable_to_non_nullable: true
close_sinks: true close_sinks: true
no_adjacent_strings_in_list: true no_adjacent_strings_in_list: true
no_default_cases: true no_default_cases: true
omit_local_variable_types: true omit_local_variable_types: true
only_throw_errors: true only_throw_errors: true
prefer_interpolation_to_compose_strings: true prefer_relative_imports: true
prefer_single_quotes: true prefer_single_quotes: true
sort_child_properties_last: true sort_child_properties_last: true
sort_pub_dependencies: true sort_pub_dependencies: true
@ -27,3 +29,4 @@ linter:
unnecessary_statements: true unnecessary_statements: true
unsafe_html: true unsafe_html: true
use_raw_strings: true use_raw_strings: true
use_super_parameters: true

View File

@ -9,7 +9,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -5,7 +5,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const App()); void main() => runApp(const App());
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();

View File

@ -12,7 +12,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: _Page()),
);
}
}
class _Page extends StatefulWidget {
@override
__PageState createState() => __PageState();
}
class __PageState extends State<_Page> {
var _animationKey = UniqueKey();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Lottie.network(
'https://assets10.lottiefiles.com/datafiles/QeC7XD39x4C1CIj/data.json',
key: _animationKey,
fit: BoxFit.contain,
width: 200,
height: 200,
),
ElevatedButton(
onPressed: () {
Lottie.cache.clear();
Lottie.cache.maximumSize = 10;
},
child: const Text('Clear cache'),
),
ElevatedButton(
onPressed: () {
setState(() {
_animationKey = UniqueKey();
});
},
child: const Text('Recreate animation'),
),
],
);
}
}

View File

@ -5,7 +5,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
} }
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
State<MyWidget> createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();
@ -60,7 +60,7 @@ class _MyWidgetState extends State<MyWidget> {
class CustomDrawer extends StatelessWidget { class CustomDrawer extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const CustomDrawer(this.composition, {Key? key}) : super(key: key); const CustomDrawer(this.composition, {super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -5,7 +5,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,7 +19,7 @@ class MyApp extends StatelessWidget {
//--- example //--- example
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
State<MyWidget> createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -7,7 +7,7 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();

View File

@ -7,7 +7,7 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();

View File

@ -9,7 +9,7 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();
@ -48,7 +48,7 @@ class _AppState extends State<App> with TickerProviderStateMixin {
class _LottieDetails extends StatefulWidget { class _LottieDetails extends StatefulWidget {
final LottieComposition composition; final LottieComposition composition;
const _LottieDetails(this.composition, {Key? key}) : super(key: key); const _LottieDetails(this.composition);
@override @override
_LottieDetailsState createState() => _LottieDetailsState(); _LottieDetailsState createState() => _LottieDetailsState();

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();

View File

@ -12,7 +12,7 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();

View File

@ -10,7 +10,7 @@ import 'package:path_provider/path_provider.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
State<MyApp> createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -14,7 +14,7 @@ void main() {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -64,7 +64,7 @@ class App extends StatelessWidget {
class _Item extends StatelessWidget { class _Item extends StatelessWidget {
final Widget child; final Widget child;
const _Item({Key? key, required this.child}) : super(key: key); const _Item({required this.child});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -89,7 +89,7 @@ class _Item extends StatelessWidget {
class Detail extends StatefulWidget { class Detail extends StatefulWidget {
final String assetName; final String assetName;
const Detail(this.assetName, {Key? key}) : super(key: key); const Detail(this.assetName, {super.key});
@override @override
State<Detail> createState() => _DetailState(); State<Detail> createState() => _DetailState();

View File

@ -13,7 +13,7 @@ void main() async {
class App extends StatelessWidget { class App extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const App({Key? key, required this.composition}) : super(key: key); const App({super.key, required this.composition});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -103,8 +103,7 @@ class _Lottie extends StatefulWidget {
final AlignmentGeometry? alignment; final AlignmentGeometry? alignment;
const _Lottie(this.composition, const _Lottie(this.composition,
{Key? key, this.width, this.height, this.fit, this.alignment}) {this.width, this.height, this.fit, this.alignment});
: super(key: key);
@override @override
__LottieState createState() => __LottieState(); __LottieState createState() => __LottieState();

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -7,7 +7,7 @@ packages:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.3.2" version: "3.3.5"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -43,6 +43,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -101,7 +108,7 @@ packages:
name: golden_toolkit name: golden_toolkit
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.0" version: "0.14.0"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
@ -116,6 +123,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.5"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -136,7 +150,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "2.0.0" version: "2.1.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -178,7 +192,7 @@ packages:
name: path_provider_android name: path_provider_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.21" version: "2.0.22"
path_provider_ios: path_provider_ios:
dependency: transitive dependency: transitive
description: description:
@ -228,6 +242,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -302,7 +323,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.1.2"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -311,5 +332,5 @@ packages:
source: hosted source: hosted
version: "0.2.0+2" version: "0.2.0+2"
sdks: sdks:
dart: ">=2.18.0 <3.0.0" dart: ">=2.18.4 <3.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

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

View File

@ -4,7 +4,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class ColorKeyframeAnimation extends KeyframeAnimation<Color> { class ColorKeyframeAnimation extends KeyframeAnimation<Color> {
ColorKeyframeAnimation(List<Keyframe<Color>> keyframes) : super(keyframes); ColorKeyframeAnimation(super.keyframes);
@override @override
Color getValue(Keyframe<Color> keyframe, double keyframeProgress) { Color getValue(Keyframe<Color> keyframe, double keyframeProgress) {

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class DoubleKeyframeAnimation extends KeyframeAnimation<double> { class DoubleKeyframeAnimation extends KeyframeAnimation<double> {
DoubleKeyframeAnimation(List<Keyframe<double>> keyframes) : super(keyframes); DoubleKeyframeAnimation(super.keyframes);
@override @override
double getValue(Keyframe<double> keyframe, double keyframeProgress) { double getValue(Keyframe<double> keyframe, double keyframeProgress) {

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class IntegerKeyframeAnimation extends KeyframeAnimation<int> { class IntegerKeyframeAnimation extends KeyframeAnimation<int> {
IntegerKeyframeAnimation(List<Keyframe<int>> keyframes) : super(keyframes); IntegerKeyframeAnimation(super.keyframes);
@override @override
int getValue(Keyframe<int> keyframe, double keyframeProgress) { int getValue(Keyframe<int> keyframe, double keyframeProgress) {

View File

@ -1,7 +1,6 @@
import '../../value/keyframe.dart';
import 'base_keyframe_animation.dart'; import 'base_keyframe_animation.dart';
abstract class KeyframeAnimation<T extends Object> abstract class KeyframeAnimation<T extends Object>
extends BaseKeyframeAnimation<T, T> { extends BaseKeyframeAnimation<T, T> {
KeyframeAnimation(List<Keyframe<T>> keyframes) : super(keyframes); KeyframeAnimation(super.keyframes);
} }

View File

@ -7,9 +7,9 @@ class PathKeyframe extends Keyframe<Offset> {
Path? _path; Path? _path;
final Keyframe<Offset> _pointKeyFrame; final Keyframe<Offset> _pointKeyFrame;
PathKeyframe(LottieComposition composition, Keyframe<Offset> keyframe) PathKeyframe(LottieComposition super.composition, Keyframe<Offset> keyframe)
: _pointKeyFrame = keyframe, : _pointKeyFrame = keyframe,
super(composition, super(
startValue: keyframe.startValue, startValue: keyframe.startValue,
endValue: keyframe.endValue, endValue: keyframe.endValue,
interpolator: keyframe.interpolator, interpolator: keyframe.interpolator,

View File

@ -7,7 +7,7 @@ class PathKeyframeAnimation extends KeyframeAnimation<Offset> {
PathKeyframe? _pathMeasureKeyframe; PathKeyframe? _pathMeasureKeyframe;
late PathMetric _pathMeasure; late PathMetric _pathMeasure;
PathKeyframeAnimation(List<Keyframe<Offset>> keyframes) : super(keyframes); PathKeyframeAnimation(super.keyframes);
@override @override
Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) { Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) {

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class PointKeyframeAnimation extends KeyframeAnimation<Offset> { class PointKeyframeAnimation extends KeyframeAnimation<Offset> {
PointKeyframeAnimation(List<Keyframe<Offset>> keyframes) : super(keyframes); PointKeyframeAnimation(super.keyframes);
@override @override
Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) { Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) {

View File

@ -11,8 +11,7 @@ class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
final Path _tempPath = PathFactory.create(); final Path _tempPath = PathFactory.create();
List<ShapeModifierContent>? _shapeModifiers; List<ShapeModifierContent>? _shapeModifiers;
ShapeKeyframeAnimation(List<Keyframe<ShapeData>> keyframes) ShapeKeyframeAnimation(super.keyframes);
: super(keyframes);
@override @override
Path getValue(Keyframe<ShapeData> keyframe, double keyframeProgress) { Path getValue(Keyframe<ShapeData> keyframe, double keyframeProgress) {

View File

@ -5,8 +5,7 @@ import '../../value/lottie_value_callback.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> { class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> {
TextKeyframeAnimation(List<Keyframe<DocumentData>> keyframes) TextKeyframeAnimation(super.keyframes);
: super(keyframes);
@override @override
DocumentData getValue( DocumentData getValue(

View File

@ -4,6 +4,7 @@ import '../lottie.dart';
import 'composition.dart'; import 'composition.dart';
import 'l.dart'; import 'l.dart';
import 'lottie_builder.dart'; import 'lottie_builder.dart';
import 'providers/lottie_provider.dart';
/// A widget to display a loaded [LottieComposition]. /// A widget to display a loaded [LottieComposition].
/// The [controller] property allows to specify a custom AnimationController that /// The [controller] property allows to specify a custom AnimationController that
@ -11,8 +12,11 @@ import 'lottie_builder.dart';
/// automatically and the behavior could be adjusted with the properties [animate], /// automatically and the behavior could be adjusted with the properties [animate],
/// [repeat] and [reverse]. /// [repeat] and [reverse].
class Lottie extends StatefulWidget { class Lottie extends StatefulWidget {
/// The cache instance for recently loaded Lottie compositions.
static LottieCache get cache => sharedLottieCache;
const Lottie({ const Lottie({
Key? key, super.key,
required this.composition, required this.composition,
this.controller, this.controller,
this.width, this.width,
@ -30,8 +34,7 @@ class Lottie extends StatefulWidget {
}) : animate = animate ?? true, }) : animate = animate ?? true,
reverse = reverse ?? false, reverse = reverse ?? false,
repeat = repeat ?? true, repeat = repeat ?? true,
addRepaintBoundary = addRepaintBoundary ?? true, addRepaintBoundary = addRepaintBoundary ?? true;
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle]. /// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
static LottieBuilder asset( static LottieBuilder asset(

View File

@ -41,7 +41,7 @@ typedef LottieErrorWidgetBuilder = Widget Function(
/// ///
class LottieBuilder extends StatefulWidget { class LottieBuilder extends StatefulWidget {
const LottieBuilder({ const LottieBuilder({
Key? key, super.key,
required this.lottie, required this.lottie,
this.controller, this.controller,
this.frameRate, this.frameRate,
@ -60,7 +60,7 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality, this.filterQuality,
this.onWarning, this.onWarning,
}) : super(key: key); });
/// Creates a widget that displays an [LottieComposition] obtained from the network. /// Creates a widget that displays an [LottieComposition] obtained from the network.
LottieBuilder.network( LottieBuilder.network(
@ -75,7 +75,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
this.width, this.width,
@ -86,8 +86,7 @@ class LottieBuilder extends StatefulWidget {
this.filterQuality, this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = NetworkLottie(src, }) : lottie = NetworkLottie(src,
headers: headers, imageProviderFactory: imageProviderFactory), headers: headers, imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from a [File]. /// Creates a widget that displays an [LottieComposition] obtained from a [File].
/// ///
@ -110,7 +109,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
this.width, this.width,
@ -120,8 +119,7 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality, this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory), }) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle]. /// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
LottieBuilder.asset( LottieBuilder.asset(
@ -135,7 +133,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
AssetBundle? bundle, AssetBundle? bundle,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
@ -150,8 +148,7 @@ class LottieBuilder extends StatefulWidget {
}) : lottie = AssetLottie(name, }) : lottie = AssetLottie(name,
bundle: bundle, bundle: bundle,
package: package, package: package,
imageProviderFactory: imageProviderFactory), imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List]. /// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
LottieBuilder.memory( LottieBuilder.memory(
@ -166,7 +163,7 @@ class LottieBuilder extends StatefulWidget {
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
this.errorBuilder, this.errorBuilder,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.width, this.width,
this.height, this.height,
@ -175,9 +172,7 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality, this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = }) : lottie = MemoryLottie(bytes, imageProviderFactory: imageProviderFactory);
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
super(key: key);
/// The lottie animation to load. /// The lottie animation to load.
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie] /// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]

View File

@ -1,11 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/color_keyframe_animation.dart'; import '../../animation/keyframe/color_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableColorValue extends BaseAnimatableValue<Color, Color> { class AnimatableColorValue extends BaseAnimatableValue<Color, Color> {
AnimatableColorValue.fromKeyframes(List<Keyframe<Color>> keyframes) AnimatableColorValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
ColorKeyframeAnimation createAnimation() { ColorKeyframeAnimation createAnimation() {

View File

@ -1,12 +1,10 @@
import '../../animation/keyframe/double_keyframe_animation.dart'; import '../../animation/keyframe/double_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableDoubleValue extends BaseAnimatableValue<double, double> { class AnimatableDoubleValue extends BaseAnimatableValue<double, double> {
AnimatableDoubleValue() : super.fromValue(0.0); AnimatableDoubleValue() : super.fromValue(0.0);
AnimatableDoubleValue.fromKeyframes(List<Keyframe<double>> keyframes) AnimatableDoubleValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
DoubleKeyframeAnimation createAnimation() { DoubleKeyframeAnimation createAnimation() {

View File

@ -1,13 +1,11 @@
import '../../animation/keyframe/gradient_color_keyframe_animation.dart'; import '../../animation/keyframe/gradient_color_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../content/gradient_color.dart'; import '../content/gradient_color.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableGradientColorValue class AnimatableGradientColorValue
extends BaseAnimatableValue<GradientColor, GradientColor> { extends BaseAnimatableValue<GradientColor, GradientColor> {
AnimatableGradientColorValue.fromKeyframes( AnimatableGradientColorValue.fromKeyframes(super.keyframes)
List<Keyframe<GradientColor>> keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
GradientColorKeyframeAnimation createAnimation() { GradientColorKeyframeAnimation createAnimation() {

View File

@ -1,13 +1,11 @@
import '../../animation/keyframe/base_keyframe_animation.dart'; import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/integer_keyframe_animation.dart'; import '../../animation/keyframe/integer_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableIntegerValue extends BaseAnimatableValue<int, int> { class AnimatableIntegerValue extends BaseAnimatableValue<int, int> {
AnimatableIntegerValue() : super.fromValue(100); AnimatableIntegerValue() : super.fromValue(100);
AnimatableIntegerValue.fromKeyframes(List<Keyframe<int>> keyframes) AnimatableIntegerValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
BaseKeyframeAnimation<int, int> createAnimation() { BaseKeyframeAnimation<int, int> createAnimation() {

View File

@ -1,11 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/point_keyframe_animation.dart'; import '../../animation/keyframe/point_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatablePointValue extends BaseAnimatableValue<Offset, Offset> { class AnimatablePointValue extends BaseAnimatableValue<Offset, Offset> {
AnimatablePointValue.fromKeyframes(List<Keyframe<Offset>> keyframes) AnimatablePointValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
PointKeyframeAnimation createAnimation() { PointKeyframeAnimation createAnimation() {

View File

@ -1,16 +1,14 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/base_keyframe_animation.dart'; import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/point_keyframe_animation.dart'; import '../../animation/keyframe/point_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> { class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> {
AnimatableScaleValue.one() : this(const Offset(1, 1)); AnimatableScaleValue.one() : this(const Offset(1, 1));
AnimatableScaleValue(Offset value) : super.fromValue(value); AnimatableScaleValue(super.value) : super.fromValue();
AnimatableScaleValue.fromKeyframes(List<Keyframe<Offset>> keyframes) AnimatableScaleValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
BaseKeyframeAnimation<Offset, Offset> createAnimation() { BaseKeyframeAnimation<Offset, Offset> createAnimation() {

View File

@ -1,12 +1,10 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/shape_keyframe_animation.dart'; import '../../animation/keyframe/shape_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../content/shape_data.dart'; import '../content/shape_data.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableShapeValue extends BaseAnimatableValue<ShapeData, Path> { class AnimatableShapeValue extends BaseAnimatableValue<ShapeData, Path> {
AnimatableShapeValue.fromKeyframes(List<Keyframe<ShapeData>> keyframes) AnimatableShapeValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
ShapeKeyframeAnimation createAnimation() { ShapeKeyframeAnimation createAnimation() {

View File

@ -1,12 +1,10 @@
import '../../animation/keyframe/text_keyframe_animation.dart'; import '../../animation/keyframe/text_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../document_data.dart'; import '../document_data.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableTextFrame class AnimatableTextFrame
extends BaseAnimatableValue<DocumentData, DocumentData> { extends BaseAnimatableValue<DocumentData, DocumentData> {
AnimatableTextFrame.fromKeyframes(List<Keyframe<DocumentData>> keyframes) AnimatableTextFrame.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
TextKeyframeAnimation createAnimation() { TextKeyframeAnimation createAnimation() {

View File

@ -2,19 +2,16 @@ import 'dart:ui';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import '../../animation/keyframe/base_keyframe_animation.dart'; import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/value_callback_keyframe_animation.dart'; import '../../animation/keyframe/value_callback_keyframe_animation.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart'; import '../../lottie_property.dart';
import '../../utils.dart'; import '../../utils.dart';
import '../../value/lottie_value_callback.dart'; import '../../value/lottie_value_callback.dart';
import 'base_layer.dart'; import 'base_layer.dart';
import 'layer.dart';
class ImageLayer extends BaseLayer { class ImageLayer extends BaseLayer {
final Paint paint = Paint(); final Paint paint = Paint();
BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation; BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation;
ImageLayer(LottieDrawable lottieDrawable, Layer layerModel) ImageLayer(super.lottieDrawable, super.layerModel);
: super(lottieDrawable, layerModel);
@override @override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix, void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,

View File

@ -1,12 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import '../../lottie_drawable.dart';
import 'base_layer.dart'; import 'base_layer.dart';
import 'layer.dart';
class NullLayer extends BaseLayer { class NullLayer extends BaseLayer {
NullLayer(LottieDrawable lottieDrawable, Layer layerModel) NullLayer(super.lottieDrawable, super.layerModel);
: super(lottieDrawable, layerModel);
@override @override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix, void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,

View File

@ -13,8 +13,8 @@ class AssetLottie extends LottieProvider {
this.assetName, { this.assetName, {
this.bundle, this.bundle,
this.package, this.package,
LottieImageProviderFactory? imageProviderFactory, super.imageProviderFactory,
}) : super(imageProviderFactory: imageProviderFactory); });
final String assetName; final String assetName;
String get keyName => String get keyName =>
@ -25,9 +25,8 @@ class AssetLottie extends LottieProvider {
final String? package; final String? package;
@override @override
Future<LottieComposition> load() async { Future<LottieComposition> load() {
var cacheKey = 'asset-$keyName-$bundle'; return sharedLottieCache.putIfAbsent(this, () async {
return sharedLottieCache.putIfAbsent(cacheKey, () async {
final chosenBundle = bundle ?? rootBundle; final chosenBundle = bundle ?? rootBundle;
var data = await chosenBundle.load(keyName); var data = await chosenBundle.load(keyName);

View File

@ -7,15 +7,13 @@ import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as io; import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as io;
class FileLottie extends LottieProvider { class FileLottie extends LottieProvider {
FileLottie(this.file, {LottieImageProviderFactory? imageProviderFactory}) FileLottie(this.file, {super.imageProviderFactory});
: super(imageProviderFactory: imageProviderFactory);
final Object /*io.File|html.File*/ file; final Object /*io.File|html.File*/ file;
@override @override
Future<LottieComposition> load() async { Future<LottieComposition> load() {
var cacheKey = 'file-${io.filePath(file)}'; return sharedLottieCache.putIfAbsent(this, () async {
return sharedLottieCache.putIfAbsent(cacheKey, () async {
var bytes = await io.loadFile(file); var bytes = await io.loadFile(file);
var composition = await LottieComposition.fromBytes(bytes, var composition = await LottieComposition.fromBytes(bytes,
name: p.basenameWithoutExtension(io.filePath(file)), name: p.basenameWithoutExtension(io.filePath(file)),

View File

@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import '../../lottie.dart'; import '../../lottie.dart';
import 'load_image.dart'; import 'load_image.dart';
@ -19,37 +20,103 @@ abstract class LottieProvider {
} }
class LottieCache { class LottieCache {
final int maximumSize; final Map<Object, Future<LottieComposition>> _pending =
final _cache = <String, Future<LottieComposition>>{}; <Object, Future<LottieComposition>>{};
final Map<Object, LottieComposition> _cache = <Object, LottieComposition>{};
LottieCache({int? maximumSize}) : maximumSize = maximumSize ?? 1000; /// Maximum number of entries to store in the cache.
///
/// Once this many entries have been cached, the least-recently-used entry is
/// evicted when adding a new entry.
int get maximumSize => _maximumSize;
int _maximumSize = 1000;
Future<LottieComposition> putIfAbsent( /// Changes the maximum cache size.
String key, Future<LottieComposition> Function() load) { ///
var composition = _cache[key]; /// If the new size is smaller than the current number of elements, the
if (composition != null) { /// extraneous elements are evicted immediately. Setting this to zero and then
// Remove it so that we add it in front of the cache to prevent evicted /// returning it to its original value will therefore immediately clear the
_cache.remove(key); /// cache.
set maximumSize(int value) {
assert(value >= 0);
if (value == maximumSize) {
return;
}
_maximumSize = value;
if (maximumSize == 0) {
clear();
} else { } else {
composition = load();
}
_cache[key] = composition;
_checkCacheSize();
return composition;
}
void _checkCacheSize() {
while (_cache.length > maximumSize) { while (_cache.length > maximumSize) {
_cache.remove(_cache.keys.first); _cache.remove(_cache.keys.first);
} }
} }
}
/// Evicts all entries from the cache.
///
/// This is useful if, for instance, the root asset bundle has been updated
/// and therefore new images must be obtained.
void clear() { void clear() {
_cache.clear(); _cache.clear();
} }
/// Evicts a single entry from the cache, returning true if successful.
bool evict(Object key) {
return _cache.remove(key) != null;
}
/// Returns the previously cached [LottieComposition] for the given key, if available;
/// if not, calls the given callback to obtain it first. In either case, the
/// key is moved to the "most recently used" position.
///
/// The arguments must not be null. The `loader` cannot return null.
Future<LottieComposition> putIfAbsent(
Object key,
Future<LottieComposition> Function() loader,
) {
var pendingResult = _pending[key];
if (pendingResult != null) {
return pendingResult;
}
var result = _cache[key];
if (result != null) {
// Remove the provider from the list so that we can put it back in below
// and thus move it to the end of the list.
_cache.remove(key);
} else {
if (_cache.length == maximumSize && maximumSize > 0) {
_cache.remove(_cache.keys.first);
}
pendingResult = loader();
_pending[key] = pendingResult;
pendingResult.then<void>((LottieComposition data) {
_pending.remove(key);
_add(key, data);
result = data; // in case it was a synchronous future.
}).catchError((Object? e) {
_pending.remove(key);
});
}
if (result != null) {
_add(key, result!);
return SynchronousFuture<LottieComposition>(result!);
}
assert(_cache.length <= maximumSize);
return pendingResult!;
}
void _add(Object key, LottieComposition result) {
if (maximumSize > 0) {
assert(_cache.length < maximumSize);
_cache[key] = result;
}
assert(_cache.length <= maximumSize);
}
/// The number of entries in the cache.
int get count => _cache.length;
} }
final sharedLottieCache = LottieCache(); final sharedLottieCache = LottieCache();

View File

@ -8,16 +8,13 @@ import 'load_image.dart';
import 'lottie_provider.dart'; import 'lottie_provider.dart';
class MemoryLottie extends LottieProvider { class MemoryLottie extends LottieProvider {
MemoryLottie(this.bytes, {LottieImageProviderFactory? imageProviderFactory}) MemoryLottie(this.bytes, {super.imageProviderFactory});
: super(imageProviderFactory: imageProviderFactory);
final Uint8List bytes; final Uint8List bytes;
@override @override
Future<LottieComposition> load() async { Future<LottieComposition> load() {
// TODO(xha): hash the list content return sharedLottieCache.putIfAbsent(this, () async {
var cacheKey = 'memory-${bytes.hashCode}-${bytes.lengthInBytes}';
return sharedLottieCache.putIfAbsent(cacheKey, () async {
var composition = await LottieComposition.fromBytes(bytes, var composition = await LottieComposition.fromBytes(bytes,
imageProviderFactory: imageProviderFactory); imageProviderFactory: imageProviderFactory);
for (var image in composition.images.values) { for (var image in composition.images.values) {
@ -41,6 +38,8 @@ class MemoryLottie extends LottieProvider {
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) return false; if (other.runtimeType != runtimeType) return false;
//TODO(xha): compare bytes content
return other is MemoryLottie && other.bytes == bytes; return other is MemoryLottie && other.bytes == bytes;
} }

View File

@ -9,17 +9,14 @@ import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as network; import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as network;
class NetworkLottie extends LottieProvider { class NetworkLottie extends LottieProvider {
NetworkLottie(this.url, NetworkLottie(this.url, {this.headers, super.imageProviderFactory});
{this.headers, LottieImageProviderFactory? imageProviderFactory})
: super(imageProviderFactory: imageProviderFactory);
final String url; final String url;
final Map<String, String>? headers; final Map<String, String>? headers;
@override @override
Future<LottieComposition> load() async { Future<LottieComposition> load() {
var cacheKey = 'network-$url'; return sharedLottieCache.putIfAbsent(this, () async {
return sharedLottieCache.putIfAbsent(cacheKey, () async {
var resolved = Uri.base.resolve(url); var resolved = Uri.base.resolve(url);
var bytes = await network.loadHttp(resolved, headers: headers); var bytes = await network.loadHttp(resolved, headers: headers);

View File

@ -13,7 +13,7 @@ import 'render_lottie.dart';
class RawLottie extends LeafRenderObjectWidget { class RawLottie extends LeafRenderObjectWidget {
/// Creates a widget that displays a Lottie composition. /// Creates a widget that displays a Lottie composition.
const RawLottie({ const RawLottie({
Key? key, super.key,
this.composition, this.composition,
this.delegates, this.delegates,
this.options, this.options,
@ -25,8 +25,7 @@ class RawLottie extends LeafRenderObjectWidget {
AlignmentGeometry? alignment, AlignmentGeometry? alignment,
this.filterQuality, this.filterQuality,
}) : progress = progress ?? 0.0, }) : progress = progress ?? 0.0,
alignment = alignment ?? Alignment.center, alignment = alignment ?? Alignment.center;
super(key: key);
/// The Lottie composition to display. /// The Lottie composition to display.
final LottieComposition? composition; final LottieComposition? composition;

View File

@ -7,21 +7,21 @@ packages:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "48.0.0" version: "50.0.0"
analyzer: analyzer:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.0.0" version: "5.2.0"
archive: archive:
dependency: "direct main" dependency: "direct main"
description: description:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.3.1" version: "3.3.5"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -56,7 +56,7 @@ packages:
name: build_config name: build_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
build_daemon: build_daemon:
dependency: transitive dependency: transitive
description: description:
@ -70,21 +70,21 @@ packages:
name: build_resolvers name: build_resolvers
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.10" version: "2.1.0"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.1" version: "2.3.2"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "7.2.4" version: "7.2.7"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@ -98,7 +98,7 @@ packages:
name: built_value name: built_value
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "8.4.1" version: "8.4.2"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -126,7 +126,7 @@ packages:
name: code_builder name: code_builder
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.0" version: "4.3.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -140,7 +140,7 @@ packages:
name: convert name: convert
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.2" version: "3.1.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -199,21 +199,21 @@ packages:
name: frontend_server_client name: frontend_server_client
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "3.2.0"
glob: glob:
dependency: transitive dependency: transitive
description: description:
name: glob name: glob
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
name: graphs name: graphs
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.0"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
@ -227,7 +227,7 @@ packages:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.1" version: "4.0.2"
io: io:
dependency: transitive dependency: transitive
description: description:
@ -241,28 +241,28 @@ packages:
name: js name: js
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.4" version: "0.6.5"
json_annotation: json_annotation:
dependency: transitive dependency: transitive
description: description:
name: json_annotation name: json_annotation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.6.0" version: "4.7.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
name: logging name: logging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.1.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -290,7 +290,7 @@ packages:
name: mime name: mime
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.0.3"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@ -305,6 +305,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.2" version: "1.8.2"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -318,7 +325,7 @@ packages:
name: pub_semver name: pub_semver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.3"
pubspec_parse: pubspec_parse:
dependency: transitive dependency: transitive
description: description:
@ -332,14 +339,14 @@ packages:
name: shelf name: shelf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.2" version: "1.4.0"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.0.3"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -372,7 +379,7 @@ packages:
name: stream_transform name: stream_transform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.1.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -421,7 +428,7 @@ packages:
name: watcher name: watcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.2"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:

View File

@ -1,7 +1,7 @@
name: lottie name: lottie
description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player. description: Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
version: 2.0.0 version: 2.1.0
homepage: https://github.com/xvrh/lottie-flutter repository: https://github.com/xvrh/lottie-flutter
environment: environment:
sdk: '>=2.18.0 <3.0.0' sdk: '>=2.18.0 <3.0.0'

View File

@ -35,10 +35,10 @@ void main() {
tester.binding.window.physicalSizeTestValue = size; tester.binding.window.physicalSizeTestValue = size;
tester.binding.window.devicePixelRatioTestValue = 1.0; tester.binding.window.devicePixelRatioTestValue = 1.0;
var image = await tester.runAsync(() => var image = await tester.runAsync(() async =>
loadImage(FileImage(File('example/assets/Images/WeAccept/img_0.png')))); loadImage(FileImage(File('example/assets/Images/WeAccept/img_0.png'))));
var composition = (await tester.runAsync(() => var composition = (await tester.runAsync(() async =>
FileLottie(File('example/assets/spinning_carrousel.zip')).load()))!; FileLottie(File('example/assets/spinning_carrousel.zip')).load()))!;
var delegates = LottieDelegates(image: (composition, asset) { var delegates = LottieDelegates(image: (composition, asset) {

View File

@ -4,11 +4,10 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
import 'package:lottie/src/providers/lottie_provider.dart';
void main() { void main() {
tearDown(() { tearDown(() {
sharedLottieCache.clear(); Lottie.cache.clear();
}); });
testWidgets('Should settle if no animation', (tester) async { testWidgets('Should settle if no animation', (tester) async {
@ -324,6 +323,72 @@ void main() {
expect(find.byKey(errorKey), findsNothing); expect(find.byKey(errorKey), findsNothing);
expect(loadedCall, 1); expect(loadedCall, 1);
}); });
testWidgets('Cache should be synchronous', (tester) async {
var hamburgerData =
Future.value(bytesForFile('example/assets/HamburgerArrow.json'));
var mockAsset = FakeAssetBundle({
'hamburger.json': hamburgerData,
});
var loadedCall = 0;
var lottieWidget = LottieBuilder.asset(
'hamburger.json',
bundle: mockAsset,
onLoaded: (c) {
++loadedCall;
},
);
await tester.pumpWidget(lottieWidget);
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNull);
await tester.pump();
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNotNull);
await tester.pumpWidget(Column(
children: [
lottieWidget,
lottieWidget,
],
));
expect(tester.widget<Lottie>(find.byType(Lottie).at(0)).composition,
isNotNull);
expect(tester.widget<Lottie>(find.byType(Lottie).at(1)).composition,
isNotNull);
expect(loadedCall, 3);
});
testWidgets('Cache can be cleared', (tester) async {
var hamburgerData =
Future.value(bytesForFile('example/assets/HamburgerArrow.json'));
var mockAsset = FakeAssetBundle({
'hamburger.json': hamburgerData,
});
var loadedCall = 0;
var lottieWidget = LottieBuilder.asset(
'hamburger.json',
bundle: mockAsset,
onLoaded: (c) {
++loadedCall;
},
);
await tester.pumpWidget(lottieWidget);
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNull);
await tester.pump();
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNotNull);
Lottie.cache.clear();
await tester.pumpWidget(Center(
child: lottieWidget,
));
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNull);
await tester.pump();
expect(tester.widget<Lottie>(find.byType(Lottie)).composition, isNotNull);
expect(loadedCall, 2);
});
} }
class SynchronousFile extends Fake implements File { class SynchronousFile extends Fake implements File {

View File

@ -7,8 +7,7 @@ class FilmStrip extends StatelessWidget {
final Size size; final Size size;
const FilmStrip(this.composition, const FilmStrip(this.composition,
{Key? key, required this.size, this.delegates}) {super.key, required this.size, this.delegates});
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {