mirror of
https://github.com/xvrh/lottie-flutter.git
synced 2025-08-06 16:39:36 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
42833c6429 | |||
c9a6a6a187 | |||
6eb9210952 | |||
20405b1c5f | |||
c383dc6be1 | |||
98c2efe12d |
@ -1,3 +1,11 @@
|
|||||||
|
## [0.2.2] - 2020-02-21
|
||||||
|
- Add a [repeat] parameter to specify if the automatic animation should loop.
|
||||||
|
- Add the [animate], [reverse], [repeat] properties on `LottieBuilder`
|
||||||
|
- Fix bug with `onLoaded` callback when the `LottieProvider` is changed
|
||||||
|
|
||||||
|
## [0.2.1] - 2020-02-11
|
||||||
|
- Fix a big bug in the path transformation code. A lot more animations look correct now.
|
||||||
|
|
||||||
## [0.2.0+1] - 2020-02-04
|
## [0.2.0+1] - 2020-02-04
|
||||||
- Improve readme
|
- Improve readme
|
||||||
- (internal) Add golden tests
|
- (internal) Add golden tests
|
||||||
|
@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {
|
|||||||
|
|
||||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
if (flutterRoot == null) {
|
if (flutterRoot == null) {
|
||||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
}
|
}
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
@ -37,8 +37,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
applicationId "com.github.xvrh.lottie.sample"
|
||||||
applicationId "com.example.example"
|
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.example">
|
package="com.github.xvrh.lottie.sample">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.example">
|
package="com.github.xvrh.lottie.sample">
|
||||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||||
In most cases you can leave this as-is, but you if you want to provide
|
In most cases you can leave this as-is, but you if you want to provide
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.example.example
|
package com.github.xvrh.lottie.sample
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.example">
|
package="com.github.xvrh.lottie.sample">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
@ -322,7 +322,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@ -457,7 +457,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@ -485,7 +485,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
11
example/lib/src/web_files.dart
Normal file
11
example/lib/src/web_files.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
final files = [
|
||||||
|
'assets/lottiefiles/cubo_livre.json',
|
||||||
|
'assets/lottiefiles/slack_app_loader.json',
|
||||||
|
'assets/lottiefiles/walking.json',
|
||||||
|
'assets/lottiefiles/jojo_the_bird.json',
|
||||||
|
'assets/lottiefiles/bitcoin_to_the_moon.json',
|
||||||
|
'assets/lottiefiles/splashy_loader.json',
|
||||||
|
'assets/lottiefiles/uk.json',
|
||||||
|
'assets/lottiefiles/yoga_carpet.json',
|
||||||
|
'assets/lottiefiles/books.json',
|
||||||
|
];
|
@ -8,7 +8,7 @@
|
|||||||
PRODUCT_NAME = example
|
PRODUCT_NAME = example
|
||||||
|
|
||||||
// The application's bundle identifier
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example
|
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample
|
||||||
|
|
||||||
// The copyright displayed in application information
|
// The copyright displayed in application information
|
||||||
PRODUCT_COPYRIGHT = Copyright © 2020 com.example. All rights reserved.
|
PRODUCT_COPYRIGHT = Copyright © 2020 com.example. All rights reserved.
|
||||||
|
@ -87,7 +87,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.2.0+1"
|
version: "0.2.1"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -182,8 +182,8 @@ abstract class BaseStrokeContent
|
|||||||
|
|
||||||
var currentLength = 0.0;
|
var currentLength = 0.0;
|
||||||
for (var j = pathGroup.paths.length - 1; j >= 0; j--) {
|
for (var j = pathGroup.paths.length - 1; j >= 0; j--) {
|
||||||
_trimPathPath.set(pathGroup.paths[j].getPath());
|
_trimPathPath
|
||||||
_trimPathPath.transform(parentMatrix.storage);
|
.set(pathGroup.paths[j].getPath().transform(parentMatrix.storage));
|
||||||
var pathMetrics = _trimPathPath.computeMetrics().toList();
|
var pathMetrics = _trimPathPath.computeMetrics().toList();
|
||||||
var length = pathMetrics.first.length;
|
var length = pathMetrics.first.length;
|
||||||
if (endLength > totalLength &&
|
if (endLength > totalLength &&
|
||||||
|
@ -87,7 +87,7 @@ class MergePathsContent implements PathContent, GreedyContent {
|
|||||||
var pathList = content.getPathList();
|
var pathList = content.getPathList();
|
||||||
for (var j = pathList.length - 1; j >= 0; j--) {
|
for (var j = pathList.length - 1; j >= 0; j--) {
|
||||||
var path = pathList[j].getPath();
|
var path = pathList[j].getPath();
|
||||||
path.transform(content.getTransformationMatrix().storage);
|
path = path.transform(content.getTransformationMatrix().storage);
|
||||||
_remainderPath.addPath(path, Offset.zero);
|
_remainderPath.addPath(path, Offset.zero);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -100,7 +100,7 @@ class MergePathsContent implements PathContent, GreedyContent {
|
|||||||
var pathList = lastContent.getPathList();
|
var pathList = lastContent.getPathList();
|
||||||
for (var j = 0; j < pathList.length; j++) {
|
for (var j = 0; j < pathList.length; j++) {
|
||||||
var path = pathList[j].getPath();
|
var path = pathList[j].getPath();
|
||||||
path.transform(lastContent.getTransformationMatrix().storage);
|
path = path.transform(lastContent.getTransformationMatrix().storage);
|
||||||
_firstPath.addPath(path, Offset.zero);
|
_firstPath.addPath(path, Offset.zero);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,21 +6,12 @@ import '../lottie.dart';
|
|||||||
import 'lottie_builder.dart';
|
import 'lottie_builder.dart';
|
||||||
import 'providers/load_image.dart';
|
import 'providers/load_image.dart';
|
||||||
|
|
||||||
|
/// A widget to display a loaded [LottieComposition].
|
||||||
|
/// The [controller] property allows to specify a custom AnimationController that
|
||||||
|
/// will drive the animation. If [controller] is null, the animation will play
|
||||||
|
/// automatically and the behavior could be adjusted with the properties [animate],
|
||||||
|
/// [repeat] and [reverse].
|
||||||
class Lottie extends StatefulWidget {
|
class Lottie extends StatefulWidget {
|
||||||
final LottieComposition composition;
|
|
||||||
|
|
||||||
/// The animation controller to animate the Lottie animation.
|
|
||||||
/// If null, a controller is automatically created by this class and is configured
|
|
||||||
/// with the properties [animate], [reverse]
|
|
||||||
final AnimationController controller;
|
|
||||||
|
|
||||||
/// If no controller is specified, use this values to automatically plays the
|
|
||||||
/// Lottie animation.
|
|
||||||
final bool animate, reverse;
|
|
||||||
final double width, height;
|
|
||||||
final AlignmentGeometry alignment;
|
|
||||||
final BoxFit fit;
|
|
||||||
|
|
||||||
Lottie({
|
Lottie({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.composition,
|
@required this.composition,
|
||||||
@ -34,10 +25,14 @@ class Lottie extends StatefulWidget {
|
|||||||
bool reverse,
|
bool reverse,
|
||||||
}) : animate = animate ?? true,
|
}) : animate = animate ?? true,
|
||||||
reverse = reverse ?? false,
|
reverse = reverse ?? false,
|
||||||
|
repeat = repeat ?? true,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
static LottieBuilder asset(String name,
|
static LottieBuilder asset(String name,
|
||||||
{AnimationController controller,
|
{Animation<double> controller,
|
||||||
|
bool animate,
|
||||||
|
bool repeat,
|
||||||
|
bool reverse,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
Key key,
|
Key key,
|
||||||
@ -51,6 +46,9 @@ class Lottie extends StatefulWidget {
|
|||||||
LottieBuilder.asset(
|
LottieBuilder.asset(
|
||||||
name,
|
name,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
animate: animate,
|
||||||
|
repeat: repeat,
|
||||||
|
reverse: reverse,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -65,7 +63,10 @@ class Lottie extends StatefulWidget {
|
|||||||
|
|
||||||
static LottieBuilder file(
|
static LottieBuilder file(
|
||||||
File file, {
|
File file, {
|
||||||
AnimationController controller,
|
Animation<double> controller,
|
||||||
|
bool animate,
|
||||||
|
bool repeat,
|
||||||
|
bool reverse,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -78,6 +79,9 @@ class Lottie extends StatefulWidget {
|
|||||||
LottieBuilder.file(
|
LottieBuilder.file(
|
||||||
file,
|
file,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
animate: animate,
|
||||||
|
repeat: repeat,
|
||||||
|
reverse: reverse,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -90,7 +94,10 @@ class Lottie extends StatefulWidget {
|
|||||||
|
|
||||||
static LottieBuilder memory(
|
static LottieBuilder memory(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
AnimationController controller,
|
Animation<double> controller,
|
||||||
|
bool animate,
|
||||||
|
bool repeat,
|
||||||
|
bool reverse,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -103,6 +110,9 @@ class Lottie extends StatefulWidget {
|
|||||||
LottieBuilder.memory(
|
LottieBuilder.memory(
|
||||||
bytes,
|
bytes,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
animate: animate,
|
||||||
|
repeat: repeat,
|
||||||
|
reverse: reverse,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -115,7 +125,10 @@ class Lottie extends StatefulWidget {
|
|||||||
|
|
||||||
static LottieBuilder network(
|
static LottieBuilder network(
|
||||||
String url, {
|
String url, {
|
||||||
AnimationController controller,
|
Animation<double> controller,
|
||||||
|
bool animate,
|
||||||
|
bool repeat,
|
||||||
|
bool reverse,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -128,6 +141,9 @@ class Lottie extends StatefulWidget {
|
|||||||
LottieBuilder.network(
|
LottieBuilder.network(
|
||||||
url,
|
url,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
animate: animate,
|
||||||
|
repeat: repeat,
|
||||||
|
reverse: reverse,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -138,6 +154,67 @@ class Lottie extends StatefulWidget {
|
|||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// The Lottie composition to animate.
|
||||||
|
/// It could be parsed asynchronously with `LottieComposition.fromBytes`.
|
||||||
|
final LottieComposition composition;
|
||||||
|
|
||||||
|
/// The animation controller to animate the Lottie animation.
|
||||||
|
/// If null, a controller is automatically created by this class and is configured
|
||||||
|
/// with the properties [animate], [reverse]
|
||||||
|
final Animation<double> controller;
|
||||||
|
|
||||||
|
/// If no controller is specified, this value indicate whether or not the
|
||||||
|
/// Lottie animation should be played automatically (default to true).
|
||||||
|
/// If there is an animation controller specified, this property has no effect.
|
||||||
|
///
|
||||||
|
/// See [repeat] to control whether the animation should repeat.
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
|
/// Specify that the automatic animation should repeat in a loop (default to true).
|
||||||
|
/// The property has no effect if [animate] is false or [controller] is not null.
|
||||||
|
final bool repeat;
|
||||||
|
|
||||||
|
/// Specify that the automatic animation should repeat in a loop in a "reverse"
|
||||||
|
/// mode (go from start to end and then continuously from end to start).
|
||||||
|
/// It default to false.
|
||||||
|
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
||||||
|
final bool reverse;
|
||||||
|
|
||||||
|
/// If non-null, require the Lottie composition to have this width.
|
||||||
|
///
|
||||||
|
/// If null, the composition will pick a size that best preserves its intrinsic
|
||||||
|
/// aspect ratio.
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
/// If non-null, require the Lottie composition to have this height.
|
||||||
|
///
|
||||||
|
/// If null, the composition will pick a size that best preserves its intrinsic
|
||||||
|
/// aspect ratio.
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
/// How to inscribe the Lottie composition into the space allocated during layout.
|
||||||
|
final BoxFit fit;
|
||||||
|
|
||||||
|
/// How to align the composition within its bounds.
|
||||||
|
///
|
||||||
|
/// The alignment aligns the given position in the image to the given position
|
||||||
|
/// in the layout bounds. For example, an [Alignment] alignment of (-1.0,
|
||||||
|
/// -1.0) aligns the image to the top-left corner of its layout bounds, while a
|
||||||
|
/// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the
|
||||||
|
/// image with the bottom right corner of its layout bounds. Similarly, an
|
||||||
|
/// alignment of (0.0, 1.0) aligns the bottom middle of the image with the
|
||||||
|
/// middle of the bottom edge of its layout bounds.
|
||||||
|
///
|
||||||
|
/// Defaults to [Alignment.center].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Alignment], a class with convenient constants typically used to
|
||||||
|
/// specify an [AlignmentGeometry].
|
||||||
|
/// * [AlignmentDirectional], like [Alignment] for specifying alignments
|
||||||
|
/// relative to text direction.
|
||||||
|
final AlignmentGeometry alignment;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LottieState createState() => _LottieState();
|
_LottieState createState() => _LottieState();
|
||||||
}
|
}
|
||||||
@ -167,8 +244,12 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
|
|||||||
void _updateAutoAnimation() {
|
void _updateAutoAnimation() {
|
||||||
_autoAnimation.stop();
|
_autoAnimation.stop();
|
||||||
|
|
||||||
if (widget.animate) {
|
if (widget.animate && widget.controller == null) {
|
||||||
_autoAnimation.repeat(reverse: widget.reverse);
|
if (widget.repeat) {
|
||||||
|
_autoAnimation.repeat(reverse: widget.reverse);
|
||||||
|
} else {
|
||||||
|
_autoAnimation.forward();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,18 +25,21 @@ typedef LottieFrameBuilder = Widget Function(
|
|||||||
/// Several constructors are provided for the various ways that a Lottie file
|
/// Several constructors are provided for the various ways that a Lottie file
|
||||||
/// can be provided:
|
/// can be provided:
|
||||||
///
|
///
|
||||||
/// * [new Lottie], for obtaining an image from a [LottieProvider].
|
/// * [new Lottie], for obtaining a composition from a [LottieProvider].
|
||||||
/// * [new Lottie.asset], for obtaining a Lottie file from an [AssetBundle]
|
/// * [new Lottie.asset], for obtaining a Lottie file from an [AssetBundle]
|
||||||
/// using a key.
|
/// using a key.
|
||||||
/// * [new Image.network], for obtaining a lottie file from a URL.
|
/// * [new Lottie.network], for obtaining a lottie file from a URL.
|
||||||
/// * [new Image.file], for obtaining a lottie file from a [File].
|
/// * [new Lottie.file], for obtaining a lottie file from a [File].
|
||||||
/// * [new Image.memory], for obtaining a lottie file from a [Uint8List].
|
/// * [new Lottie.memory], for obtaining a lottie file from a [Uint8List].
|
||||||
///
|
///
|
||||||
class LottieBuilder extends StatefulWidget {
|
class LottieBuilder extends StatefulWidget {
|
||||||
const LottieBuilder({
|
const LottieBuilder({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.lottie,
|
@required this.lottie,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.animate,
|
||||||
|
this.reverse,
|
||||||
|
this.repeat,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
this.frameBuilder,
|
this.frameBuilder,
|
||||||
this.width,
|
this.width,
|
||||||
@ -51,6 +54,9 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
String src, {
|
String src, {
|
||||||
Map<String, String> headers,
|
Map<String, String> headers,
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.animate,
|
||||||
|
this.reverse,
|
||||||
|
this.repeat,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -78,6 +84,9 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
LottieBuilder.file(
|
LottieBuilder.file(
|
||||||
File file, {
|
File file, {
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.animate,
|
||||||
|
this.reverse,
|
||||||
|
this.repeat,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -92,6 +101,9 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
LottieBuilder.asset(
|
LottieBuilder.asset(
|
||||||
String name, {
|
String name, {
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.animate,
|
||||||
|
this.reverse,
|
||||||
|
this.repeat,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -112,6 +124,9 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
LottieBuilder.memory(
|
LottieBuilder.memory(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
this.controller,
|
this.controller,
|
||||||
|
this.animate,
|
||||||
|
this.reverse,
|
||||||
|
this.repeat,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -125,6 +140,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// The lottie animation to display.
|
/// The lottie animation to display.
|
||||||
|
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
|
||||||
final LottieProvider lottie;
|
final LottieProvider lottie;
|
||||||
|
|
||||||
/// A callback called when the LottieComposition has been loaded.
|
/// A callback called when the LottieComposition has been loaded.
|
||||||
@ -135,7 +151,24 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
/// The animation controller of the Lottie animation.
|
/// The animation controller of the Lottie animation.
|
||||||
/// The animated value will be mapped to the `progress` property of the
|
/// The animated value will be mapped to the `progress` property of the
|
||||||
/// Lottie animation.
|
/// Lottie animation.
|
||||||
final AnimationController controller;
|
final Animation<double> controller;
|
||||||
|
|
||||||
|
/// If no controller is specified, this value indicate whether or not the
|
||||||
|
/// Lottie animation should be played automatically (default to true).
|
||||||
|
/// If there is an animation controller specified, this property has no effect.
|
||||||
|
///
|
||||||
|
/// See [repeat] to control whether the animation should repeat.
|
||||||
|
final bool animate;
|
||||||
|
|
||||||
|
/// Specify that the automatic animation should repeat in a loop (default to true).
|
||||||
|
/// The property has no effect if [animate] is false or [controller] is not null.
|
||||||
|
final bool repeat;
|
||||||
|
|
||||||
|
/// Specify that the automatic animation should repeat in a loop in a "reverse"
|
||||||
|
/// mode (go from start to end and then continuously from end to start).
|
||||||
|
/// It default to false.
|
||||||
|
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
||||||
|
final bool reverse;
|
||||||
|
|
||||||
/// A builder function responsible for creating the widget that represents
|
/// A builder function responsible for creating the widget that represents
|
||||||
/// this lottie animation.
|
/// this lottie animation.
|
||||||
@ -143,10 +176,10 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
/// If this is null, this widget will display a lottie animation that is painted as
|
/// If this is null, this widget will display a lottie animation that is painted as
|
||||||
/// soon as it is available (and will appear to "pop" in
|
/// soon as it is available (and will appear to "pop" in
|
||||||
/// if it becomes available asynchronously). Callers might use this builder to
|
/// if it becomes available asynchronously). Callers might use this builder to
|
||||||
/// add effects to the image (such as fading the image in when it becomes
|
/// add effects to the animation (such as fading the animation in when it becomes
|
||||||
/// available) or to display a placeholder widget while the image is loading.
|
/// available) or to display a placeholder widget while the animation is loading.
|
||||||
///
|
///
|
||||||
/// To have finer-grained control over the way that an image's loading
|
/// To have finer-grained control over the way that an animation's loading
|
||||||
/// progress is communicated to the user, see [loadingBuilder].
|
/// progress is communicated to the user, see [loadingBuilder].
|
||||||
///
|
///
|
||||||
/// {@template lottie.chainedBuildersExample}
|
/// {@template lottie.chainedBuildersExample}
|
||||||
@ -177,7 +210,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
/// {@tool snippet --template=stateless_widget_material}
|
/// {@tool snippet --template=stateless_widget_material}
|
||||||
///
|
///
|
||||||
/// The following sample demonstrates how to use this builder to implement an
|
/// The following sample demonstrates how to use this builder to implement an
|
||||||
/// image that fades in once it's been loaded.
|
/// animation that fades in once it's been loaded.
|
||||||
///
|
///
|
||||||
/// This sample contains a limited subset of the functionality that the
|
/// This sample contains a limited subset of the functionality that the
|
||||||
/// [FadeInImage] widget provides out of the box.
|
/// [FadeInImage] widget provides out of the box.
|
||||||
@ -219,9 +252,9 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
/// It is strongly recommended that either both the [width] and the [height]
|
/// It is strongly recommended that either both the [width] and the [height]
|
||||||
/// be specified, or that the widget be placed in a context that sets tight
|
/// be specified, or that the widget be placed in a context that sets tight
|
||||||
/// layout constraints, so that the image does not change size as it loads.
|
/// layout constraints, so that the animation does not change size as it loads.
|
||||||
/// Consider using [fit] to adapt the image's rendering to fit the given width
|
/// Consider using [fit] to adapt the animation's rendering to fit the given width
|
||||||
/// and height if the exact image dimensions are not known in advance.
|
/// and height if the exact animation dimensions are not known in advance.
|
||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
/// If non-null, require the lottie animation to have this height.
|
/// If non-null, require the lottie animation to have this height.
|
||||||
@ -231,28 +264,28 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
/// It is strongly recommended that either both the [width] and the [height]
|
/// It is strongly recommended that either both the [width] and the [height]
|
||||||
/// be specified, or that the widget be placed in a context that sets tight
|
/// be specified, or that the widget be placed in a context that sets tight
|
||||||
/// layout constraints, so that the image does not change size as it loads.
|
/// layout constraints, so that the animation does not change size as it loads.
|
||||||
/// Consider using [fit] to adapt the image's rendering to fit the given width
|
/// Consider using [fit] to adapt the animation's rendering to fit the given width
|
||||||
/// and height if the exact image dimensions are not known in advance.
|
/// and height if the exact animation dimensions are not known in advance.
|
||||||
final double height;
|
final double height;
|
||||||
|
|
||||||
/// How to inscribe the image into the space allocated during layout.
|
/// How to inscribe the animation into the space allocated during layout.
|
||||||
///
|
///
|
||||||
/// The default varies based on the other fields. See the discussion at
|
/// The default varies based on the other fields. See the discussion at
|
||||||
/// [paintImage].
|
/// [paintImage].
|
||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
|
|
||||||
/// How to align the image within its bounds.
|
/// How to align the animation within its bounds.
|
||||||
///
|
///
|
||||||
/// The alignment aligns the given position in the image to the given position
|
/// The alignment aligns the given position in the animation to the given position
|
||||||
/// in the layout bounds. For example, an [Alignment] alignment of (-1.0,
|
/// in the layout bounds. For example, an [Alignment] alignment of (-1.0,
|
||||||
/// -1.0) aligns the image to the top-left corner of its layout bounds, while an
|
/// -1.0) aligns the animation to the top-left corner of its layout bounds, while an
|
||||||
/// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the
|
/// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the
|
||||||
/// image with the bottom right corner of its layout bounds. Similarly, an
|
/// animation with the bottom right corner of its layout bounds. Similarly, an
|
||||||
/// alignment of (0.0, 1.0) aligns the bottom middle of the image with the
|
/// alignment of (0.0, 1.0) aligns the bottom middle of the animation with the
|
||||||
/// middle of the bottom edge of its layout bounds.
|
/// middle of the bottom edge of its layout bounds.
|
||||||
///
|
///
|
||||||
/// To display a subpart of an image, consider using a [CustomPainter] and
|
/// To display a subpart of an animation, consider using a [CustomPainter] and
|
||||||
/// [Canvas.drawImageRect].
|
/// [Canvas.drawImageRect].
|
||||||
///
|
///
|
||||||
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
|
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
|
||||||
@ -288,13 +321,12 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
|
|
||||||
class _LottieBuilderState extends State<LottieBuilder> {
|
class _LottieBuilderState extends State<LottieBuilder> {
|
||||||
Future<LottieComposition> _loadingFuture;
|
Future<LottieComposition> _loadingFuture;
|
||||||
bool _calledLoadedCallback = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_loadingFuture = widget.lottie.load();
|
_load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -302,11 +334,21 @@ class _LottieBuilderState extends State<LottieBuilder> {
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
if (oldWidget.lottie != widget.lottie) {
|
if (oldWidget.lottie != widget.lottie) {
|
||||||
_loadingFuture = widget.lottie.load();
|
_load();
|
||||||
_calledLoadedCallback = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _load() {
|
||||||
|
var provider = widget.lottie;
|
||||||
|
_loadingFuture = widget.lottie.load().then((composition) {
|
||||||
|
if (mounted && widget.onLoaded != null && widget.lottie == provider) {
|
||||||
|
widget.onLoaded(composition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return composition;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder<LottieComposition>(
|
return FutureBuilder<LottieComposition>(
|
||||||
@ -319,16 +361,13 @@ class _LottieBuilderState extends State<LottieBuilder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var composition = snapshot.data;
|
var composition = snapshot.data;
|
||||||
if (composition != null && !_calledLoadedCallback) {
|
|
||||||
_calledLoadedCallback = true;
|
|
||||||
if (widget.onLoaded != null) {
|
|
||||||
widget.onLoaded(composition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget result = Lottie(
|
Widget result = Lottie(
|
||||||
composition: composition,
|
composition: composition,
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
|
animate: widget.animate,
|
||||||
|
reverse: widget.reverse,
|
||||||
|
repeat: widget.repeat,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
|
@ -52,7 +52,6 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ui.Path _path = ui.Path();
|
|
||||||
final Matrix4 _matrix = Matrix4.identity();
|
final Matrix4 _matrix = Matrix4.identity();
|
||||||
final Paint _contentPaint = ui.Paint();
|
final Paint _contentPaint = ui.Paint();
|
||||||
final Paint _dstInPaint = ui.Paint()..blendMode = ui.BlendMode.dstIn;
|
final Paint _dstInPaint = ui.Paint()..blendMode = ui.BlendMode.dstIn;
|
||||||
@ -279,8 +278,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
BaseKeyframeAnimation<dynamic, Path> maskAnimation =
|
BaseKeyframeAnimation<dynamic, Path> maskAnimation =
|
||||||
_mask.maskAnimations[i];
|
_mask.maskAnimations[i];
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path.set(maskPath);
|
var path = maskPath.transform(matrix.storage);
|
||||||
_path.transform(matrix.storage);
|
|
||||||
|
|
||||||
switch (mask.maskMode) {
|
switch (mask.maskMode) {
|
||||||
case MaskMode.maskModeNone:
|
case MaskMode.maskModeNone:
|
||||||
@ -297,7 +295,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
var maskBounds = _path.getBounds();
|
var maskBounds = path.getBounds();
|
||||||
// As we iterate through the masks, we want to calculate the union region of the masks.
|
// As we iterate through the masks, we want to calculate the union region of the masks.
|
||||||
// We initialize the rect with the first mask. If we don't call set() on the first call,
|
// We initialize the rect with the first mask. If we don't call set() on the first call,
|
||||||
// the rect will always extend to (0,0).
|
// the rect will always extend to (0,0).
|
||||||
@ -420,11 +418,9 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
BaseKeyframeAnimation<ShapeData, Path> maskAnimation,
|
BaseKeyframeAnimation<ShapeData, Path> maskAnimation,
|
||||||
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path
|
var path = maskPath.transform(matrix.storage);
|
||||||
..set(maskPath)
|
|
||||||
..transform(matrix.storage);
|
|
||||||
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
||||||
canvas.drawPath(_path, _contentPaint);
|
canvas.drawPath(path, _contentPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _applyInvertedAddMask(
|
void _applyInvertedAddMask(
|
||||||
@ -437,11 +433,9 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
canvas.saveLayer(bounds, _contentPaint);
|
canvas.saveLayer(bounds, _contentPaint);
|
||||||
canvas.drawRect(bounds, _contentPaint);
|
canvas.drawRect(bounds, _contentPaint);
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path
|
var path = maskPath.transform(matrix.storage);
|
||||||
..set(maskPath)
|
|
||||||
..transform(matrix.storage);
|
|
||||||
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
||||||
canvas.drawPath(_path, _dstOutPaint);
|
canvas.drawPath(path, _dstOutPaint);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,10 +446,8 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
BaseKeyframeAnimation<ShapeData, Path> maskAnimation,
|
BaseKeyframeAnimation<ShapeData, Path> maskAnimation,
|
||||||
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path
|
var path = maskPath.transform(matrix.storage);
|
||||||
..set(maskPath)
|
canvas.drawPath(path, _dstOutPaint);
|
||||||
..transform(matrix.storage);
|
|
||||||
canvas.drawPath(_path, _dstOutPaint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _applyInvertedSubtractMask(
|
void _applyInvertedSubtractMask(
|
||||||
@ -470,9 +462,8 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
_dstOutPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
_dstOutPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
||||||
|
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path.set(maskPath);
|
var path = maskPath.transform(matrix.storage);
|
||||||
_path.transform(matrix.storage);
|
canvas.drawPath(path, _dstOutPaint);
|
||||||
canvas.drawPath(_path, _dstOutPaint);
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,10 +476,9 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
BaseKeyframeAnimation<int, int> opacityAnimation) {
|
||||||
canvas.saveLayer(bounds, _dstInPaint);
|
canvas.saveLayer(bounds, _dstInPaint);
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path.set(maskPath);
|
var path = maskPath.transform(matrix.storage);
|
||||||
_path.transform(matrix.storage);
|
|
||||||
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
_contentPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
||||||
canvas.drawPath(_path, _contentPaint);
|
canvas.drawPath(path, _contentPaint);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,9 +493,8 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
|
|||||||
canvas.drawRect(bounds, _contentPaint);
|
canvas.drawRect(bounds, _contentPaint);
|
||||||
_dstOutPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
_dstOutPaint.setAlpha((opacityAnimation.value * 2.55).round());
|
||||||
var maskPath = maskAnimation.value;
|
var maskPath = maskAnimation.value;
|
||||||
_path.set(maskPath);
|
var path = maskPath.transform(matrix.storage);
|
||||||
_path.transform(matrix.storage);
|
canvas.drawPath(path, _dstOutPaint);
|
||||||
canvas.drawPath(_path, _dstOutPaint);
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ class TextLayer extends BaseLayer {
|
|||||||
_matrix.translate(
|
_matrix.translate(
|
||||||
0.0, -documentData.baselineShift * window.devicePixelRatio);
|
0.0, -documentData.baselineShift * window.devicePixelRatio);
|
||||||
_matrix.scale(fontScale, fontScale);
|
_matrix.scale(fontScale, fontScale);
|
||||||
path.transform(_matrix.storage);
|
path = path.transform(_matrix.storage);
|
||||||
if (documentData.strokeOverFill) {
|
if (documentData.strokeOverFill) {
|
||||||
_drawGlyph(path, _fillPaint, canvas);
|
_drawGlyph(path, _fillPaint, canvas);
|
||||||
_drawGlyph(path, _strokePaint, canvas);
|
_drawGlyph(path, _strokePaint, canvas);
|
||||||
|
@ -20,7 +20,8 @@ class MaskParser {
|
|||||||
var mode = reader.nextName();
|
var mode = reader.nextName();
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'mode':
|
case 'mode':
|
||||||
switch (reader.nextString()) {
|
var modeName = reader.nextString();
|
||||||
|
switch (modeName) {
|
||||||
case 'a':
|
case 'a':
|
||||||
maskMode = MaskMode.maskModeAdd;
|
maskMode = MaskMode.maskModeAdd;
|
||||||
break;
|
break;
|
||||||
@ -36,7 +37,7 @@ class MaskParser {
|
|||||||
maskMode = MaskMode.maskModeIntersect;
|
maskMode = MaskMode.maskModeIntersect;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.warning('Unknown mask mode $mode. Defaulting to Add.');
|
logger.warning('Unknown mask mode $modeName. Defaulting to Add.');
|
||||||
maskMode = MaskMode.maskModeAdd;
|
maskMode = MaskMode.maskModeAdd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -46,6 +46,10 @@ class LottieCache {
|
|||||||
_cache.remove(_cache.keys.first);
|
_cache.remove(_cache.keys.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_cache.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final sharedLottieCache = LottieCache();
|
final sharedLottieCache = LottieCache();
|
||||||
|
@ -67,7 +67,7 @@ class MiscUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int floorMod(double x, double y) {
|
static int floorMod(double x, double y) {
|
||||||
return _floorDiv(x.round(), y.round());
|
return x.toInt() - y.toInt() * _floorDiv(x.toInt(), y.toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _floorDiv(int x, int y) {
|
static int _floorDiv(int x, int y) {
|
||||||
|
@ -144,6 +144,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.8"
|
version: "1.1.8"
|
||||||
|
mockito:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: mockito
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.1"
|
||||||
node_interop:
|
node_interop:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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: 0.2.0+1
|
version: 0.2.2
|
||||||
homepage: https://github.com/xvrh/lottie-flutter
|
homepage: https://github.com/xvrh/lottie-flutter
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -19,9 +19,10 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
yaml:
|
|
||||||
analyzer:
|
analyzer:
|
||||||
dart_style:
|
dart_style:
|
||||||
|
mockito: ^4.0.0
|
||||||
|
yaml:
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
pedantic: ^1.9.0
|
pedantic: ^1.9.0
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB |
Binary file not shown.
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 133 KiB |
312
test/lottie_test.dart
Normal file
312
test/lottie_test.dart
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
import 'package:lottie/src/providers/lottie_provider.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
tearDown(() {
|
||||||
|
sharedLottieCache.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Should settle if no animation', (tester) async {
|
||||||
|
var data = File('example/assets/HamburgerArrow.json').readAsBytesSync();
|
||||||
|
var composition = await LottieComposition.fromBytes(data);
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
animate: false,
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('onLoaded called with the correct composition', (tester) async {
|
||||||
|
LottieComposition composition;
|
||||||
|
|
||||||
|
var file = MockFile();
|
||||||
|
var data = File('example/assets/HamburgerArrow.json').readAsBytesSync();
|
||||||
|
when(file.readAsBytes()).thenAnswer((value) => Future.value(data));
|
||||||
|
|
||||||
|
await tester.pumpWidget(LottieBuilder.file(
|
||||||
|
file,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(composition, isNotNull);
|
||||||
|
expect(composition.endFrame, 179.99);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('onLoaded called when remplacing the widget animation',
|
||||||
|
(tester) async {
|
||||||
|
var mockAsset = MockAssetBundle();
|
||||||
|
|
||||||
|
ByteData read(String path) =>
|
||||||
|
File(path).readAsBytesSync().buffer.asByteData();
|
||||||
|
|
||||||
|
var hamburgerData =
|
||||||
|
Future.value(read('example/assets/HamburgerArrow.json'));
|
||||||
|
var androidData = Future.value(read('example/assets/AndroidWave.json'));
|
||||||
|
|
||||||
|
when(mockAsset.load('hamburger.json')).thenAnswer((_) => hamburgerData);
|
||||||
|
when(mockAsset.load('android.json')).thenAnswer((_) => androidData);
|
||||||
|
|
||||||
|
var animation = AnimationController(vsync: tester);
|
||||||
|
|
||||||
|
LottieComposition composition;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'hamburger.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
var widgetFinder = find.byType(Lottie);
|
||||||
|
expect(widgetFinder, findsOneWidget);
|
||||||
|
expect(composition, isNotNull);
|
||||||
|
expect(composition.duration, Duration(seconds: 6));
|
||||||
|
|
||||||
|
composition = null;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'android.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition, isNotNull);
|
||||||
|
expect(composition.duration, Duration(seconds: 2, milliseconds: 50));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('onLoaded data race 1', (tester) async {
|
||||||
|
var mockAsset = MockAssetBundle();
|
||||||
|
|
||||||
|
ByteData read(String path) =>
|
||||||
|
File(path).readAsBytesSync().buffer.asByteData();
|
||||||
|
|
||||||
|
var hamburgerCompleter = Completer<ByteData>();
|
||||||
|
var androidCompleter = Completer<ByteData>();
|
||||||
|
|
||||||
|
var hamburgerData = read('example/assets/HamburgerArrow.json');
|
||||||
|
var androidData = read('example/assets/AndroidWave.json');
|
||||||
|
|
||||||
|
when(mockAsset.load('hamburger.json'))
|
||||||
|
.thenAnswer((_) => hamburgerCompleter.future);
|
||||||
|
when(mockAsset.load('android.json'))
|
||||||
|
.thenAnswer((_) => androidCompleter.future);
|
||||||
|
|
||||||
|
var animation = AnimationController(vsync: tester);
|
||||||
|
|
||||||
|
var onLoadedCount = 0;
|
||||||
|
|
||||||
|
LottieComposition composition;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'hamburger.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
++onLoadedCount;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate((w) => w is RawLottie && w.composition == null),
|
||||||
|
findsOneWidget);
|
||||||
|
expect(composition, isNull);
|
||||||
|
expect(onLoadedCount, 0);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'android.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
++onLoadedCount;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition, isNull);
|
||||||
|
expect(onLoadedCount, 0);
|
||||||
|
|
||||||
|
hamburgerCompleter.complete(hamburgerData);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition, isNull);
|
||||||
|
expect(onLoadedCount, 0);
|
||||||
|
|
||||||
|
androidCompleter.complete(androidData);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition.duration, Duration(seconds: 2, milliseconds: 50));
|
||||||
|
expect(onLoadedCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('onLoaded data race 2', (tester) async {
|
||||||
|
var mockAsset = MockAssetBundle();
|
||||||
|
|
||||||
|
ByteData read(String path) =>
|
||||||
|
File(path).readAsBytesSync().buffer.asByteData();
|
||||||
|
|
||||||
|
var hamburgerCompleter = Completer<ByteData>();
|
||||||
|
var androidCompleter = Completer<ByteData>();
|
||||||
|
|
||||||
|
var hamburgerData = read('example/assets/HamburgerArrow.json');
|
||||||
|
var androidData = read('example/assets/AndroidWave.json');
|
||||||
|
|
||||||
|
when(mockAsset.load('hamburger.json'))
|
||||||
|
.thenAnswer((_) => hamburgerCompleter.future);
|
||||||
|
when(mockAsset.load('android.json'))
|
||||||
|
.thenAnswer((_) => androidCompleter.future);
|
||||||
|
|
||||||
|
var animation = AnimationController(vsync: tester);
|
||||||
|
|
||||||
|
var onLoadedCount = 0;
|
||||||
|
|
||||||
|
LottieComposition composition;
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'hamburger.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
++onLoadedCount;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate((w) => w is RawLottie && w.composition == null),
|
||||||
|
findsOneWidget);
|
||||||
|
expect(composition, isNull);
|
||||||
|
expect(onLoadedCount, 0);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie.asset(
|
||||||
|
'android.json',
|
||||||
|
controller: animation,
|
||||||
|
bundle: mockAsset,
|
||||||
|
onLoaded: (c) {
|
||||||
|
composition = c;
|
||||||
|
++onLoadedCount;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition, isNull);
|
||||||
|
expect(onLoadedCount, 0);
|
||||||
|
|
||||||
|
androidCompleter.complete(androidData);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition.duration, Duration(seconds: 2, milliseconds: 50));
|
||||||
|
expect(onLoadedCount, 1);
|
||||||
|
|
||||||
|
hamburgerCompleter.complete(hamburgerData);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
expect(composition.duration, Duration(seconds: 2, milliseconds: 50));
|
||||||
|
expect(onLoadedCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Should auto animate', (tester) async {
|
||||||
|
var composition = await LottieComposition.fromBytes(
|
||||||
|
File('example/assets/HamburgerArrow.json').readAsBytesSync());
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
var lottie =
|
||||||
|
tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 6));
|
||||||
|
expect((lottie.listenable as AnimationController).isAnimating, true);
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
animate: false,
|
||||||
|
));
|
||||||
|
|
||||||
|
lottie = tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 6));
|
||||||
|
expect((lottie.listenable as AnimationController).isAnimating, false);
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
));
|
||||||
|
|
||||||
|
lottie = tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 6));
|
||||||
|
|
||||||
|
var animationController =
|
||||||
|
AnimationController(vsync: tester, duration: Duration(seconds: 2));
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
controller: animationController.view,
|
||||||
|
));
|
||||||
|
|
||||||
|
lottie = tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 2));
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
controller: animationController.view,
|
||||||
|
animate: false,
|
||||||
|
));
|
||||||
|
|
||||||
|
lottie = tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 2));
|
||||||
|
|
||||||
|
await tester.pumpWidget(Lottie(
|
||||||
|
composition: composition,
|
||||||
|
animate: false,
|
||||||
|
));
|
||||||
|
|
||||||
|
lottie = tester.firstWidget<AnimatedBuilder>(find.byType(AnimatedBuilder));
|
||||||
|
expect(lottie.listenable, isNotNull);
|
||||||
|
expect((lottie.listenable as AnimationController).duration,
|
||||||
|
Duration(seconds: 6));
|
||||||
|
expect((lottie.listenable as AnimationController).isAnimating, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockFile extends Mock implements File {}
|
||||||
|
|
||||||
|
class MockAssetBundle extends Mock implements AssetBundle {}
|
Reference in New Issue
Block a user