Expose LottieDelegates to modify animation properties at runtime (#23)
6
.github/workflows/analyze-and-test.yaml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
flutter: ['stable', 'dev']
|
flutter: ['stable', 'dev']
|
||||||
runs-on: ubuntu-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: subosito/flutter-action@v1
|
- uses: subosito/flutter-action@v1
|
||||||
@ -19,8 +19,12 @@ jobs:
|
|||||||
channel: ${{ matrix.flutter }}
|
channel: ${{ matrix.flutter }}
|
||||||
- run: flutter doctor
|
- run: flutter doctor
|
||||||
- run: flutter --version
|
- run: flutter --version
|
||||||
|
- run: flutter pub get
|
||||||
|
working-directory: example
|
||||||
- run: flutter analyze
|
- run: flutter analyze
|
||||||
- run: flutter test test # https://github.com/flutter/flutter/issues/20907
|
- run: flutter test test # https://github.com/flutter/flutter/issues/20907
|
||||||
|
- run: flutter test test
|
||||||
|
working-directory: example
|
||||||
- run: flutter pub run tool/prepare_submit.dart
|
- run: flutter pub run tool/prepare_submit.dart
|
||||||
- name: "check for uncommitted changes"
|
- name: "check for uncommitted changes"
|
||||||
run: |
|
run: |
|
||||||
|
2
.gitignore
vendored
@ -4,6 +4,8 @@ _*
|
|||||||
!.gitignore
|
!.gitignore
|
||||||
!.github
|
!.github
|
||||||
|
|
||||||
|
**/failures/*.png
|
||||||
|
|
||||||
*.iml
|
*.iml
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
build/
|
build/
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
## [0.3.0] - 2020-03-02
|
||||||
|
- Add `LottieDelegates` a group of options to customize the lottie animation at runtime.
|
||||||
|
ie: Dynamically modify color, position, size, text... of every elements of the animation.
|
||||||
|
- Integrate latest changes from Lottie-android
|
||||||
|
|
||||||
## [0.2.2] - 2020-02-21
|
## [0.2.2] - 2020-02-21
|
||||||
- Add a [repeat] parameter to specify if the automatic animation should loop.
|
- Add a [repeat] parameter to specify if the automatic animation should loop.
|
||||||
- Add the [animate], [reverse], [repeat] properties on `LottieBuilder`
|
- Add the [animate], [reverse], [repeat] properties on `LottieBuilder`
|
||||||
|
50
README.md
@ -37,7 +37,7 @@ class MyApp extends StatelessWidget {
|
|||||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
|
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
|
||||||
|
|
||||||
// Load an animation and its images from a zip file
|
// Load an animation and its images from a zip file
|
||||||
Lottie.asset('assets/lottiesfiles/angel.zip'),
|
Lottie.asset('assets/lottiefiles/angel.zip'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -46,15 +46,8 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To load an animation from the assets folder, we need to add an `assets` section in the `pubspec.yaml`:
|
|
||||||
```yaml
|
|
||||||
flutter:
|
|
||||||
assets:
|
|
||||||
- assets/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Specify a custom `AnimationController`
|
### Specify a custom `AnimationController`
|
||||||
This example shows how to have full control over the animation by providing your own `AnimationController`.
|
This example shows how to take full control over the animation by providing your own `AnimationController`.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -201,7 +194,9 @@ class _Painter extends CustomPainter {
|
|||||||
var columns = 10;
|
var columns = 10;
|
||||||
for (var i = 0; i < frameCount; i++) {
|
for (var i = 0; i < frameCount; i++) {
|
||||||
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
|
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
|
||||||
drawable.draw(canvas, destRect, progress: i / frameCount);
|
drawable
|
||||||
|
..setProgress(i / frameCount)
|
||||||
|
..draw(canvas, destRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,14 +207,45 @@ class _Painter extends CustomPainter {
|
|||||||
}
|
}
|
||||||
````
|
````
|
||||||
|
|
||||||
|
### Modify properties at runtime
|
||||||
|
This example shows how to modify some properties of the animation at runtime. Here we change the text,
|
||||||
|
the color, the opacity and the position of some layers.
|
||||||
|
For each `ValueDelegate` we can either provide a static `value` or a `callback` to compute a value for a each frame.
|
||||||
|
|
||||||
|
````dart
|
||||||
|
class _Animation extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Lottie.asset(
|
||||||
|
'assets/Tests/Shapes.json',
|
||||||
|
delegates: LottieDelegates(
|
||||||
|
text: (initialText) => translate(initialText),
|
||||||
|
values: [
|
||||||
|
ValueDelegate.color(
|
||||||
|
const ['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
value: Colors.red,
|
||||||
|
),
|
||||||
|
ValueDelegate.opacity(
|
||||||
|
const ['Shape Layer 1', 'Rectangle'],
|
||||||
|
callback: (frameInfo) =>
|
||||||
|
(frameInfo.overallProgress * 100).round(),
|
||||||
|
),
|
||||||
|
ValueDelegate.position(
|
||||||
|
const ['Shape Layer 1', 'Rectangle'],
|
||||||
|
relative: Offset(100, 200),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
This is a new library so usability, documentation and performance are still work in progress.
|
This is a new library so usability, documentation and performance are still work in progress.
|
||||||
|
|
||||||
The following features are not yet implemented:
|
The following features are not yet implemented:
|
||||||
- Dash path effects
|
- Dash path effects
|
||||||
- Transforms on gradients (stroke and fills)
|
- Transforms on gradients (stroke and fills)
|
||||||
- Expose `Value callback` to modify dynamically some properties of the animation
|
|
||||||
- Text in animations has very basic support (unoptimized and buggy)
|
|
||||||
|
|
||||||
## Flutter Web
|
## Flutter Web
|
||||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||||
|
@ -20,15 +20,8 @@ The `Lottie` widget will load the json file and run the animation indefinitely.
|
|||||||
import 'example/lib/examples/main.dart';
|
import 'example/lib/examples/main.dart';
|
||||||
```
|
```
|
||||||
|
|
||||||
To load an animation from the assets folder, we need to add an `assets` section in the `pubspec.yaml`:
|
|
||||||
```yaml
|
|
||||||
flutter:
|
|
||||||
assets:
|
|
||||||
- assets/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Specify a custom `AnimationController`
|
### Specify a custom `AnimationController`
|
||||||
This example shows how to have full control over the animation by providing your own `AnimationController`.
|
This example shows how to take full control over the animation by providing your own `AnimationController`.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'example/lib/examples/animation_controller.dart';
|
import 'example/lib/examples/animation_controller.dart';
|
||||||
@ -69,14 +62,21 @@ a specific position and size.
|
|||||||
import 'example/lib/examples/custom_draw.dart#example';
|
import 'example/lib/examples/custom_draw.dart#example';
|
||||||
````
|
````
|
||||||
|
|
||||||
|
### Modify properties at runtime
|
||||||
|
This example shows how to modify some properties of the animation at runtime. Here we change the text,
|
||||||
|
the color, the opacity and the position of some layers.
|
||||||
|
For each `ValueDelegate` we can either provide a static `value` or a `callback` to compute a value for a each frame.
|
||||||
|
|
||||||
|
````dart
|
||||||
|
import 'example/lib/examples/simple_dynamic_properties.dart#example';
|
||||||
|
````
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
This is a new library so usability, documentation and performance are still work in progress.
|
This is a new library so usability, documentation and performance are still work in progress.
|
||||||
|
|
||||||
The following features are not yet implemented:
|
The following features are not yet implemented:
|
||||||
- Dash path effects
|
- Dash path effects
|
||||||
- Transforms on gradients (stroke and fills)
|
- Transforms on gradients (stroke and fills)
|
||||||
- Expose `Value callback` to modify dynamically some properties of the animation
|
|
||||||
- Text in animations has very basic support (unoptimized and buggy)
|
|
||||||
|
|
||||||
## Flutter Web
|
## Flutter Web
|
||||||
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
Run the app with `flutter run -d Chrome --dart-define=FLUTTER_WEB_USE_SKIA=true --release`
|
||||||
|
BIN
example/assets/fonts/NotoEmoji-Regular.ttf
Normal file
@ -81,7 +81,9 @@ class _Painter extends CustomPainter {
|
|||||||
var columns = 10;
|
var columns = 10;
|
||||||
for (var i = 0; i < frameCount; i++) {
|
for (var i = 0; i < frameCount; i++) {
|
||||||
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
|
var destRect = Offset(i % columns * 50.0, i ~/ 10 * 80.0) & (size / 5);
|
||||||
drawable.draw(canvas, destRect, progress: i / frameCount);
|
drawable
|
||||||
|
..setProgress(i / frameCount)
|
||||||
|
..draw(canvas, destRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
86
example/lib/examples/dynamic_properties.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
runApp(App());
|
||||||
|
}
|
||||||
|
|
||||||
|
class App extends StatefulWidget {
|
||||||
|
const App({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AppState createState() => _AppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppState extends State<App> with TickerProviderStateMixin {
|
||||||
|
Color _color = Colors.green;
|
||||||
|
double _opacity = 0.5;
|
||||||
|
bool _useDelegates = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var valueDelegates = [
|
||||||
|
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
value: _color),
|
||||||
|
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
callback: (_) => (_opacity * 100).round()),
|
||||||
|
];
|
||||||
|
|
||||||
|
return MaterialApp(
|
||||||
|
color: Colors.blue,
|
||||||
|
home: Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Dynamic properties'),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
child: Lottie.asset(
|
||||||
|
'assets/Tests/Shapes.json',
|
||||||
|
delegates: LottieDelegates(
|
||||||
|
values: _useDelegates ? valueDelegates : null),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Checkbox(
|
||||||
|
value: _useDelegates,
|
||||||
|
onChanged: (newValue) {
|
||||||
|
setState(() {
|
||||||
|
_useDelegates = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: _opacity,
|
||||||
|
onChanged: (newOpacity) {
|
||||||
|
setState(() {
|
||||||
|
_opacity = newOpacity;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 500,
|
||||||
|
child: ColorPicker(
|
||||||
|
pickerColor: _color,
|
||||||
|
onColorChanged: (newColor) {
|
||||||
|
setState(() {
|
||||||
|
_color = newColor;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showLabel: false,
|
||||||
|
enableAlpha: false,
|
||||||
|
pickerAreaHeightPercent: 0.8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
72
example/lib/examples/dynamic_text.dart
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
runApp(App());
|
||||||
|
}
|
||||||
|
|
||||||
|
class App extends StatefulWidget {
|
||||||
|
const App({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AppState createState() => _AppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppState extends State<App> with TickerProviderStateMixin {
|
||||||
|
TextEditingController _textController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_textController = TextEditingController(text: /*'🔥Fire🔥'*/ 'Fire');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_textController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
color: Colors.blue,
|
||||||
|
home: Scaffold(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Dynamic text'),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
child: Lottie.asset(
|
||||||
|
'assets/Tests/DynamicText.json',
|
||||||
|
delegates: LottieDelegates(
|
||||||
|
text: (animationText) => _textController.text,
|
||||||
|
textStyle: (font) => TextStyle(
|
||||||
|
fontFamily: font.fontFamily,
|
||||||
|
fontStyle: FontStyle.italic),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 300,
|
||||||
|
child: CupertinoTextField(
|
||||||
|
controller: _textController,
|
||||||
|
onChanged: (newText) {
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
|
|||||||
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
|
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
|
||||||
|
|
||||||
// Load an animation and its images from a zip file
|
// Load an animation and its images from a zip file
|
||||||
Lottie.asset('assets/lottiesfiles/angel.zip'),
|
Lottie.asset('assets/lottiefiles/angel.zip'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
47
example/lib/examples/simple_dynamic_properties.dart
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
void main() => runApp(MyApp());
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: ListView(
|
||||||
|
children: [_Animation()],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String translate(String input) => '**$input**';
|
||||||
|
|
||||||
|
//--- example
|
||||||
|
class _Animation extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Lottie.asset(
|
||||||
|
'assets/Tests/Shapes.json',
|
||||||
|
delegates: LottieDelegates(
|
||||||
|
text: (initialText) => translate(initialText),
|
||||||
|
values: [
|
||||||
|
ValueDelegate.color(
|
||||||
|
const ['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
value: Colors.red,
|
||||||
|
),
|
||||||
|
ValueDelegate.opacity(
|
||||||
|
const ['Shape Layer 1', 'Rectangle'],
|
||||||
|
callback: (frameInfo) =>
|
||||||
|
(frameInfo.overallProgress * 100).round(),
|
||||||
|
),
|
||||||
|
ValueDelegate.position(
|
||||||
|
const ['Shape Layer 1', 'Rectangle'],
|
||||||
|
relative: Offset(100, 200),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//---
|
@ -66,7 +66,6 @@ class _Item extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
//border: Border.all(color: Colors.black12),
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -29,6 +29,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -62,11 +69,25 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_colorpicker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_colorpicker
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
golden_toolkit:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: golden_toolkit
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -87,7 +108,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.2.2"
|
version: "0.3.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -200,3 +221,4 @@ packages:
|
|||||||
version: "3.5.0"
|
version: "3.5.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.7.0 <3.0.0"
|
dart: ">=2.7.0 <3.0.0"
|
||||||
|
flutter: ">=1.5.4 <2.0.0"
|
||||||
|
@ -10,10 +10,12 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
lottie:
|
lottie:
|
||||||
path: ../
|
path: ../
|
||||||
|
flutter_colorpicker:
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
golden_toolkit:
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
pedantic: ^1.9.0
|
pedantic: ^1.9.0
|
||||||
@ -34,33 +36,25 @@ flutter:
|
|||||||
- assets/Images/
|
- assets/Images/
|
||||||
- assets/Images/WeAccept/
|
- assets/Images/WeAccept/
|
||||||
|
|
||||||
# To add assets to your package, add an assets section, like this:
|
fonts:
|
||||||
# assets:
|
- family: Comic Neue
|
||||||
# - images/a_dot_burr.jpeg
|
fonts:
|
||||||
# - images/a_dot_ham.jpeg
|
- asset: assets/fonts/Comic-Neue.ttf
|
||||||
#
|
- family: Helvetica
|
||||||
# For details regarding assets in packages, see
|
fonts:
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
- asset: assets/fonts/Helvetica.ttf
|
||||||
#
|
- family: Helvetica Neue
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
fonts:
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
- asset: assets/fonts/Helvetica-Neue.ttf
|
||||||
|
- family: Open Sans
|
||||||
# To add custom fonts to your package, add a fonts section here,
|
fonts:
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
- asset: assets/fonts/Open-Sans.ttf
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
- family: PT Serif
|
||||||
# list giving the asset and other descriptors for the font. For
|
fonts:
|
||||||
# example:
|
- asset: assets/fonts/PT-Serif.ttf
|
||||||
# fonts:
|
- family: Roboto
|
||||||
# - family: Schyler
|
fonts:
|
||||||
# fonts:
|
- asset: assets/fonts/Roboto.ttf
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
- family: Noto Emoji
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
fonts:
|
||||||
# style: italic
|
- asset: assets/fonts/NotoEmoji-Regular.ttf
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts in packages, see
|
|
||||||
# https://flutter.dev/custom-fonts/#from-packages
|
|
293
example/test/dynamic_properties_test.dart
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/painting.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
LottieComposition composition;
|
||||||
|
|
||||||
|
setUpAll(() async {
|
||||||
|
composition = await LottieComposition.fromBytes(
|
||||||
|
File('assets/Tests/Shapes.json').readAsBytesSync());
|
||||||
|
});
|
||||||
|
|
||||||
|
void testGolden(String description, ValueDelegate delegate,
|
||||||
|
{double progress}) async {
|
||||||
|
var screenshotName = description
|
||||||
|
.toLowerCase()
|
||||||
|
.replaceAll(RegExp('[^a-z0-9 ]'), '')
|
||||||
|
.replaceAll(' ', '_');
|
||||||
|
|
||||||
|
testWidgets(description, (tester) async {
|
||||||
|
var animation =
|
||||||
|
AnimationController(vsync: tester, duration: composition.duration);
|
||||||
|
if (progress != null) {
|
||||||
|
animation.value = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie(
|
||||||
|
composition: composition,
|
||||||
|
controller: animation,
|
||||||
|
delegates: LottieDelegates(values: [delegate]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(find.byType(Lottie),
|
||||||
|
matchesGoldenFile('goldens/dynamic/$screenshotName.png'));
|
||||||
|
|
||||||
|
if (progress == null || progress == 0) {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Lottie(
|
||||||
|
composition: composition,
|
||||||
|
controller: animation,
|
||||||
|
delegates: LottieDelegates(values: []),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(find.byType(Lottie),
|
||||||
|
matchesGoldenFile('goldens/dynamic_without_delegate.png'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Fill color (Green)',
|
||||||
|
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
value: Colors.green),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Fill color (Yellow)',
|
||||||
|
ValueDelegate.color(['Shape Layer 1', 'Rectangle', 'Fill 1'],
|
||||||
|
value: Colors.yellow),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Fill opacity',
|
||||||
|
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Fill 1'], value: 50),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Stroke color',
|
||||||
|
ValueDelegate.strokeColor(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
|
||||||
|
value: Colors.green),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Stroke width',
|
||||||
|
ValueDelegate.strokeWidth(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
|
||||||
|
value: 50),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Stroke opacity',
|
||||||
|
ValueDelegate.opacity(['Shape Layer 1', 'Rectangle', 'Stroke 1'],
|
||||||
|
value: 50),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform anchor point',
|
||||||
|
ValueDelegate.transformAnchorPoint(['Shape Layer 1', 'Rectangle'],
|
||||||
|
value: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform position',
|
||||||
|
ValueDelegate.transformPosition(['Shape Layer 1', 'Rectangle'],
|
||||||
|
value: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform position (relative)',
|
||||||
|
ValueDelegate.transformPosition(['Shape Layer 1', 'Rectangle'],
|
||||||
|
relative: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform opacity',
|
||||||
|
ValueDelegate.transformOpacity(['Shape Layer 1', 'Rectangle'], value: 50),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform rotation',
|
||||||
|
ValueDelegate.transformRotation(['Shape Layer 1', 'Rectangle'], value: 45),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Transform scale',
|
||||||
|
ValueDelegate.transformScale(['Shape Layer 1', 'Rectangle'],
|
||||||
|
value: Offset(0.5, 0.5)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Rectangle corner roundedness',
|
||||||
|
ValueDelegate.cornerRadius(
|
||||||
|
['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
|
||||||
|
value: 7),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Rectangle position',
|
||||||
|
ValueDelegate.position(['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
|
||||||
|
relative: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Rectangle size',
|
||||||
|
ValueDelegate.rectangleSize(
|
||||||
|
['Shape Layer 1', 'Rectangle', 'Rectangle Path 1'],
|
||||||
|
relative: Offset(30, 40)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Ellipse position',
|
||||||
|
ValueDelegate.position(['Shape Layer 1', 'Ellipse', 'Ellipse Path 1'],
|
||||||
|
relative: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Ellipse size',
|
||||||
|
ValueDelegate.ellipseSize(['Shape Layer 1', 'Ellipse', 'Ellipse Path 1'],
|
||||||
|
relative: Offset(40, 60)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star points',
|
||||||
|
ValueDelegate.polystarPoints(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 8),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star rotation',
|
||||||
|
ValueDelegate.polystarRotation(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star position',
|
||||||
|
ValueDelegate.position(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
relative: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star inner radius',
|
||||||
|
ValueDelegate.polystarInnerRadius(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star inner roundedness',
|
||||||
|
ValueDelegate.polystarInnerRoundedness(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 100),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star outer radius',
|
||||||
|
ValueDelegate.polystarOuterRadius(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 60),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Star outer roundedness',
|
||||||
|
ValueDelegate.polystarOuterRoundedness(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 100),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Polygon points',
|
||||||
|
ValueDelegate.polystarPoints(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 8),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Polygon rotation',
|
||||||
|
ValueDelegate.polystarRotation(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 10),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Polygon position',
|
||||||
|
ValueDelegate.position(['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
relative: Offset(20, 20)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Polygon radius',
|
||||||
|
ValueDelegate.polystarOuterRadius(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
relative: 60),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Polygon roundedness',
|
||||||
|
ValueDelegate.polystarOuterRoundedness(
|
||||||
|
['Shape Layer 1', 'Star', 'Polystar Path 1'],
|
||||||
|
value: 100),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Repeater transform position',
|
||||||
|
ValueDelegate.transformPosition(
|
||||||
|
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
|
||||||
|
relative: Offset(100, 100)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Repeater transform start opacity',
|
||||||
|
ValueDelegate.transformStartOpacity(
|
||||||
|
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
|
||||||
|
value: 25),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Repeater transform end opacity',
|
||||||
|
ValueDelegate.transformEndOpacity(
|
||||||
|
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
|
||||||
|
value: 25),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Repeater transform rotation',
|
||||||
|
ValueDelegate.transformRotation(
|
||||||
|
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
|
||||||
|
value: 45),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Repeater transform scale',
|
||||||
|
ValueDelegate.transformScale(
|
||||||
|
['Shape Layer 1', 'Repeater Shape', 'Repeater 1'],
|
||||||
|
value: Offset(2, 2)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden('Time remapping', ValueDelegate.timeRemap(['Circle 1'], value: 1),
|
||||||
|
progress: 0.1);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Color Filter',
|
||||||
|
ValueDelegate.colorFilter(['**'],
|
||||||
|
value: ColorFilter.mode(Colors.green, BlendMode.srcATop)),
|
||||||
|
);
|
||||||
|
|
||||||
|
testGolden(
|
||||||
|
'Null Color Filter',
|
||||||
|
ValueDelegate.colorFilter(['**'], value: null),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var progress in [0.0, 0.5, 1.0]) {
|
||||||
|
testGolden(
|
||||||
|
'Opacity interpolation ($progress)',
|
||||||
|
ValueDelegate.transformOpacity(['Shape Layer 1', 'Rectangle'],
|
||||||
|
callback: (frameInfo) => lerpDouble(
|
||||||
|
10, 100, Curves.linear.transform(frameInfo.overallProgress))
|
||||||
|
.round()),
|
||||||
|
progress: progress);
|
||||||
|
}
|
||||||
|
}
|
29
example/test/dynamic_text_test.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:lottie/lottie.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Dynamic test', (tester) async {
|
||||||
|
var composition = await LottieComposition.fromBytes(
|
||||||
|
File('assets/Tests/DynamicText.json').readAsBytesSync());
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Lottie(
|
||||||
|
composition: composition,
|
||||||
|
animate: false,
|
||||||
|
delegates: LottieDelegates(
|
||||||
|
text: (input) => '🔥c️🔥👮🏿🔥',
|
||||||
|
textStyle: (font) => TextStyle(
|
||||||
|
fontFamily: 'Roboto', fontFamilyFallback: ['Noto Emoji']),
|
||||||
|
values: []),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
find.byType(Lottie), matchesGoldenFile('goldens/dynamic_text.png'));
|
||||||
|
});
|
||||||
|
}
|
7
example/test/flutter_test_config.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||||
|
|
||||||
|
Future<void> main(FutureOr<void> Function() testMain) async {
|
||||||
|
await loadAppFonts();
|
||||||
|
return testMain();
|
||||||
|
}
|
BIN
example/test/goldens/dynamic/color_filter.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
example/test/goldens/dynamic/ellipse_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/ellipse_size.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
example/test/goldens/dynamic/fill_color_green.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/fill_color_yellow.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/fill_opacity.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/null_color_filter.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/opacity_interpolation_00.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/opacity_interpolation_05.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/opacity_interpolation_10.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/polygon_points.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
example/test/goldens/dynamic/polygon_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/polygon_radius.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
example/test/goldens/dynamic/polygon_rotation.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/polygon_roundedness.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
example/test/goldens/dynamic/rectangle_corner_roundedness.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
example/test/goldens/dynamic/rectangle_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/rectangle_size.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/repeater_transform_end_opacity.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/repeater_transform_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/repeater_transform_rotation.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/repeater_transform_scale.png
Normal file
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/star_inner_radius.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
example/test/goldens/dynamic/star_inner_roundedness.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/star_outer_radius.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
example/test/goldens/dynamic/star_outer_roundedness.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
example/test/goldens/dynamic/star_points.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
example/test/goldens/dynamic/star_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/star_rotation.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/stroke_color.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/stroke_opacity.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/stroke_width.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/time_remapping.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/transform_anchor_point.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/transform_opacity.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/transform_position.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/transform_position_relative.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
example/test/goldens/dynamic/transform_rotation.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
example/test/goldens/dynamic/transform_scale.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
example/test/goldens/dynamic_text.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
example/test/goldens/dynamic_without_delegate.png
Normal file
After Width: | Height: | Size: 37 KiB |
@ -1,12 +1,9 @@
|
|||||||
// This is a basic Flutter widget test.
|
|
||||||
//
|
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// utility that Flutter provides. For example, you can send tap and scroll
|
|
||||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:lottie_example/main.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {});
|
testWidgets('Main sample', (tester) async {
|
||||||
|
await tester.pumpWidget(App());
|
||||||
|
await tester.pump();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
export 'src/composition.dart' show LottieComposition;
|
export 'src/composition.dart' show LottieComposition;
|
||||||
export 'src/lottie.dart' show Lottie;
|
export 'src/lottie.dart' show Lottie;
|
||||||
export 'src/lottie_builder.dart' show LottieBuilder;
|
export 'src/lottie_builder.dart' show LottieBuilder;
|
||||||
export 'src/lottie_drawable.dart' show LottieDrawable;
|
export 'src/lottie_delegates.dart' show LottieDelegates;
|
||||||
|
export 'src/lottie_drawable.dart' show LottieDrawable, LottieFontStyle;
|
||||||
export 'src/lottie_image_asset.dart' show LottieImageAsset;
|
export 'src/lottie_image_asset.dart' show LottieImageAsset;
|
||||||
export 'src/raw_lottie.dart' show RawLottie;
|
export 'src/raw_lottie.dart' show RawLottie;
|
||||||
|
export 'src/value_delegate.dart' show ValueDelegate;
|
||||||
|
@ -132,7 +132,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LinearGradient _getLinearGradient() {
|
LinearGradient _getLinearGradient() {
|
||||||
var gradientHash = getGradientHash();
|
var gradientHash = _getGradientHash();
|
||||||
var gradient = _linearGradientCache[gradientHash];
|
var gradient = _linearGradientCache[gradientHash];
|
||||||
if (gradient != null) {
|
if (gradient != null) {
|
||||||
return gradient;
|
return gradient;
|
||||||
@ -152,7 +152,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RadialGradient _getRadialGradient() {
|
RadialGradient _getRadialGradient() {
|
||||||
var gradientHash = getGradientHash();
|
var gradientHash = _getGradientHash();
|
||||||
var gradient = _radialGradientCache[gradientHash];
|
var gradient = _radialGradientCache[gradientHash];
|
||||||
if (gradient != null) {
|
if (gradient != null) {
|
||||||
return gradient;
|
return gradient;
|
||||||
@ -176,7 +176,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
|
|||||||
return gradient;
|
return gradient;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getGradientHash() {
|
int _getGradientHash() {
|
||||||
var startPointProgress =
|
var startPointProgress =
|
||||||
(_startPointAnimation.progress * _cacheSteps).round();
|
(_startPointAnimation.progress * _cacheSteps).round();
|
||||||
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();
|
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import '../../value/keyframe.dart';
|
|
||||||
import '../../value/scale_xy.dart';
|
|
||||||
import 'keyframe_animation.dart';
|
|
||||||
|
|
||||||
class ScaleKeyframeAnimation extends KeyframeAnimation<ScaleXY> {
|
|
||||||
ScaleKeyframeAnimation(List<Keyframe<ScaleXY>> keyframes) : super(keyframes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
ScaleXY getValue(Keyframe<ScaleXY> keyframe, double keyframeProgress) {
|
|
||||||
if (keyframe.startValue == null || keyframe.endValue == null) {
|
|
||||||
throw StateError('Missing values for keyframe.');
|
|
||||||
}
|
|
||||||
var startTransform = keyframe.startValue;
|
|
||||||
var endTransform = keyframe.endValue;
|
|
||||||
|
|
||||||
if (valueCallback != null) {
|
|
||||||
var value = valueCallback.getValueInternal(
|
|
||||||
keyframe.startFrame,
|
|
||||||
keyframe.endFrame,
|
|
||||||
startTransform,
|
|
||||||
endTransform,
|
|
||||||
keyframeProgress,
|
|
||||||
getLinearCurrentKeyframeProgress(),
|
|
||||||
progress);
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ScaleXY.lerp(startTransform, endTransform, keyframeProgress);
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,6 @@ import '../../model/layer/base_layer.dart';
|
|||||||
import '../../utils.dart';
|
import '../../utils.dart';
|
||||||
import '../../value/keyframe.dart';
|
import '../../value/keyframe.dart';
|
||||||
import '../../value/lottie_value_callback.dart';
|
import '../../value/lottie_value_callback.dart';
|
||||||
import '../../value/scale_xy.dart';
|
|
||||||
import 'base_keyframe_animation.dart';
|
import 'base_keyframe_animation.dart';
|
||||||
import 'double_keyframe_animation.dart';
|
import 'double_keyframe_animation.dart';
|
||||||
import 'value_callback_keyframe_animation.dart';
|
import 'value_callback_keyframe_animation.dart';
|
||||||
@ -37,7 +36,7 @@ class TransformKeyframeAnimation {
|
|||||||
|
|
||||||
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
|
BaseKeyframeAnimation<Offset, Offset> /*?*/ _anchorPoint;
|
||||||
BaseKeyframeAnimation<dynamic, Offset> /*?*/ _position;
|
BaseKeyframeAnimation<dynamic, Offset> /*?*/ _position;
|
||||||
BaseKeyframeAnimation<ScaleXY, ScaleXY> /*?*/ _scale;
|
BaseKeyframeAnimation<Offset, Offset> /*?*/ _scale;
|
||||||
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
|
BaseKeyframeAnimation<double, double> /*?*/ _rotation;
|
||||||
DoubleKeyframeAnimation /*?*/ _skew;
|
DoubleKeyframeAnimation /*?*/ _skew;
|
||||||
DoubleKeyframeAnimation /*?*/ _skewAngle;
|
DoubleKeyframeAnimation /*?*/ _skewAngle;
|
||||||
@ -141,8 +140,8 @@ class TransformKeyframeAnimation {
|
|||||||
|
|
||||||
if (_scale != null) {
|
if (_scale != null) {
|
||||||
final scale = _scale.value;
|
final scale = _scale.value;
|
||||||
if (scale.x != 1 || scale.y != 1) {
|
if (scale.dx != 1 || scale.dy != 1) {
|
||||||
_matrix.scale(scale.x, scale.y);
|
_matrix.scale(scale.dx, scale.dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +168,7 @@ class TransformKeyframeAnimation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (scale != null) {
|
if (scale != null) {
|
||||||
_matrix.scale(scale.x, scale.y);
|
_matrix.scale(scale.dx, scale.dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rotation != null) {
|
if (rotation != null) {
|
||||||
@ -200,9 +199,9 @@ class TransformKeyframeAnimation {
|
|||||||
} else if (property == LottieProperty.transformScale) {
|
} else if (property == LottieProperty.transformScale) {
|
||||||
if (_scale == null) {
|
if (_scale == null) {
|
||||||
_scale = ValueCallbackKeyframeAnimation(
|
_scale = ValueCallbackKeyframeAnimation(
|
||||||
callback as LottieValueCallback<ScaleXY>, ScaleXY.one());
|
callback as LottieValueCallback<Offset>, Offset(1, 1));
|
||||||
} else {
|
} else {
|
||||||
_scale.setValueCallback(callback as LottieValueCallback<ScaleXY>);
|
_scale.setValueCallback(callback as LottieValueCallback<Offset>);
|
||||||
}
|
}
|
||||||
} else if (property == LottieProperty.transformRotation) {
|
} else if (property == LottieProperty.transformRotation) {
|
||||||
if (_rotation == null) {
|
if (_rotation == null) {
|
||||||
|
@ -34,7 +34,8 @@ class ValueCallbackKeyframeAnimation<K, A> extends BaseKeyframeAnimation<K, A> {
|
|||||||
@override
|
@override
|
||||||
A get value {
|
A get value {
|
||||||
return valueCallback.getValueInternal(0.0, 0.0, valueCallbackValue,
|
return valueCallback.getValueInternal(0.0, 0.0, valueCallbackValue,
|
||||||
valueCallbackValue, progress, progress, progress);
|
valueCallbackValue, progress, progress, progress) ??
|
||||||
|
valueCallbackValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -23,16 +23,19 @@ class Lottie extends StatefulWidget {
|
|||||||
bool animate,
|
bool animate,
|
||||||
bool repeat,
|
bool repeat,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
|
this.delegates,
|
||||||
}) : animate = animate ?? true,
|
}) : animate = animate ?? true,
|
||||||
reverse = reverse ?? false,
|
reverse = reverse ?? false,
|
||||||
repeat = repeat ?? true,
|
repeat = repeat ?? true,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
|
||||||
static LottieBuilder asset(String name,
|
static LottieBuilder asset(String name,
|
||||||
{Animation<double> controller,
|
{Animation<double> controller,
|
||||||
bool animate,
|
bool animate,
|
||||||
bool repeat,
|
bool repeat,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
|
LottieDelegates delegates,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
Key key,
|
Key key,
|
||||||
@ -49,6 +52,7 @@ class Lottie extends StatefulWidget {
|
|||||||
animate: animate,
|
animate: animate,
|
||||||
repeat: repeat,
|
repeat: repeat,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
|
delegates: delegates,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -61,12 +65,14 @@ class Lottie extends StatefulWidget {
|
|||||||
package: package,
|
package: package,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
|
||||||
static LottieBuilder file(
|
static LottieBuilder file(
|
||||||
File file, {
|
File file, {
|
||||||
Animation<double> controller,
|
Animation<double> controller,
|
||||||
bool animate,
|
bool animate,
|
||||||
bool repeat,
|
bool repeat,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
|
LottieDelegates delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -82,6 +88,7 @@ class Lottie extends StatefulWidget {
|
|||||||
animate: animate,
|
animate: animate,
|
||||||
repeat: repeat,
|
repeat: repeat,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
|
delegates: delegates,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -92,12 +99,14 @@ class Lottie extends StatefulWidget {
|
|||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
|
||||||
static LottieBuilder memory(
|
static LottieBuilder memory(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
Animation<double> controller,
|
Animation<double> controller,
|
||||||
bool animate,
|
bool animate,
|
||||||
bool repeat,
|
bool repeat,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
|
LottieDelegates delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -113,6 +122,7 @@ class Lottie extends StatefulWidget {
|
|||||||
animate: animate,
|
animate: animate,
|
||||||
repeat: repeat,
|
repeat: repeat,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
|
delegates: delegates,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -123,12 +133,14 @@ class Lottie extends StatefulWidget {
|
|||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Creates a widget that displays an [LottieComposition] obtained from the network.
|
||||||
static LottieBuilder network(
|
static LottieBuilder network(
|
||||||
String url, {
|
String url, {
|
||||||
Animation<double> controller,
|
Animation<double> controller,
|
||||||
bool animate,
|
bool animate,
|
||||||
bool repeat,
|
bool repeat,
|
||||||
bool reverse,
|
bool reverse,
|
||||||
|
LottieDelegates delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
void Function(LottieComposition) onLoaded,
|
void Function(LottieComposition) onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -144,6 +156,7 @@ class Lottie extends StatefulWidget {
|
|||||||
animate: animate,
|
animate: animate,
|
||||||
repeat: repeat,
|
repeat: repeat,
|
||||||
reverse: reverse,
|
reverse: reverse,
|
||||||
|
delegates: delegates,
|
||||||
imageProviderFactory: imageProviderFactory,
|
imageProviderFactory: imageProviderFactory,
|
||||||
onLoaded: onLoaded,
|
onLoaded: onLoaded,
|
||||||
key: key,
|
key: key,
|
||||||
@ -180,13 +193,13 @@ class Lottie extends StatefulWidget {
|
|||||||
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
/// If non-null, require the Lottie composition to have this width.
|
/// If non-null, requires the composition to have this width.
|
||||||
///
|
///
|
||||||
/// If null, the composition will pick a size that best preserves its intrinsic
|
/// If null, the composition will pick a size that best preserves its intrinsic
|
||||||
/// aspect ratio.
|
/// aspect ratio.
|
||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
/// If non-null, require the Lottie composition to have this height.
|
/// If non-null, require the composition to have this height.
|
||||||
///
|
///
|
||||||
/// If null, the composition will pick a size that best preserves its intrinsic
|
/// If null, the composition will pick a size that best preserves its intrinsic
|
||||||
/// aspect ratio.
|
/// aspect ratio.
|
||||||
@ -215,6 +228,13 @@ class Lottie extends StatefulWidget {
|
|||||||
/// relative to text direction.
|
/// relative to text direction.
|
||||||
final AlignmentGeometry alignment;
|
final AlignmentGeometry alignment;
|
||||||
|
|
||||||
|
/// A group of options to further customize the lottie animation.
|
||||||
|
/// - A [text] delegate to dynamically change some text displayed in the animation
|
||||||
|
/// - A value callback to change the properties of the animation at runtime.
|
||||||
|
/// - A text style factory to map between a font family specified in the animation
|
||||||
|
/// and the font family in your assets.
|
||||||
|
final LottieDelegates delegates;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LottieState createState() => _LottieState();
|
_LottieState createState() => _LottieState();
|
||||||
}
|
}
|
||||||
@ -268,6 +288,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
|
|||||||
animation: _progressAnimation,
|
animation: _progressAnimation,
|
||||||
builder: (context, _) => RawLottie(
|
builder: (context, _) => RawLottie(
|
||||||
composition: widget.composition,
|
composition: widget.composition,
|
||||||
|
delegates: widget.delegates,
|
||||||
progress: _progressAnimation.value,
|
progress: _progressAnimation.value,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
|
@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import '../lottie.dart';
|
import '../lottie.dart';
|
||||||
import 'lottie.dart';
|
import 'lottie.dart';
|
||||||
import 'lottie_drawable.dart';
|
|
||||||
import 'providers/asset_provider.dart';
|
import 'providers/asset_provider.dart';
|
||||||
import 'providers/file_provider.dart';
|
import 'providers/file_provider.dart';
|
||||||
import 'providers/load_image.dart';
|
import 'providers/load_image.dart';
|
||||||
@ -40,6 +39,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
this.animate,
|
this.animate,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
this.repeat,
|
this.repeat,
|
||||||
|
this.delegates,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
this.frameBuilder,
|
this.frameBuilder,
|
||||||
this.width,
|
this.width,
|
||||||
@ -49,7 +49,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
}) : assert(lottie != null),
|
}) : assert(lottie != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// Creates a widget that displays an [LottieStream] obtained from the network.
|
/// Creates a widget that displays an [LottieComposition] obtained from the network.
|
||||||
LottieBuilder.network(
|
LottieBuilder.network(
|
||||||
String src, {
|
String src, {
|
||||||
Map<String, String> headers,
|
Map<String, String> headers,
|
||||||
@ -57,6 +57,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
this.animate,
|
this.animate,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
this.repeat,
|
this.repeat,
|
||||||
|
this.delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -69,13 +70,11 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
headers: headers, imageProviderFactory: imageProviderFactory),
|
headers: headers, imageProviderFactory: imageProviderFactory),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// Creates a widget that displays an [ImageStream] obtained from a [File].
|
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
|
||||||
///
|
|
||||||
/// The [file], [scale], and [repeat] arguments must not be null.
|
|
||||||
///
|
///
|
||||||
/// Either the [width] and [height] arguments should be specified, or the
|
/// Either the [width] and [height] arguments should be specified, or the
|
||||||
/// widget should be placed in a context that sets tight layout constraints.
|
/// widget should be placed in a context that sets tight layout constraints.
|
||||||
/// Otherwise, the image dimensions will change as the image is loaded, which
|
/// Otherwise, the image dimensions will change as the animation is loaded, which
|
||||||
/// will result in ugly layout changes.
|
/// will result in ugly layout changes.
|
||||||
///
|
///
|
||||||
/// On Android, this may require the
|
/// On Android, this may require the
|
||||||
@ -87,6 +86,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
this.animate,
|
this.animate,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
this.repeat,
|
this.repeat,
|
||||||
|
this.delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -98,12 +98,14 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory),
|
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
|
||||||
LottieBuilder.asset(
|
LottieBuilder.asset(
|
||||||
String name, {
|
String name, {
|
||||||
this.controller,
|
this.controller,
|
||||||
this.animate,
|
this.animate,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
this.repeat,
|
this.repeat,
|
||||||
|
this.delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -120,13 +122,14 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
imageProviderFactory: imageProviderFactory),
|
imageProviderFactory: imageProviderFactory),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// Creates a widget that displays an [LottieDrawable] obtained from a [Uint8List].
|
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
|
||||||
LottieBuilder.memory(
|
LottieBuilder.memory(
|
||||||
Uint8List bytes, {
|
Uint8List bytes, {
|
||||||
this.controller,
|
this.controller,
|
||||||
this.animate,
|
this.animate,
|
||||||
this.reverse,
|
this.reverse,
|
||||||
this.repeat,
|
this.repeat,
|
||||||
|
this.delegates,
|
||||||
LottieImageProviderFactory imageProviderFactory,
|
LottieImageProviderFactory imageProviderFactory,
|
||||||
this.onLoaded,
|
this.onLoaded,
|
||||||
Key key,
|
Key key,
|
||||||
@ -139,7 +142,7 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
|
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// The lottie animation to display.
|
/// The lottie animation to load.
|
||||||
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
|
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
|
||||||
final LottieProvider lottie;
|
final LottieProvider lottie;
|
||||||
|
|
||||||
@ -170,6 +173,13 @@ class LottieBuilder extends StatefulWidget {
|
|||||||
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
/// The property has no effect if [animate] is false, [repeat] is false or [controller] is not null.
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
||||||
|
/// A group of options to further customize the lottie animation.
|
||||||
|
/// - A [text] delegate to dynamically change some text displayed in the animation
|
||||||
|
/// - A value callback to change the properties of the animation at runtime.
|
||||||
|
/// - A text style factory to map between a font family specified in the animation
|
||||||
|
/// and the font family in your assets.
|
||||||
|
final LottieDelegates delegates;
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
@ -368,6 +378,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
|
|||||||
animate: widget.animate,
|
animate: widget.animate,
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
repeat: widget.repeat,
|
repeat: widget.repeat,
|
||||||
|
delegates: widget.delegates,
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
fit: widget.fit,
|
fit: widget.fit,
|
||||||
|
64
lib/src/lottie_delegates.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'lottie_drawable.dart';
|
||||||
|
import 'value_delegate.dart';
|
||||||
|
|
||||||
|
// TODO(xha): recognize style Bold, Medium, Regular, SemiBold, etc...
|
||||||
|
TextStyle _defaultTextStyleDelegate(LottieFontStyle font) =>
|
||||||
|
TextStyle(fontFamily: font.fontFamily);
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class LottieDelegates {
|
||||||
|
/// Specify a callback to dynamically changes the text displayed in the lottie
|
||||||
|
/// animation.
|
||||||
|
/// For instance, this is useful when you want to translate the text in the animation.
|
||||||
|
final String Function(String) /*?*/ text;
|
||||||
|
|
||||||
|
/// A callback to map between a font family specified in the json animation
|
||||||
|
/// with the font family in your assets.
|
||||||
|
/// This is useful either if:
|
||||||
|
/// - the name of the font in your asset doesn't match the one in the json file.
|
||||||
|
/// - you want to use an other font than the one declared in the json
|
||||||
|
///
|
||||||
|
/// If the callback is null, the font family from the json is used as it.
|
||||||
|
///
|
||||||
|
/// Given an object containing the font family and style specified in the json
|
||||||
|
/// return a configured `TextStyle` that will be used as the base style when
|
||||||
|
/// painting the text.
|
||||||
|
final TextStyle Function(LottieFontStyle) textStyle;
|
||||||
|
|
||||||
|
/// A list of value delegates to dynamically modify the animation
|
||||||
|
/// properties at runtime.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// Lottie.asset(
|
||||||
|
/// 'lottiefile.json',
|
||||||
|
/// delegates: LottieDelegates(
|
||||||
|
/// value: [
|
||||||
|
/// ValueDelegate.color(['lake', 'fill'], value: Colors.blue),
|
||||||
|
/// ValueDelegate.opacity(['**', 'fill'], callback: (frameInfo) => 0.5 * frameInfo.overallProgress),
|
||||||
|
/// ],
|
||||||
|
/// ),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
final List<ValueDelegate> values;
|
||||||
|
|
||||||
|
//TODO(xha): imageDelegate to change the image to display?
|
||||||
|
|
||||||
|
LottieDelegates({
|
||||||
|
this.text,
|
||||||
|
TextStyle Function(LottieFontStyle) textStyle,
|
||||||
|
this.values,
|
||||||
|
}) : textStyle = textStyle ?? _defaultTextStyleDelegate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is LottieDelegates &&
|
||||||
|
text == other.text &&
|
||||||
|
textStyle == other.textStyle &&
|
||||||
|
values == other.values;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashValues(text, textStyle, values);
|
||||||
|
}
|
@ -1,42 +1,65 @@
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
import 'composition.dart';
|
import 'composition.dart';
|
||||||
|
import 'lottie_delegates.dart';
|
||||||
|
import 'model/key_path.dart';
|
||||||
import 'model/layer/composition_layer.dart';
|
import 'model/layer/composition_layer.dart';
|
||||||
import 'parser/layer_parser.dart';
|
import 'parser/layer_parser.dart';
|
||||||
import 'text_delegate.dart';
|
import 'value_delegate.dart';
|
||||||
|
|
||||||
class LottieDrawable {
|
class LottieDrawable {
|
||||||
final LottieComposition composition;
|
final LottieComposition composition;
|
||||||
final _matrix = Matrix4.identity();
|
final _matrix = Matrix4.identity();
|
||||||
CompositionLayer _compositionLayer;
|
CompositionLayer _compositionLayer;
|
||||||
final Size size;
|
final Size size;
|
||||||
TextDelegate /*?*/ textDelegate;
|
LottieDelegates _delegates;
|
||||||
|
bool _isDirty = true;
|
||||||
|
|
||||||
LottieDrawable(this.composition, {this.textDelegate})
|
LottieDrawable(this.composition, {LottieDelegates delegates})
|
||||||
: size = Size(composition.bounds.width.toDouble(),
|
: size = Size(composition.bounds.width.toDouble(),
|
||||||
composition.bounds.height.toDouble()) {
|
composition.bounds.height.toDouble()) {
|
||||||
|
this.delegates = delegates;
|
||||||
_compositionLayer = CompositionLayer(
|
_compositionLayer = CompositionLayer(
|
||||||
this, LayerParser.parse(composition), composition.layers, composition);
|
this, LayerParser.parse(composition), composition.layers, composition);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLayer get compositionLayer => _compositionLayer;
|
|
||||||
|
|
||||||
/// Sets whether to apply opacity to the each layer instead of shape.
|
/// Sets whether to apply opacity to the each layer instead of shape.
|
||||||
///
|
///
|
||||||
/// Opacity is normally applied directly to a shape. In cases where translucent shapes overlap, applying opacity to a layer will be more accurate
|
/// Opacity is normally applied directly to a shape. In cases where translucent
|
||||||
/// at the expense of performance.
|
/// shapes overlap, applying opacity to a layer will be more accurate at the
|
||||||
|
/// expense of performance.
|
||||||
///
|
///
|
||||||
/// The default value is false.
|
/// The default value is false.
|
||||||
///
|
///
|
||||||
/// Note: This process is very expensive. The performance impact will be reduced when hardware acceleration is enabled.
|
/// Note: This process is very expensive. The performance impact will be reduced
|
||||||
|
/// when hardware acceleration is enabled.
|
||||||
bool isApplyingOpacityToLayersEnabled = false;
|
bool isApplyingOpacityToLayersEnabled = false;
|
||||||
|
|
||||||
void invalidateSelf() {}
|
void invalidateSelf() {
|
||||||
|
_isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double get progress => _progress;
|
||||||
|
double _progress = 0.0;
|
||||||
|
bool setProgress(double value) {
|
||||||
|
_isDirty = false;
|
||||||
|
_progress = value;
|
||||||
|
_compositionLayer.setProgress(value);
|
||||||
|
return _isDirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
LottieDelegates get delegates => _delegates;
|
||||||
|
set delegates(LottieDelegates delegates) {
|
||||||
|
delegates ??= LottieDelegates();
|
||||||
|
if (_delegates != delegates) {
|
||||||
|
_delegates = delegates;
|
||||||
|
_updateValueDelegates(delegates.values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool get useTextGlyphs {
|
bool get useTextGlyphs {
|
||||||
return textDelegate == null && composition.characters.isNotEmpty;
|
return delegates.text == null && composition.characters.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Image getImageAsset(String ref) {
|
ui.Image getImageAsset(String ref) {
|
||||||
@ -49,13 +72,54 @@ class LottieDrawable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextStyle getTextStyle(String font, String style) {
|
TextStyle getTextStyle(String font, String style) {
|
||||||
//TODO(xha): allow the user to map Font in the animation with FontFamily loaded for flutter
|
return _delegates
|
||||||
// Support to inherit TextStyle from DefaultTextStyle applied for the Lottie wiget
|
.textStyle(LottieFontStyle(fontFamily: font, style: style));
|
||||||
return TextStyle(fontFamily: font);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(ui.Canvas canvas, ui.Rect rect,
|
List<ValueDelegate> _valueDelegates = <ValueDelegate>[];
|
||||||
{@required double progress, BoxFit fit, Alignment alignment}) {
|
void _updateValueDelegates(List<ValueDelegate> newDelegates) {
|
||||||
|
if (identical(_valueDelegates, newDelegates)) return;
|
||||||
|
|
||||||
|
newDelegates ??= const [];
|
||||||
|
|
||||||
|
var delegates = <ValueDelegate>[];
|
||||||
|
|
||||||
|
for (var newDelegate in newDelegates) {
|
||||||
|
var existingDelegate = _valueDelegates
|
||||||
|
.firstWhere((f) => f.isSameProperty(newDelegate), orElse: () => null);
|
||||||
|
if (existingDelegate != null) {
|
||||||
|
var resolved = internalResolved(existingDelegate);
|
||||||
|
resolved.updateDelegate(newDelegate);
|
||||||
|
delegates.add(existingDelegate);
|
||||||
|
} else {
|
||||||
|
var keyPaths = _resolveKeyPath(KeyPath(newDelegate.keyPath));
|
||||||
|
var resolvedValueDelegate = internalResolve(newDelegate, keyPaths);
|
||||||
|
resolvedValueDelegate.addValueCallback(this);
|
||||||
|
delegates.add(newDelegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var oldDelegate in _valueDelegates) {
|
||||||
|
if (delegates.every((c) => !c.isSameProperty(oldDelegate))) {
|
||||||
|
var resolved = internalResolved(oldDelegate);
|
||||||
|
resolved.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_valueDelegates = delegates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a {@link KeyPath}, potentially with wildcards or globstars and resolve it to a list of
|
||||||
|
/// zero or more actual {@link KeyPath Keypaths} that exist in the current animation.
|
||||||
|
/// <p>
|
||||||
|
/// If you want to set value callbacks for any of these values, it is recommend to use the
|
||||||
|
/// returned {@link KeyPath} objects because they will be internally resolved to their content
|
||||||
|
/// and won't trigger a tree walk of the animation contents when applied.
|
||||||
|
List<KeyPath> _resolveKeyPath(KeyPath keyPath) {
|
||||||
|
var keyPaths = <KeyPath>[];
|
||||||
|
_compositionLayer.resolveKeyPath(keyPath, 0, keyPaths, KeyPath([]));
|
||||||
|
return keyPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(ui.Canvas canvas, ui.Rect rect, {BoxFit fit, Alignment alignment}) {
|
||||||
if (rect.isEmpty) {
|
if (rect.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -79,9 +143,12 @@ class LottieDrawable {
|
|||||||
_matrix.translate(destinationRect.left, destinationRect.top);
|
_matrix.translate(destinationRect.left, destinationRect.top);
|
||||||
_matrix.scale(destinationRect.size.width / sourceRect.width,
|
_matrix.scale(destinationRect.size.width / sourceRect.width,
|
||||||
destinationRect.size.height / sourceRect.height);
|
destinationRect.size.height / sourceRect.height);
|
||||||
progress ??= 0;
|
_compositionLayer.draw(canvas, rect.size, _matrix, parentAlpha: 255);
|
||||||
_compositionLayer
|
|
||||||
..setProgress(progress)
|
|
||||||
..draw(canvas, rect.size, _matrix, parentAlpha: 255);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LottieFontStyle {
|
||||||
|
final String fontFamily, style;
|
||||||
|
|
||||||
|
LottieFontStyle({this.fontFamily, this.style});
|
||||||
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'value/scale_xy.dart';
|
|
||||||
|
|
||||||
/// Property values are the same type as the generic type of their corresponding
|
/// Property values are the same type as the generic type of their corresponding
|
||||||
/// {@link LottieValueCallback}. With this, we can use generics to maintain type safety
|
/// {@link LottieValueCallback}. With this, we can use generics to maintain type safety
|
||||||
@ -62,69 +61,69 @@ abstract class LottieProperty {
|
|||||||
static final int opacity = 4;
|
static final int opacity = 4;
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final Offset transformAnchorPoint = Offset.zero;
|
static final Offset transformAnchorPoint = Offset(5, 5);
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final Offset transformPosition = Offset.zero;
|
static final Offset transformPosition = Offset(6, 6);
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final Offset ellipseSize = Offset.zero;
|
static final Offset ellipseSize = Offset(7, 7);
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final Offset rectangleSize = Offset.zero;
|
static final Offset rectangleSize = Offset(8, 8);
|
||||||
|
|
||||||
/// In degrees */
|
/// In degrees */
|
||||||
static final double cornerRadius = 0.0;
|
static final double cornerRadius = 9.0;
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final Offset position = Offset.zero;
|
static final Offset position = Offset(10, 10);
|
||||||
static final ScaleXY transformScale = ScaleXY.one();
|
static final Offset transformScale = Offset(11, 11);
|
||||||
|
|
||||||
/// In degrees */
|
/// In degrees */
|
||||||
static final double transformRotation = 1.0;
|
static final double transformRotation = 12.0;
|
||||||
|
|
||||||
/// 0-85 */
|
/// 0-85 */
|
||||||
static final double transformSkew = 0.0;
|
static final double transformSkew = 13.0;
|
||||||
|
|
||||||
/// In degrees */
|
/// In degrees */
|
||||||
static final double transformSkewAngle = 0.0;
|
static final double transformSkewAngle = 14.0;
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final double strokeWidth = 2.0;
|
static final double strokeWidth = 15.0;
|
||||||
static final double textTracking = 3.0;
|
static final double textTracking = 16.0;
|
||||||
static final double repeaterCopies = 4.0;
|
static final double repeaterCopies = 17.0;
|
||||||
static final double repeaterOffset = 5.0;
|
static final double repeaterOffset = 18.0;
|
||||||
static final double polystarPoints = 6.0;
|
static final double polystarPoints = 19.0;
|
||||||
|
|
||||||
/// In degrees */
|
/// In degrees */
|
||||||
static final double polystarRotation = 7.0;
|
static final double polystarRotation = 20.0;
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final double polystarInnerRadius = 8.0;
|
static final double polystarInnerRadius = 21.0;
|
||||||
|
|
||||||
/// In Px */
|
/// In Px */
|
||||||
static final double polystarOuterRadius = 9.0;
|
static final double polystarOuterRadius = 22.0;
|
||||||
|
|
||||||
/// [0,100] */
|
/// [0,100] */
|
||||||
static final double polystarInnerRoundedness = 10.0;
|
static final double polystarInnerRoundedness = 23.0;
|
||||||
|
|
||||||
/// [0,100] */
|
/// [0,100] */
|
||||||
static final double polystarOuterRoundedness = 11.0;
|
static final double polystarOuterRoundedness = 24.0;
|
||||||
|
|
||||||
/// [0,100] */
|
/// [0,100] */
|
||||||
static final double transformStartOpacity = 12.0;
|
static final double transformStartOpacity = 25.0;
|
||||||
|
|
||||||
/// [0,100] */
|
/// [0,100] */
|
||||||
static final double transformEndOpacity = 12.1;
|
static final double transformEndOpacity = 26.0;
|
||||||
|
|
||||||
/// The time value in seconds */
|
/// The time value in seconds */
|
||||||
static final double timeRemap = 13.0;
|
static final double timeRemap = 27.0;
|
||||||
|
|
||||||
/// In Dp */
|
/// In Dp
|
||||||
static final double textSize = 14.0;
|
static final double textSize = 28.0;
|
||||||
|
|
||||||
static final ColorFilter colorFilter =
|
static final ColorFilter colorFilter =
|
||||||
ColorFilter.mode(Color(0xFF000000), BlendMode.dst);
|
ColorFilter.mode(Color(0xFF000000), BlendMode.dst);
|
||||||
|
|
||||||
static final List<Color> gradientColor = const [];
|
static final List<Color> gradientColor = [];
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
|
import 'dart:ui';
|
||||||
import '../../animation/keyframe/base_keyframe_animation.dart';
|
import '../../animation/keyframe/base_keyframe_animation.dart';
|
||||||
import '../../animation/keyframe/scale_keyframe_animation.dart';
|
import '../../animation/keyframe/point_keyframe_animation.dart';
|
||||||
import '../../value/keyframe.dart';
|
import '../../value/keyframe.dart';
|
||||||
import '../../value/scale_xy.dart';
|
|
||||||
import 'base_animatable_value.dart';
|
import 'base_animatable_value.dart';
|
||||||
|
|
||||||
class AnimatableScaleValue extends BaseAnimatableValue<ScaleXY, ScaleXY> {
|
class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> {
|
||||||
AnimatableScaleValue.one() : this(ScaleXY.one());
|
AnimatableScaleValue.one() : this(Offset(1, 1));
|
||||||
|
|
||||||
AnimatableScaleValue(ScaleXY value) : super.fromValue(value);
|
AnimatableScaleValue(Offset value) : super.fromValue(value);
|
||||||
|
|
||||||
AnimatableScaleValue.fromKeyframes(List<Keyframe<ScaleXY>> keyframes)
|
AnimatableScaleValue.fromKeyframes(List<Keyframe<Offset>> keyframes)
|
||||||
: super.fromKeyframes(keyframes);
|
: super.fromKeyframes(keyframes);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseKeyframeAnimation<ScaleXY, ScaleXY> createAnimation() {
|
BaseKeyframeAnimation<Offset, Offset> createAnimation() {
|
||||||
return ScaleKeyframeAnimation(keyframes);
|
return PointKeyframeAnimation(keyframes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
import 'package:characters/characters.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
import '../../animation/content/content_group.dart';
|
import '../../animation/content/content_group.dart';
|
||||||
@ -22,7 +23,6 @@ class TextLayer extends BaseLayer {
|
|||||||
final _matrix = Matrix4.identity();
|
final _matrix = Matrix4.identity();
|
||||||
final _fillPaint = Paint()..style = PaintingStyle.fill;
|
final _fillPaint = Paint()..style = PaintingStyle.fill;
|
||||||
final _strokePaint = Paint()..style = PaintingStyle.stroke;
|
final _strokePaint = Paint()..style = PaintingStyle.stroke;
|
||||||
TextStyle _textStyle;
|
|
||||||
final _contentsForCharacter = <FontCharacter, List<ContentGroup>>{};
|
final _contentsForCharacter = <FontCharacter, List<ContentGroup>>{};
|
||||||
final TextKeyframeAnimation _textAnimation;
|
final TextKeyframeAnimation _textAnimation;
|
||||||
final LottieComposition _composition;
|
final LottieComposition _composition;
|
||||||
@ -227,14 +227,14 @@ class TextLayer extends BaseLayer {
|
|||||||
void _drawTextWithFont(DocumentData documentData, Font font,
|
void _drawTextWithFont(DocumentData documentData, Font font,
|
||||||
Matrix4 parentMatrix, Canvas canvas) {
|
Matrix4 parentMatrix, Canvas canvas) {
|
||||||
var parentScale = parentMatrix.getScale();
|
var parentScale = parentMatrix.getScale();
|
||||||
_textStyle = lottieDrawable.getTextStyle(font.family, font.style);
|
var textStyle = lottieDrawable.getTextStyle(font.family, font.style);
|
||||||
if (_textStyle == null) {
|
if (textStyle == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var text = documentData.text;
|
var text = documentData.text;
|
||||||
var textDelegate = lottieDrawable.textDelegate;
|
var textDelegate = lottieDrawable.delegates.text;
|
||||||
if (textDelegate != null) {
|
if (textDelegate != null) {
|
||||||
text = textDelegate.getTextInternal(text);
|
text = textDelegate(text);
|
||||||
}
|
}
|
||||||
double textSize;
|
double textSize;
|
||||||
if (_textSizeCallbackAnimation != null) {
|
if (_textSizeCallbackAnimation != null) {
|
||||||
@ -244,8 +244,8 @@ class TextLayer extends BaseLayer {
|
|||||||
} else {
|
} else {
|
||||||
textSize = documentData.size;
|
textSize = documentData.size;
|
||||||
}
|
}
|
||||||
_textStyle =
|
textStyle =
|
||||||
_textStyle.copyWith(fontSize: textSize * window.devicePixelRatio);
|
textStyle.copyWith(fontSize: textSize * window.devicePixelRatio);
|
||||||
|
|
||||||
// Line height
|
// Line height
|
||||||
var lineHeight = documentData.lineHeight * window.devicePixelRatio;
|
var lineHeight = documentData.lineHeight * window.devicePixelRatio;
|
||||||
@ -256,7 +256,7 @@ class TextLayer extends BaseLayer {
|
|||||||
for (var l = 0; l < textLineCount; l++) {
|
for (var l = 0; l < textLineCount; l++) {
|
||||||
var textLine = textLines[l];
|
var textLine = textLines[l];
|
||||||
var textPainter = TextPainter(
|
var textPainter = TextPainter(
|
||||||
text: TextSpan(text: textLine, style: _textStyle),
|
text: TextSpan(text: textLine, style: textStyle),
|
||||||
textDirection: _textDirection);
|
textDirection: _textDirection);
|
||||||
textPainter.layout();
|
textPainter.layout();
|
||||||
var textLineWidth = textPainter.width;
|
var textLineWidth = textPainter.width;
|
||||||
@ -270,7 +270,7 @@ class TextLayer extends BaseLayer {
|
|||||||
canvas.translate(0, translateY);
|
canvas.translate(0, translateY);
|
||||||
|
|
||||||
// Draw each line
|
// Draw each line
|
||||||
_drawFontTextLine(textLine, documentData, canvas, parentScale);
|
_drawFontTextLine(textLine, textStyle, documentData, canvas, parentScale);
|
||||||
|
|
||||||
// Reset canvas
|
// Reset canvas
|
||||||
canvas.transform(parentMatrix.storage);
|
canvas.transform(parentMatrix.storage);
|
||||||
@ -284,14 +284,13 @@ class TextLayer extends BaseLayer {
|
|||||||
return textLinesArray;
|
return textLinesArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawFontTextLine(String text, DocumentData documentData, Canvas canvas,
|
void _drawFontTextLine(String text, TextStyle textStyle,
|
||||||
double parentScale) {
|
DocumentData documentData, Canvas canvas, double parentScale) {
|
||||||
for (var i = 0; i < text.length;) {
|
for (var char in text.characters) {
|
||||||
var charString = _codePointToString(text, i);
|
var charString = char;
|
||||||
i += charString.length;
|
_drawCharacterFromFont(charString, textStyle, documentData, canvas);
|
||||||
_drawCharacterFromFont(charString, documentData, canvas);
|
|
||||||
var textPainter = TextPainter(
|
var textPainter = TextPainter(
|
||||||
text: TextSpan(text: charString, style: _textStyle),
|
text: TextSpan(text: charString, style: textStyle),
|
||||||
textDirection: _textDirection);
|
textDirection: _textDirection);
|
||||||
textPainter.layout();
|
textPainter.layout();
|
||||||
var charWidth = textPainter.width;
|
var charWidth = textPainter.width;
|
||||||
@ -369,18 +368,19 @@ class TextLayer extends BaseLayer {
|
|||||||
canvas.drawPath(path, paint);
|
canvas.drawPath(path, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawCharacterFromFont(
|
void _drawCharacterFromFont(String character, TextStyle textStyle,
|
||||||
String character, DocumentData documentData, Canvas canvas) {
|
DocumentData documentData, Canvas canvas) {
|
||||||
if (documentData.strokeOverFill) {
|
if (documentData.strokeOverFill) {
|
||||||
_drawCharacter(character, _fillPaint, canvas);
|
_drawCharacter(character, textStyle, _fillPaint, canvas);
|
||||||
_drawCharacter(character, _strokePaint, canvas);
|
_drawCharacter(character, textStyle, _strokePaint, canvas);
|
||||||
} else {
|
} else {
|
||||||
_drawCharacter(character, _strokePaint, canvas);
|
_drawCharacter(character, textStyle, _strokePaint, canvas);
|
||||||
_drawCharacter(character, _fillPaint, canvas);
|
_drawCharacter(character, textStyle, _fillPaint, canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawCharacter(String character, Paint paint, Canvas canvas) {
|
void _drawCharacter(
|
||||||
|
String character, TextStyle textStyle, Paint paint, Canvas canvas) {
|
||||||
if (paint.color.alpha == 0) {
|
if (paint.color.alpha == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -388,18 +388,17 @@ class TextLayer extends BaseLayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextStyle textStyle;
|
|
||||||
if (paint.style == PaintingStyle.fill) {
|
if (paint.style == PaintingStyle.fill) {
|
||||||
textStyle = _textStyle.copyWith(foreground: paint);
|
textStyle = textStyle.copyWith(foreground: paint);
|
||||||
} else if (paint.style == PaintingStyle.stroke) {
|
} else if (paint.style == PaintingStyle.stroke) {
|
||||||
textStyle = _textStyle.copyWith(background: paint);
|
textStyle = textStyle.copyWith(background: paint);
|
||||||
}
|
}
|
||||||
var painter = TextPainter(
|
var painter = TextPainter(
|
||||||
text: TextSpan(text: character, style: textStyle),
|
text: TextSpan(text: character, style: textStyle),
|
||||||
textDirection: _textDirection,
|
textDirection: _textDirection,
|
||||||
);
|
);
|
||||||
painter.layout();
|
painter.layout();
|
||||||
painter.paint(canvas, Offset(0, -_textStyle.fontSize));
|
painter.paint(canvas, Offset(0, -textStyle.fontSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ContentGroup> _getContentsForCharacter(FontCharacter character) {
|
List<ContentGroup> _getContentsForCharacter(FontCharacter character) {
|
||||||
@ -417,11 +416,6 @@ class TextLayer extends BaseLayer {
|
|||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(xha): use package:character to correctly iterate over visual glyphs
|
|
||||||
String _codePointToString(String text, int startIndex) {
|
|
||||||
return text[startIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void addValueCallback<T>(T property, LottieValueCallback<T> /*?*/ callback) {
|
void addValueCallback<T>(T property, LottieValueCallback<T> /*?*/ callback) {
|
||||||
super.addValueCallback(property, callback);
|
super.addValueCallback(property, callback);
|
||||||
|
@ -170,7 +170,8 @@ class AnimatableTransformParser {
|
|||||||
|
|
||||||
static bool isScaleIdentity(AnimatableScaleValue scale) {
|
static bool isScaleIdentity(AnimatableScaleValue scale) {
|
||||||
return scale == null ||
|
return scale == null ||
|
||||||
(scale.isStatic && scale.keyframes.first.startValue.equals(1.0, 1.0));
|
(scale.isStatic &&
|
||||||
|
scale.keyframes.first.startValue == Offset(1.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isSkewIdentity(AnimatableDoubleValue skew) {
|
static bool isSkewIdentity(AnimatableDoubleValue skew) {
|
||||||
|
@ -14,7 +14,7 @@ Color colorParser(JsonReader reader, {double scale}) {
|
|||||||
reader.endArray();
|
reader.endArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r <= 1 && g <= 1 && b <= 11) {
|
if (r <= 1 && g <= 1 && b <= 1) {
|
||||||
r *= 255;
|
r *= 255;
|
||||||
g *= 255;
|
g *= 255;
|
||||||
b *= 255;
|
b *= 255;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import '../value/scale_xy.dart';
|
import 'dart:ui';
|
||||||
import 'moshi/json_reader.dart';
|
import 'moshi/json_reader.dart';
|
||||||
|
|
||||||
ScaleXY scaleXYParser(JsonReader reader, {double scale}) {
|
Offset scaleXYParser(JsonReader reader, {double scale}) {
|
||||||
var isArray = reader.peek() == Token.beginArray;
|
var isArray = reader.peek() == Token.beginArray;
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
@ -14,5 +14,5 @@ ScaleXY scaleXYParser(JsonReader reader, {double scale}) {
|
|||||||
if (isArray) {
|
if (isArray) {
|
||||||
reader.endArray();
|
reader.endArray();
|
||||||
}
|
}
|
||||||
return ScaleXY(sx / 100.0 * scale, sy / 100.0 * scale);
|
return Offset(sx / 100.0 * scale, sy / 100.0 * scale);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ class RawLottie extends LeafRenderObjectWidget {
|
|||||||
const RawLottie({
|
const RawLottie({
|
||||||
Key key,
|
Key key,
|
||||||
this.composition,
|
this.composition,
|
||||||
|
this.delegates,
|
||||||
double progress,
|
double progress,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
@ -24,6 +25,9 @@ class RawLottie extends LeafRenderObjectWidget {
|
|||||||
/// The Lottie composition to display.
|
/// The Lottie composition to display.
|
||||||
final LottieComposition composition;
|
final LottieComposition composition;
|
||||||
|
|
||||||
|
/// Allows to modify the Lottie animation at runtime
|
||||||
|
final LottieDelegates delegates;
|
||||||
|
|
||||||
/// The progress of the Lottie animation (between 0.0 and 1.0).
|
/// The progress of the Lottie animation (between 0.0 and 1.0).
|
||||||
final double progress;
|
final double progress;
|
||||||
|
|
||||||
@ -66,6 +70,7 @@ class RawLottie extends LeafRenderObjectWidget {
|
|||||||
RenderLottie createRenderObject(BuildContext context) {
|
RenderLottie createRenderObject(BuildContext context) {
|
||||||
return RenderLottie(
|
return RenderLottie(
|
||||||
composition: composition,
|
composition: composition,
|
||||||
|
delegates: delegates,
|
||||||
progress: progress,
|
progress: progress,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
@ -77,8 +82,7 @@ class RawLottie extends LeafRenderObjectWidget {
|
|||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, RenderLottie renderObject) {
|
void updateRenderObject(BuildContext context, RenderLottie renderObject) {
|
||||||
renderObject
|
renderObject
|
||||||
..composition = composition
|
..setComposition(composition, progress: progress, delegates: delegates)
|
||||||
..progress = progress
|
|
||||||
..width = width
|
..width = width
|
||||||
..height = height
|
..height = height
|
||||||
..alignment = alignment
|
..alignment = alignment
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
import '../lottie.dart';
|
import '../lottie.dart';
|
||||||
import 'lottie_drawable.dart';
|
import 'lottie_drawable.dart';
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ import 'lottie_drawable.dart';
|
|||||||
class RenderLottie extends RenderBox {
|
class RenderLottie extends RenderBox {
|
||||||
RenderLottie({
|
RenderLottie({
|
||||||
LottieComposition composition,
|
LottieComposition composition,
|
||||||
|
LottieDelegates delegates,
|
||||||
double progress = 0.0,
|
double progress = 0.0,
|
||||||
double width,
|
double width,
|
||||||
double height,
|
double height,
|
||||||
@ -16,37 +18,49 @@ class RenderLottie extends RenderBox {
|
|||||||
AlignmentGeometry alignment = Alignment.center,
|
AlignmentGeometry alignment = Alignment.center,
|
||||||
}) : assert(alignment != null),
|
}) : assert(alignment != null),
|
||||||
assert(progress != null && progress >= 0.0 && progress <= 1.0),
|
assert(progress != null && progress >= 0.0 && progress <= 1.0),
|
||||||
_composition = composition,
|
_drawable = composition != null
|
||||||
_progress = progress,
|
? (LottieDrawable(composition)
|
||||||
|
..setProgress(progress)
|
||||||
|
..delegates = delegates)
|
||||||
|
: null,
|
||||||
_width = width,
|
_width = width,
|
||||||
_height = height,
|
_height = height,
|
||||||
_fit = fit,
|
_fit = fit,
|
||||||
_alignment = alignment;
|
_alignment = alignment;
|
||||||
|
|
||||||
/// The lottie composition to display.
|
/// The lottie composition to display.
|
||||||
LottieComposition get composition => _composition;
|
LottieComposition get composition => _drawable?.composition;
|
||||||
LottieComposition _composition;
|
LottieDrawable _drawable;
|
||||||
set composition(LottieComposition value) {
|
void setComposition(LottieComposition composition,
|
||||||
if (value == _composition) {
|
{@required double progress, @required LottieDelegates delegates}) {
|
||||||
return;
|
var needsLayout = false;
|
||||||
}
|
var needsPaint = false;
|
||||||
_composition = value;
|
if (composition == null) {
|
||||||
_drawable = null;
|
_drawable = null;
|
||||||
markNeedsPaint();
|
needsPaint = true;
|
||||||
if (_width == null || _height == null) {
|
needsLayout = true;
|
||||||
markNeedsLayout();
|
} else {
|
||||||
|
if (_drawable?.composition != composition) {
|
||||||
|
_drawable = LottieDrawable(composition);
|
||||||
|
needsLayout = true;
|
||||||
|
needsPaint = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
needsPaint |= _drawable.setProgress(progress);
|
||||||
|
|
||||||
|
if (_drawable.delegates != delegates) {
|
||||||
|
_drawable.delegates = delegates;
|
||||||
|
needsPaint = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double get progress => _progress;
|
if (needsPaint) {
|
||||||
double _progress;
|
markNeedsPaint();
|
||||||
set progress(double value) {
|
|
||||||
if (value == _progress) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
_progress = value;
|
if (needsLayout && (_width == null || _height == null)) {
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// If non-null, requires the composition to have this width.
|
/// If non-null, requires the composition to have this width.
|
||||||
///
|
///
|
||||||
@ -116,14 +130,12 @@ class RenderLottie extends RenderBox {
|
|||||||
height: _height,
|
height: _height,
|
||||||
).enforce(constraints);
|
).enforce(constraints);
|
||||||
|
|
||||||
if (_composition == null) {
|
if (_drawable == null) {
|
||||||
return constraints.smallest;
|
return constraints.smallest;
|
||||||
}
|
}
|
||||||
|
|
||||||
return constraints.constrainSizeAndAttemptToPreserveAspectRatio(Size(
|
return constraints
|
||||||
_composition.bounds.width.toDouble(),
|
.constrainSizeAndAttemptToPreserveAspectRatio(_drawable.size);
|
||||||
_composition.bounds.height.toDouble(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -168,17 +180,12 @@ class RenderLottie extends RenderBox {
|
|||||||
size = _sizeForConstraints(constraints);
|
size = _sizeForConstraints(constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
LottieDrawable _drawable;
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
if (_composition == null) return;
|
if (_drawable == null) return;
|
||||||
|
|
||||||
_drawable ??= LottieDrawable(_composition);
|
|
||||||
|
|
||||||
_drawable.draw(context.canvas, offset & size,
|
_drawable.draw(context.canvas, offset & size,
|
||||||
progress: _progress,
|
fit: _fit, alignment: _alignment.resolve(TextDirection.ltr));
|
||||||
fit: _fit,
|
|
||||||
alignment: _alignment.resolve(TextDirection.ltr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import 'lottie_drawable.dart';
|
|
||||||
|
|
||||||
/// Extend this class to replace animation text with custom text. This can be useful to handle
|
|
||||||
/// translations.
|
|
||||||
///
|
|
||||||
/// The only method you should have to override is {@link #getText(String)}.
|
|
||||||
class TextDelegate {
|
|
||||||
final _stringMap = <String, String>{};
|
|
||||||
final LottieDrawable /*?*/ drawable;
|
|
||||||
bool _cacheText = true;
|
|
||||||
|
|
||||||
TextDelegate(this.drawable);
|
|
||||||
|
|
||||||
/// Override this to replace the animation text with something dynamic. This can be used for
|
|
||||||
/// translations or custom data.
|
|
||||||
String getText(String input) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the text that will be rendered for the given input text.
|
|
||||||
void setText(String input, String output) {
|
|
||||||
_stringMap[input] = output;
|
|
||||||
_invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether or not {@link TextDelegate} will cache (memoize) the results of getText.
|
|
||||||
/// If this isn't necessary then set it to false.
|
|
||||||
void setCacheText(bool cacheText) {
|
|
||||||
_cacheText = cacheText;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invalidates a cached string with the given input.
|
|
||||||
void invalidateText(String input) {
|
|
||||||
_stringMap.remove(input);
|
|
||||||
_invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invalidates all cached strings
|
|
||||||
void invalidateAllText() {
|
|
||||||
_stringMap.clear();
|
|
||||||
_invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
String getTextInternal(String input) {
|
|
||||||
if (_cacheText && _stringMap.containsKey(input)) {
|
|
||||||
return _stringMap[input];
|
|
||||||
}
|
|
||||||
var text = getText(input);
|
|
||||||
if (_cacheText) {
|
|
||||||
_stringMap[input] = text;
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _invalidate() {
|
|
||||||
if (drawable != null) {
|
|
||||||
drawable.invalidateSelf();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
lib/src/value/lottie_relative_double_value_callback.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
import 'lottie_frame_info.dart';
|
||||||
|
|
||||||
|
double Function(LottieFrameInfo<double>) relativeDoubleValueCallback(
|
||||||
|
double offset) {
|
||||||
|
return (LottieFrameInfo<double> frameInfo) {
|
||||||
|
var originalValue = lerpDouble(frameInfo.startValue, frameInfo.endValue,
|
||||||
|
frameInfo.interpolatedKeyframeProgress);
|
||||||
|
|
||||||
|
return originalValue + offset;
|
||||||
|
};
|
||||||
|
}
|
11
lib/src/value/lottie_relative_integer_value_callback.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
import 'lottie_frame_info.dart';
|
||||||
|
|
||||||
|
int Function(LottieFrameInfo<int>) relativeIntegerValueCallback(int offset) {
|
||||||
|
return (LottieFrameInfo<int> frameInfo) {
|
||||||
|
var originalValue = lerpDouble(frameInfo.startValue, frameInfo.endValue,
|
||||||
|
frameInfo.interpolatedKeyframeProgress);
|
||||||
|
|
||||||
|
return (originalValue + offset).round();
|
||||||
|
};
|
||||||
|
}
|
12
lib/src/value/lottie_relative_point_value_callback.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
import 'lottie_frame_info.dart';
|
||||||
|
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) relativeOffsetValueCallback(
|
||||||
|
Offset offset) {
|
||||||
|
return (LottieFrameInfo<Offset> frameInfo) {
|
||||||
|
var point = Offset.lerp(frameInfo.startValue, frameInfo.endValue,
|
||||||
|
frameInfo.interpolatedKeyframeProgress);
|
||||||
|
|
||||||
|
return point.translate(offset.dx, offset.dy);
|
||||||
|
};
|
||||||
|
}
|
@ -4,26 +4,34 @@ import 'lottie_frame_info.dart';
|
|||||||
/// Allows you to set a callback on a resolved {@link com.airbnb.lottie.model.KeyPath} to modify
|
/// Allows you to set a callback on a resolved {@link com.airbnb.lottie.model.KeyPath} to modify
|
||||||
/// its animation values at runtime.
|
/// its animation values at runtime.
|
||||||
class LottieValueCallback<T> {
|
class LottieValueCallback<T> {
|
||||||
BaseKeyframeAnimation /*?*/ animation;
|
LottieValueCallback(this._value);
|
||||||
|
|
||||||
|
BaseKeyframeAnimation /*?*/ _animation;
|
||||||
|
BaseKeyframeAnimation get animation => _animation;
|
||||||
|
|
||||||
/// This can be set with {@link #setValue(Object)} to use a value instead of deferring
|
/// This can be set with {@link #setValue(Object)} to use a value instead of deferring
|
||||||
/// to the callback.
|
/// to the callback.
|
||||||
///*/
|
///*/
|
||||||
T /*?*/ value;
|
T /*?*/ _value;
|
||||||
|
T get value => _value;
|
||||||
|
|
||||||
LottieValueCallback(this.value);
|
T Function(LottieFrameInfo<T>) callback;
|
||||||
|
|
||||||
/// Override this if you haven't set a static value in the constructor or with setValue.
|
/// Override this if you haven't set a static value in the constructor or with setValue.
|
||||||
///
|
///
|
||||||
/// Return null to resort to the default value.
|
/// Return null to resort to the default value.
|
||||||
T getValue(LottieFrameInfo<T> frameInfo) {
|
T getValue(LottieFrameInfo<T> frameInfo) {
|
||||||
|
if (callback != null) {
|
||||||
|
return callback(frameInfo);
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValue(T /*?*/ value) {
|
void setValue(T /*?*/ value) {
|
||||||
this.value = value;
|
_value = value;
|
||||||
if (animation != null) {
|
if (_animation != null) {
|
||||||
animation.notifyListeners();
|
_animation.notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,11 +43,17 @@ class LottieValueCallback<T> {
|
|||||||
double linearKeyframeProgress,
|
double linearKeyframeProgress,
|
||||||
double interpolatedKeyframeProgress,
|
double interpolatedKeyframeProgress,
|
||||||
double overallProgress) {
|
double overallProgress) {
|
||||||
return getValue(LottieFrameInfo(startFrame, endFrame, startValue, endValue,
|
return getValue(LottieFrameInfo<T>(
|
||||||
linearKeyframeProgress, interpolatedKeyframeProgress, overallProgress));
|
startFrame,
|
||||||
|
endFrame,
|
||||||
|
startValue,
|
||||||
|
endValue,
|
||||||
|
linearKeyframeProgress,
|
||||||
|
interpolatedKeyframeProgress,
|
||||||
|
overallProgress));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAnimation(BaseKeyframeAnimation /*?*/ animation) {
|
void setAnimation(BaseKeyframeAnimation /*?*/ animation) {
|
||||||
this.animation = animation;
|
_animation = animation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
//TODO(xha): delete and use Offset
|
|
||||||
class ScaleXY {
|
|
||||||
final double x;
|
|
||||||
final double y;
|
|
||||||
|
|
||||||
ScaleXY(this.x, this.y);
|
|
||||||
ScaleXY.one() : this(1, 1);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(other) {
|
|
||||||
return other is ScaleXY && x == other.x && y == other.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => hashValues(x, y);
|
|
||||||
|
|
||||||
bool equals(double x, double y) => this.x == x && this.y == y;
|
|
||||||
|
|
||||||
static ScaleXY lerp(ScaleXY a, ScaleXY b, double t) =>
|
|
||||||
ScaleXY(lerpDouble(a.x, b.x, t), lerpDouble(a.y, b.y, t));
|
|
||||||
}
|
|
331
lib/src/value_delegate.dart
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'lottie_drawable.dart';
|
||||||
|
import 'lottie_property.dart';
|
||||||
|
import 'model/key_path.dart';
|
||||||
|
import 'value/lottie_frame_info.dart';
|
||||||
|
import 'value/lottie_relative_double_value_callback.dart';
|
||||||
|
import 'value/lottie_relative_integer_value_callback.dart';
|
||||||
|
import 'value/lottie_relative_point_value_callback.dart';
|
||||||
|
import 'value/lottie_value_callback.dart';
|
||||||
|
|
||||||
|
class ValueDelegate<T> {
|
||||||
|
final List<String> keyPath;
|
||||||
|
final T property;
|
||||||
|
final T value;
|
||||||
|
final T Function(LottieFrameInfo<T>) callback;
|
||||||
|
|
||||||
|
ValueDelegate._(this.keyPath, this.property, this.value, this.callback)
|
||||||
|
: assert(value == null || callback == null,
|
||||||
|
"Value and callback can't be both specified.");
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> _offset(
|
||||||
|
List<String> keyPath,
|
||||||
|
Offset property,
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative) {
|
||||||
|
if (relative != null) {
|
||||||
|
assert(callback == null);
|
||||||
|
callback = relativeOffsetValueCallback(relative);
|
||||||
|
}
|
||||||
|
return ValueDelegate<Offset>._(keyPath, property, value, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ValueDelegate<double> _double(
|
||||||
|
List<String> keyPath,
|
||||||
|
double property,
|
||||||
|
double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative) {
|
||||||
|
if (relative != null) {
|
||||||
|
assert(callback == null);
|
||||||
|
callback = relativeDoubleValueCallback(relative);
|
||||||
|
}
|
||||||
|
return ValueDelegate<double>._(keyPath, property, value, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ValueDelegate<int> _int(List<String> keyPath, int property, int value,
|
||||||
|
int Function(LottieFrameInfo<int>) callback, int relative) {
|
||||||
|
if (relative != null) {
|
||||||
|
assert(callback == null);
|
||||||
|
callback = relativeIntegerValueCallback(relative);
|
||||||
|
}
|
||||||
|
return ValueDelegate<int>._(keyPath, property, value, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ValueDelegate<Color> color(List<String> keyPath,
|
||||||
|
{Color value, Color Function(LottieFrameInfo<Color>) callback}) =>
|
||||||
|
ValueDelegate._(keyPath, LottieProperty.color, value, callback);
|
||||||
|
|
||||||
|
static ValueDelegate<Color> strokeColor(List<String> keyPath,
|
||||||
|
{Color value, Color Function(LottieFrameInfo<Color>) callback}) =>
|
||||||
|
ValueDelegate._(keyPath, LottieProperty.strokeColor, value, callback);
|
||||||
|
|
||||||
|
/// Opacity value are 0-100 to match after effects
|
||||||
|
static ValueDelegate<int> transformOpacity(List<String> keyPath,
|
||||||
|
{int value,
|
||||||
|
int Function(LottieFrameInfo<int>) callback,
|
||||||
|
int relative}) =>
|
||||||
|
_int(keyPath, LottieProperty.transformOpacity, value, callback, relative);
|
||||||
|
|
||||||
|
/// Opacity value are 0-100 to match after effects
|
||||||
|
static ValueDelegate<int> opacity(List<String> keyPath,
|
||||||
|
{int value,
|
||||||
|
int Function(LottieFrameInfo<int>) callback,
|
||||||
|
int relative}) =>
|
||||||
|
_int(keyPath, LottieProperty.opacity, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> transformAnchorPoint(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) {
|
||||||
|
return _offset(keyPath, LottieProperty.transformAnchorPoint, value,
|
||||||
|
callback, relative);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> transformPosition(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) =>
|
||||||
|
_offset(
|
||||||
|
keyPath, LottieProperty.transformPosition, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> ellipseSize(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) =>
|
||||||
|
_offset(keyPath, LottieProperty.ellipseSize, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> rectangleSize(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) =>
|
||||||
|
_offset(keyPath, LottieProperty.rectangleSize, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> cornerRadius(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.cornerRadius, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> position(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) =>
|
||||||
|
_offset(keyPath, LottieProperty.position, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<Offset> transformScale(
|
||||||
|
List<String> keyPath, {
|
||||||
|
Offset value,
|
||||||
|
Offset Function(LottieFrameInfo<Offset>) callback,
|
||||||
|
Offset relative,
|
||||||
|
}) =>
|
||||||
|
_offset(
|
||||||
|
keyPath, LottieProperty.transformScale, value, callback, relative);
|
||||||
|
|
||||||
|
/// In degrees
|
||||||
|
static ValueDelegate<double> transformRotation(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(
|
||||||
|
keyPath, LottieProperty.transformRotation, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> transformSkew(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.transformSkew, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> transformSkewAngle(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.transformSkewAngle, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> strokeWidth(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.strokeWidth, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> textTracking(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.textTracking, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> repeaterCopies(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(
|
||||||
|
keyPath, LottieProperty.repeaterCopies, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> repeaterOffset(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(
|
||||||
|
keyPath, LottieProperty.repeaterOffset, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> polystarPoints(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(
|
||||||
|
keyPath, LottieProperty.polystarPoints, value, callback, relative);
|
||||||
|
|
||||||
|
/// In degrees
|
||||||
|
static ValueDelegate<double> polystarRotation(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(
|
||||||
|
keyPath, LottieProperty.polystarRotation, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> polystarInnerRadius(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.polystarInnerRadius, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> polystarOuterRadius(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.polystarOuterRadius, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> polystarInnerRoundedness(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.polystarInnerRoundedness, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> polystarOuterRoundedness(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.polystarOuterRoundedness, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
/// Opacity value are 0-100 to match after effects
|
||||||
|
static ValueDelegate<double> transformStartOpacity(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.transformStartOpacity, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
/// Opacity value are 0-100 to match after effects
|
||||||
|
static ValueDelegate<double> transformEndOpacity(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.transformEndOpacity, value, callback,
|
||||||
|
relative);
|
||||||
|
|
||||||
|
/// The time value in seconds
|
||||||
|
static ValueDelegate<double> timeRemap(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.timeRemap, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<double> textSize(List<String> keyPath,
|
||||||
|
{double value,
|
||||||
|
double Function(LottieFrameInfo<double>) callback,
|
||||||
|
double relative}) =>
|
||||||
|
_double(keyPath, LottieProperty.textSize, value, callback, relative);
|
||||||
|
|
||||||
|
static ValueDelegate<ColorFilter> colorFilter(List<String> keyPath,
|
||||||
|
{ColorFilter value,
|
||||||
|
ColorFilter Function(LottieFrameInfo<ColorFilter>) callback}) =>
|
||||||
|
ValueDelegate._(keyPath, LottieProperty.colorFilter, value, callback);
|
||||||
|
|
||||||
|
static ValueDelegate<List<Color>> gradientColor(List<String> keyPath,
|
||||||
|
{List<Color> value,
|
||||||
|
List<Color> Function(LottieFrameInfo<List<Color>>) callback}) =>
|
||||||
|
ValueDelegate._(keyPath, LottieProperty.gradientColor, value, callback);
|
||||||
|
|
||||||
|
ResolvedValueDelegate<T> _resolved;
|
||||||
|
ResolvedValueDelegate _resolve(List<KeyPath> resolvedPaths) {
|
||||||
|
_resolved = ResolvedValueDelegate<T>(this, resolvedPaths);
|
||||||
|
return _resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSameProperty(ValueDelegate other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
return other is ValueDelegate<T> &&
|
||||||
|
const ListEquality().equals(other.keyPath, keyPath) &&
|
||||||
|
other.property == property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedValueDelegate internalResolved(ValueDelegate valueDelegate) {
|
||||||
|
return valueDelegate._resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedValueDelegate internalResolve(
|
||||||
|
ValueDelegate delegate, List<KeyPath> resolvedPaths) {
|
||||||
|
return delegate._resolve(resolvedPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResolvedValueDelegate<T> {
|
||||||
|
final ValueDelegate<T> valueDelegate;
|
||||||
|
final List<KeyPath> keyPaths;
|
||||||
|
final LottieValueCallback<T> valueCallback;
|
||||||
|
|
||||||
|
ResolvedValueDelegate(this.valueDelegate, this.keyPaths)
|
||||||
|
: valueCallback = LottieValueCallback(valueDelegate.value)
|
||||||
|
..callback = valueDelegate.callback;
|
||||||
|
|
||||||
|
T get property => valueDelegate.property;
|
||||||
|
|
||||||
|
void updateDelegate(ValueDelegate<T> delegate) {
|
||||||
|
valueCallback
|
||||||
|
..setValue(delegate.value)
|
||||||
|
..callback = delegate.callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
valueCallback
|
||||||
|
..setValue(null)
|
||||||
|
..callback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a property callback for the specified {@link KeyPath}. This {@link KeyPath} can resolve
|
||||||
|
/// to multiple contents. In that case, the callbacks's value will apply to all of them.
|
||||||
|
/// <p>
|
||||||
|
/// Internally, this will check if the {@link KeyPath} has already been resolved with
|
||||||
|
/// {@link #resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
|
||||||
|
void addValueCallback(LottieDrawable drawable) {
|
||||||
|
for (var keyPath in keyPaths) {
|
||||||
|
keyPath.resolvedElement.addValueCallback<T>(property, valueCallback);
|
||||||
|
}
|
||||||
|
if (keyPaths.isNotEmpty) {
|
||||||
|
drawable.invalidateSelf();
|
||||||
|
if (property == LottieProperty.timeRemap) {
|
||||||
|
// Time remapping values are read in setProgress. In order for the new value
|
||||||
|
// to apply, we have to re-set the progress with the current progress so that the
|
||||||
|
// time remapping can be reapplied.
|
||||||
|
drawable.setProgress(drawable.progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
pubspec.lock
@ -43,6 +43,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
characters:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -171,7 +178,7 @@ packages:
|
|||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.9.1"
|
||||||
path:
|
path:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -199,7 +206,7 @@ packages:
|
|||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.3"
|
||||||
quiver:
|
quiver:
|
||||||
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.2
|
version: 0.3.0
|
||||||
homepage: https://github.com/xvrh/lottie-flutter
|
homepage: https://github.com/xvrh/lottie-flutter
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -10,6 +10,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
archive: ^2.0.0
|
archive: ^2.0.0
|
||||||
|
characters:
|
||||||
charcode: ^1.0.0
|
charcode: ^1.0.0
|
||||||
logging: ^0.11.0
|
logging: ^0.11.0
|
||||||
meta: ^1.1.8
|
meta: ^1.1.8
|
||||||
|
@ -21,8 +21,9 @@ void main() {
|
|||||||
var recorder = PictureRecorder();
|
var recorder = PictureRecorder();
|
||||||
var canvas = Canvas(recorder);
|
var canvas = Canvas(recorder);
|
||||||
for (var progress = 0; progress <= 100; progress += 20) {
|
for (var progress = 0; progress <= 100; progress += 20) {
|
||||||
drawable.draw(canvas, Rect.fromLTWH(0, 0, 200, 200),
|
drawable
|
||||||
progress: progress / 100);
|
..setProgress(progress / 100)
|
||||||
|
..draw(canvas, Rect.fromLTWH(0, 0, 200, 200));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
BIN
test/golden/dynamic_text/1.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
test/golden/dynamic_text/2.png
Normal file
After Width: | Height: | Size: 23 KiB |