mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-03 04:18:25 +08:00
feat: Lottie bridge package (#2157)
This PR adds support for Lottie animations to flame
This commit is contained in:
2
.github/.cspell/flame_dictionary.txt
vendored
2
.github/.cspell/flame_dictionary.txt
vendored
@ -24,3 +24,5 @@ tavian
|
||||
trex
|
||||
wolfenrain
|
||||
Wyrmsun
|
||||
Bodymovin
|
||||
xaha
|
||||
51
doc/bridge_packages/flame_lottie/flame_lottie.md
Normal file
51
doc/bridge_packages/flame_lottie/flame_lottie.md
Normal file
@ -0,0 +1,51 @@
|
||||
# flame_lottie
|
||||
|
||||
This package allows you to load and add Lottie animations to your Flame game.
|
||||
|
||||
|
||||
The native Lottie libraries (such as [lottie-android](https://github.com/airbnb/lottie-android))
|
||||
are maintained by **Airbnb**.
|
||||
|
||||
The Flutter package ``lottie``, on which this wrapper is based on, is by developed **xaha.dev** and
|
||||
can be found on [pub.dev](https://pub.dev/packages/lottie).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
To use it in your game you just need to add `flame_lottie` to your pubspec.yaml.
|
||||
|
||||
Simply load the Lottie animation using the **loadLottie** method and
|
||||
the [LottieBuilder](https://pub.dev/documentation/lottie/latest/lottie/LottieBuilder-class.html).
|
||||
It allows all the various ways of loading a Lottie file:
|
||||
|
||||
- [Lottie.asset](https://pub.dev/documentation/lottie/latest/lottie/Lottie/asset.html), for
|
||||
obtaining a Lottie file from an AssetBundle using a key.
|
||||
- [Lottie.network](https://pub.dev/documentation/lottie/latest/lottie/Lottie/network.html), for
|
||||
obtaining a lottie file from a URL.
|
||||
- [Lottie.file](https://pub.dev/documentation/lottie/latest/lottie/Lottie/file.html), for obtaining
|
||||
a lottie file from a File.
|
||||
- [Lottie.memory](https://pub.dev/documentation/lottie/latest/lottie/Lottie/memory.html), for
|
||||
obtaining a lottie file from a Uint8List.
|
||||
|
||||
... and add it as `LottieComponent` to your Flame 🔥 game.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
class MyGame extends FlameGame {
|
||||
...
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final asset = Lottie.asset('assets/LottieLogo1.json');
|
||||
final animation = await loadLottie(asset);
|
||||
add(
|
||||
LottieComponent(
|
||||
animation,
|
||||
repeating: true, // Continuously loop the animation.
|
||||
size: Vector2.all(400),
|
||||
),
|
||||
);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
1
examples/assets/images/animations/lottieLogo.json
Normal file
1
examples/assets/images/animations/lottieLogo.json
Normal file
File diff suppressed because one or more lines are too long
@ -2,6 +2,7 @@ import 'package:dashbook/dashbook.dart';
|
||||
import 'package:examples/stories/animations/animations.dart';
|
||||
import 'package:examples/stories/bridge_libraries/audio/audio.dart';
|
||||
import 'package:examples/stories/bridge_libraries/flame_isolate/isolate.dart';
|
||||
import 'package:examples/stories/bridge_libraries/flame_lottie/lottie.dart';
|
||||
import 'package:examples/stories/bridge_libraries/forge2d/flame_forge2d.dart';
|
||||
import 'package:examples/stories/camera_and_viewport/camera_and_viewport.dart';
|
||||
import 'package:examples/stories/collision_detection/collision_detection.dart';
|
||||
@ -50,6 +51,7 @@ void main() {
|
||||
// Bridge package examples
|
||||
addForge2DStories(dashbook);
|
||||
addFlameIsolateExample(dashbook);
|
||||
addFlameLottieExample(dashbook);
|
||||
|
||||
runApp(dashbook);
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:examples/commons/commons.dart';
|
||||
import 'package:examples/stories/bridge_libraries/flame_lottie/lottie_animation_example.dart';
|
||||
import 'package:flame/game.dart';
|
||||
|
||||
void addFlameLottieExample(Dashbook dashbook) {
|
||||
dashbook.storiesOf('FlameLottie').add(
|
||||
'Lottie Animation example',
|
||||
(_) => GameWidget(
|
||||
game: LottieAnimationExample(),
|
||||
),
|
||||
codeLink: baseLink('flame_lottie/lottie_animation_example.dart'),
|
||||
info: LottieAnimationExample.description,
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame_lottie/flame_lottie.dart';
|
||||
|
||||
class LottieAnimationExample extends FlameGame {
|
||||
static const String description = '''
|
||||
This example shows how to load a Lottie animation. It is configured to
|
||||
continuously loop the animation and restart once its done.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final asset = await loadLottie(
|
||||
Lottie.asset('assets/images/animations/lottieLogo.json'),
|
||||
);
|
||||
|
||||
add(LottieComponent(asset, size: Vector2.all(400), repeating: true));
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ dependencies:
|
||||
flame_audio: ^1.3.2
|
||||
flame_forge2d: ^0.12.2
|
||||
flame_isolate: ^0.1.0
|
||||
flame_lottie: ^0.1.0
|
||||
flame_svg: ^1.6.0
|
||||
flame_tiled: ^1.8.0
|
||||
flutter:
|
||||
|
||||
10
packages/flame_lottie/.metadata
Normal file
10
packages/flame_lottie/.metadata
Normal file
@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
channel: stable
|
||||
|
||||
project_type: package
|
||||
22
packages/flame_lottie/LICENSE
Normal file
22
packages/flame_lottie/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Blue Fire
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
55
packages/flame_lottie/README.md
Normal file
55
packages/flame_lottie/README.md
Normal file
@ -0,0 +1,55 @@
|
||||
# flame_lottie
|
||||
|
||||
This package allows you to load and add Lottie animations to your Flame game.
|
||||
|
||||
> Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations
|
||||
exported as json with Bodymovin and renders them natively on mobile!
|
||||
|
||||
Source: [lottie-android](https://github.com/airbnb/lottie-android) on Github
|
||||
|
||||
|
||||
The native Lottie libraries (such as [lottie-android](https://github.com/airbnb/lottie-android))
|
||||
are maintained by **Airbnb**.
|
||||
|
||||
The Flutter package ``lottie``, on which this wrapper is based on, is by **xaha.dev** and can be
|
||||
found on [pub dev](https://pub.dev/packages/lottie).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
To use it in your game you just need to add `flame_lottie` to your pubspec.yaml.
|
||||
|
||||
Simply load the Lottie animation using the **loadLottie** method and the
|
||||
[LottieBuilder](https://pub.dev/documentation/lottie/latest/lottie/LottieBuilder-class.html). It
|
||||
allows all the various ways of loading a Lottie file:
|
||||
|
||||
- [Lottie.asset](https://pub.dev/documentation/lottie/latest/lottie/Lottie/asset.html), for
|
||||
obtaining a Lottie file from an AssetBundle using a key.
|
||||
- [Lottie.network](https://pub.dev/documentation/lottie/latest/lottie/Lottie/network.html), for
|
||||
obtaining a lottie file from a URL.
|
||||
- [Lottie.file](https://pub.dev/documentation/lottie/latest/lottie/Lottie/file.html), for obtaining
|
||||
a lottie file from a File.
|
||||
- [Lottie.memory](https://pub.dev/documentation/lottie/latest/lottie/Lottie/memory.html), for
|
||||
obtaining a lottie file from a Uint8List.
|
||||
|
||||
... and add it as `LottieComponent` to your flame 🔥 game.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
class MyGame extends FlameGame {
|
||||
...
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final asset = Lottie.asset('assets/LottieLogo1.json');
|
||||
final animation = await loadLottie(asset);
|
||||
add(
|
||||
LottieComponent(
|
||||
composition: animation,
|
||||
repeating: true, // continuously loop the animation
|
||||
),
|
||||
);
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
1
packages/flame_lottie/analysis_options.yaml
Normal file
1
packages/flame_lottie/analysis_options.yaml
Normal file
@ -0,0 +1 @@
|
||||
include: package:flame_lint/analysis_options.yaml
|
||||
30
packages/flame_lottie/example/.metadata
Normal file
30
packages/flame_lottie/example/.metadata
Normal file
@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: d9111f64021372856901a1fd5bfbc386cade3318
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: d9111f64021372856901a1fd5bfbc386cade3318
|
||||
base_revision: d9111f64021372856901a1fd5bfbc386cade3318
|
||||
- platform: macos
|
||||
create_revision: d9111f64021372856901a1fd5bfbc386cade3318
|
||||
base_revision: d9111f64021372856901a1fd5bfbc386cade3318
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
8
packages/flame_lottie/example/README.md
Normal file
8
packages/flame_lottie/example/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# flame_lottie example
|
||||
|
||||
An example for using the `flame_lottie` package, a bridge between
|
||||
[Flame](https://flame-engine.org/) 🔥 and *Lottie*.
|
||||
|
||||
The original, native **lottie** library is developed and maintained by [Airbnb](https://github.com/airbnb/lottie-android).
|
||||
|
||||
The Flutter package `lottie` is maintained by **xaha.dev** and is available on pub dev ([link](https://pub.dev/packages/lottie))
|
||||
1
packages/flame_lottie/example/analysis_options.yaml
Normal file
1
packages/flame_lottie/example/analysis_options.yaml
Normal file
@ -0,0 +1 @@
|
||||
include: package:flame_lint/analysis_options.yaml
|
||||
1
packages/flame_lottie/example/assets/LottieLogo1.json
Normal file
1
packages/flame_lottie/example/assets/LottieLogo1.json
Normal file
File diff suppressed because one or more lines are too long
21
packages/flame_lottie/example/lib/main.dart
Normal file
21
packages/flame_lottie/example/lib/main.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame_lottie/flame_lottie.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() async {
|
||||
runApp(GameWidget(game: LottieExampleGame()));
|
||||
}
|
||||
|
||||
class LottieExampleGame extends FlameGame {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final asset = await loadLottie(Lottie.asset('assets/LottieLogo1.json'));
|
||||
add(
|
||||
LottieComponent(
|
||||
asset,
|
||||
size: Vector2.all(400),
|
||||
repeating: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
23
packages/flame_lottie/example/pubspec.yaml
Normal file
23
packages/flame_lottie/example/pubspec.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
name: flame_lottie_example
|
||||
description: An example for flame_lottie. The example shows how load a Lottie animation and add it to a flame game.
|
||||
|
||||
publish_to: 'none'
|
||||
version: 0.0.1+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.2 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
flame: ^1.4.0
|
||||
flame_lottie: ^0.1.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
lottie:
|
||||
|
||||
dev_dependencies:
|
||||
flame_lint: ^0.1.3
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/
|
||||
5
packages/flame_lottie/lib/flame_lottie.dart
Normal file
5
packages/flame_lottie/lib/flame_lottie.dart
Normal file
@ -0,0 +1,5 @@
|
||||
library flame_lottie;
|
||||
|
||||
export 'package:lottie/lottie.dart';
|
||||
export 'src/lottie_component.dart';
|
||||
export 'src/lottie_renderer.dart';
|
||||
71
packages/flame_lottie/lib/src/lottie_component.dart
Normal file
71
packages/flame_lottie/lib/src/lottie_component.dart
Normal file
@ -0,0 +1,71 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame_lottie/src/lottie_renderer.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
/// A Flame [Component] which renders a [Lottie] animation using the already
|
||||
/// existing Flutter library [lottie](https://pub.dev/packages/lottie).
|
||||
class LottieComponent extends PositionComponent with HasPaint {
|
||||
late final LottieRenderer _renderer;
|
||||
|
||||
/// The [controller] drives the [Lottie] animation. In case none is specified
|
||||
/// it will be created implicitly in the [LottieRenderer].
|
||||
LottieComponent(
|
||||
LottieComposition composition, {
|
||||
EffectController? controller,
|
||||
double? progress,
|
||||
LottieDelegates? delegates,
|
||||
bool? enableMergePaths,
|
||||
FrameRate? frameRate,
|
||||
double? duration,
|
||||
bool? repeating,
|
||||
Alignment alignment = Alignment.center,
|
||||
BoxFit? fit = BoxFit.contain,
|
||||
super.position,
|
||||
super.size,
|
||||
super.scale,
|
||||
super.angle,
|
||||
super.anchor,
|
||||
super.children,
|
||||
super.priority,
|
||||
}) {
|
||||
_renderer = LottieRenderer(
|
||||
composition: composition,
|
||||
progress: progress ?? 0.0,
|
||||
size: size,
|
||||
controller: controller,
|
||||
duration: duration,
|
||||
repeating: repeating,
|
||||
alignment: alignment,
|
||||
fit: fit,
|
||||
delegates: delegates,
|
||||
enableMergePaths: enableMergePaths,
|
||||
frameRate: frameRate,
|
||||
);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
_renderer.render(canvas);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void update(double dt) {
|
||||
_renderer.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads the Lottie animation from the specified Lottie file.
|
||||
Future<LottieComposition> loadLottie(
|
||||
FutureOr<LottieBuilder> file,
|
||||
) async {
|
||||
final loaded = await file;
|
||||
final composition = await loaded.lottie.load();
|
||||
|
||||
return composition;
|
||||
}
|
||||
55
packages/flame_lottie/lib/src/lottie_renderer.dart
Normal file
55
packages/flame_lottie/lib/src/lottie_renderer.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
class LottieRenderer {
|
||||
final LottieDrawable drawable;
|
||||
final EffectController _controller;
|
||||
|
||||
final BoxFit? fit;
|
||||
final Alignment? alignment;
|
||||
|
||||
late Rect boundingRect;
|
||||
|
||||
LottieRenderer({
|
||||
required LottieComposition composition,
|
||||
required double progress,
|
||||
required NotifyingVector2 size,
|
||||
EffectController? controller,
|
||||
double? duration,
|
||||
bool? repeating,
|
||||
this.alignment,
|
||||
this.fit,
|
||||
LottieDelegates? delegates,
|
||||
bool? enableMergePaths,
|
||||
FrameRate? frameRate,
|
||||
}) : assert(progress >= 0.0 && progress <= 1.0),
|
||||
boundingRect = size.toRect(),
|
||||
drawable = LottieDrawable(composition)
|
||||
..setProgress(
|
||||
progress,
|
||||
frameRate: frameRate,
|
||||
)
|
||||
..delegates = delegates
|
||||
..enableMergePaths = enableMergePaths ?? false,
|
||||
_controller = controller ??
|
||||
EffectController(
|
||||
duration: duration ?? composition.duration.inSeconds.toDouble(),
|
||||
infinite: repeating ?? false,
|
||||
) {
|
||||
size.addListener(() {
|
||||
boundingRect = size.toRect();
|
||||
});
|
||||
}
|
||||
|
||||
/// Renders the current frame of the Lottie animation onto the canvas.
|
||||
void render(Canvas canvas) {
|
||||
drawable.draw(canvas, boundingRect, fit: fit, alignment: alignment);
|
||||
}
|
||||
|
||||
void update(double dt) {
|
||||
_controller.advance(dt);
|
||||
drawable.setProgress(_controller.progress);
|
||||
}
|
||||
}
|
||||
24
packages/flame_lottie/pubspec.yaml
Normal file
24
packages/flame_lottie/pubspec.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
name: flame_lottie
|
||||
description: Flame wrapper for Lottie by AirBnB. This package implements a bridge between Lottie and Flame, allowing to load and display Lottie animations.
|
||||
version: 0.1.0
|
||||
homepage: https://github.com/flame-engine/flame/tree/main/packages/flame_lottie
|
||||
funding:
|
||||
- https://patreon.com/bluefireoss
|
||||
- https://www.buymeacoffee.com/bluefire
|
||||
|
||||
environment:
|
||||
sdk: '>=2.17.0 <3.0.0'
|
||||
flutter: '>=3.3.0'
|
||||
|
||||
dependencies:
|
||||
flame: ^1.4.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
lottie: ^1.4.3
|
||||
|
||||
dev_dependencies:
|
||||
flame_lint: ^0.1.3
|
||||
flame_test: ^1.8.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
lint: ^1.10.0
|
||||
71
packages/flame_lottie/test/flame_lottie_test.dart
Normal file
71
packages/flame_lottie/test/flame_lottie_test.dart
Normal file
@ -0,0 +1,71 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flame_lottie/flame_lottie.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWithFlameGame(
|
||||
'Game holds LottieComponent',
|
||||
(game) async {
|
||||
// When running tests we have to sync read the file and can not use the
|
||||
// recommended [loadLottie] function.
|
||||
final data = File('example/assets/LottieLogo1.json').readAsBytesSync();
|
||||
final composition = await LottieComposition.fromBytes(data);
|
||||
|
||||
final lottieComponent = LottieComponent(composition);
|
||||
|
||||
await game.add(lottieComponent);
|
||||
await game.ready();
|
||||
|
||||
expect(game.children, isNotEmpty);
|
||||
expect(game.children, [lottieComponent]);
|
||||
},
|
||||
);
|
||||
|
||||
testWithFlameGame(
|
||||
'Load composition as AssetBundle and use loadLottie function by library',
|
||||
(game) async {
|
||||
final logoData =
|
||||
Future.value(bytesForFile('example/assets/LottieLogo1.json'));
|
||||
|
||||
final mockAsset = FakeAssetBundle({'logo.json': logoData});
|
||||
|
||||
LottieComposition? composition;
|
||||
|
||||
final asset = Lottie.asset(
|
||||
'logo.json',
|
||||
bundle: mockAsset,
|
||||
onLoaded: (c) {
|
||||
composition = c;
|
||||
},
|
||||
);
|
||||
|
||||
composition = await loadLottie(asset);
|
||||
|
||||
await game.ready();
|
||||
|
||||
expect(composition, isNotNull);
|
||||
expect(
|
||||
composition!.duration,
|
||||
const Duration(seconds: 5, milliseconds: 966),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ByteData bytesForFile(String path) =>
|
||||
File(path).readAsBytesSync().buffer.asByteData();
|
||||
|
||||
class FakeAssetBundle extends Fake implements AssetBundle {
|
||||
final Map<String, Future<ByteData>> data;
|
||||
|
||||
FakeAssetBundle(this.data);
|
||||
|
||||
@override
|
||||
Future<ByteData> load(String key) {
|
||||
return data[key] ?? (Future.error('Asset $key not found'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user