Compare commits

..

19 Commits

Author SHA1 Message Date
a2acfc7bb9 Add enableRenderCache parameter (#317) 2023-11-15 10:50:11 +01:00
3c75400351 Fix wrong usage of DefaultAssetBundle.of (#315) 2023-11-08 09:58:20 +01:00
aef8ef7ee0 Fix bugs & support .tgs & .lottie (#314)
- Fixed varying opacity stops across keyframes in the same gradient
- Fixed rounded corners for non-closed curves
- Allow to load Telegram Stickers (.tgs)

```dart
Lottie.asset(
  'sticker.tgs',
  decoder: LottieComposition.decodeGZip,
)
```

- Expose a hook to customize how to decode zip archives. This is useful
for dotlottie (.lottie) archives when we want
to specify a specific .json file inside the archive

```dart
Lottie.asset(
  'animation.lottie',
  decoder: customDecoder,
);

Future<LottieComposition?> customDecoder(List<int> bytes) {
  return LottieComposition.decodeZip(bytes, filePicker: (files) {
    return files.firstWhere((f) => f.name == 'animations/cat.json');
  });
}
```

- Remove name property from `LottieComposition`
- `imageProviderFactory` is not used in .zip file by default anymore.
To restore the old behaviour, use:
```dart
Future<LottieComposition?> decoder(List<int> bytes) {
  return LottieComposition.decodeZip(bytes, imageProviderFactory: imageProviderFactory);
}

Lottie.asset('anim.json', imageProviderFactory: imageProviderFactory, decoder: decoder)
```
2023-11-08 09:10:25 +01:00
16874cb6fc Fix bugs on building ios example (#311)
1. `Podfile` is too old
2. build name and build number aren't specified
2023-11-02 16:24:38 +01:00
9e9de6d5b6 Bring some latest fixes (#307)
- Fix gradient interpolation for opacity stops beyond the last color
stop
- Add color value callback to solid layer
2023-10-20 15:50:31 +02:00
f19e86d644 Bump actions/checkout from 2 to 4 (#301) 2023-09-12 10:19:54 +02:00
ab9b5833c3 Support loading fonts from zip (#300)
Fixes #299
2023-09-12 10:12:50 +02:00
fe0032a6c2 Auto publish: use XDG_CONFIG_HOME 2023-08-08 12:29:50 +02:00
b0bc35196e Auto publish: update credentials location 2023-08-08 12:20:18 +02:00
0600c44384 Accept List<int> in LottieComposition.fromBytes (#292) 2023-08-08 11:17:12 +02:00
8a9ef53c63 v2.5 2023-07-17 22:08:46 +02:00
6ffeaa603f Allow shape to be null (#291) 2023-07-17 22:06:30 +02:00
08e629adaa Fix TextLayer opacity calculation (#285) 2023-07-17 20:17:35 +02:00
bf3118b4cf Add layer-level opacity option to LottieOptions (#286) 2023-06-16 15:17:07 +02:00
434ae88aa8 Revert Setup github publishing
It doesn't work for flutter packages
2023-06-02 21:43:14 +02:00
6b752d829c Setup github publishing 2023-06-02 21:38:17 +02:00
124ba6997b Allow RoundedCorners name to be null (#283)
Fixes
https://github.com/xvrh/lottie-flutter/issues/270#issuecomment-1573958860
2023-06-02 21:28:52 +02:00
fdc4018f57 Add an example on how to cache animation as List<Image> (#282) 2023-06-02 13:45:22 +02:00
b61ea14116 Upgrade to Flutter 3.10 & update lints (#280) 2023-05-10 23:09:40 +02:00
607 changed files with 26152 additions and 935 deletions

9
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,9 @@
# Dependabot configuration file.
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -11,9 +11,9 @@ jobs:
strategy:
matrix:
flutter: ['stable']
runs-on: macos-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: ${{ matrix.flutter }}
@ -37,7 +37,7 @@ jobs:
name: Check that the web version can compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'

View File

@ -7,23 +7,22 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter pub run tool/publish/comment_dependency_overrides.dart
- run: flutter pub get
- run: flutter pub run tool/publish/check_version.dart ${GITHUB_REF}
- name: Setup credentials
run: |
cat <<EOF > $PUB_CACHE/credentials.json
mkdir -p $XDG_CONFIG_HOME/dart
cat <<EOF > $XDG_CONFIG_HOME/dart/pub-credentials.json
{
"accessToken":"${{ secrets.OAUTH_ACCESS_TOKEN }}",
"refreshToken":"${{ secrets.OAUTH_REFRESH_TOKEN }}",
"tokenEndpoint":"https://accounts.google.com/o/oauth2/token",
"scopes": [ "openid", "https://www.googleapis.com/auth/userinfo.email" ],
"expiration": 1580681402856
"expiration": 1691492965565
}
EOF
- name: Publish package

View File

@ -1,3 +1,85 @@
## 3.0.0-alpha.1
- Add `enableRenderCache` parameter.
```dart
Lottie.asset('assets/complex_animation.json',
enableRenderCache: true,
)
```
It allows to opt into a mode where the frames of the animation are rendered lazily in an offscreen cache.
Subsequent runs of the animation will be very cheap to render.
This is useful is the animation is complex and can consume a lot of energy from the battery.
It's a trade-off to lower the CPU usage at the cost of an increased memory usage.
The render cache is managed internally and will release the memory once the animation is disposed.
The cache is shared between all animations. If 2 `Lottie` widget are rendered at the same size, they will render only
once.
Any change in the configuration of the animation (delegates, frame rate etc...) will clear the cache.
Any change in the size will invalidate the cache. The cache use the final size visible on the screen (with all
transforms applied).
- Allow to load Telegram Stickers (.tgs)
```dart
Lottie.asset(
'sticker.tgs',
decoder: LottieComposition.decodeGZip,
)
```
- Expose a hook to customize how to decode zip archives. This is useful for dotlottie archives (.lottie) when we want
to specify a specific .json file inside the archive
```dart
Lottie.asset(
'animation.lottie',
decoder: customDecoder,
);
Future<LottieComposition?> customDecoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, filePicker: (files) {
return files.firstWhere((f) => f.name == 'animations/cat.json');
});
}
```
- Remove the name property from `LottieComposition`
- `imageProviderFactory` is not used in .zip file by default anymore.
To restore the old behaviour, use:
```dart
Future<LottieComposition?> decoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, imageProviderFactory: imageProviderFactory);
}
Lottie.asset('anim.json', imageProviderFactory: imageProviderFactory, decoder: decoder)
```
- Disable gradient cache optimization when `ValueDelegate.gradientColor` is used
- Use `DefaultAssetBundle.of` in `AssetLottie` before fallback to `rootBundle`
- Add `BuildContext` optional parameter in `LottieProvider.load`
- Fixed varying opacity stops across keyframes in the same gradient
- Fixed rounded corners for non-closed curves
## 2.7.0
- Support loading Fonts from a zip file
- Fix a bug in Text with strokes
- Fix gradient interpolation for opacity stops beyond the last color stop
- Add color value callback to solid layer
## 2.6.0
- Accept `List<int>` instead of `Uint8List` in `LottieComposition.fromBytes`
- Stroke line cap defaults to butt instead of square
## 2.5.0
- Add layer-level opacity option to LottieOptions
- Fix TextLayer opacity calculation
## 2.4.0
- Require minimum Dart 3.0.0 and Flutter 3.10.0
- Fix a parsing bug when the name property in RoundedCorner was null
## 2.3.2
- Fix a bug when running on the web due to [bitwise operations difference](https://dart.dev/guides/language/numbers#bitwise-operations).

View File

@ -245,6 +245,72 @@ class _Animation extends StatelessWidget {
}
````
### Frame rate
By default, the animation is played at the frame rate exported by AfterEffect.
This is the most power-friendly as generally the animation is exported at 10 or 30 FPS compared to the phone's 60 or 120 FPS.
If the result is not good, you can change the frame rate
````dart
Lottie.asset('anim.json',
// Use the device frame rate (up to 120FPS)
frameRate: FrameRate.max,
// Use the exported frame rate (default)
frameRate: FrameRate.composition,
// Specific frame rate
frameRate: FrameRate(10),
)
````
### Telegram Stickers (.tgs) and DotLottie (.lottie)
TGS file can be loaded by providing a special decoder
````dart
Widget build(BuildContext context) {
return ListView(
children: [
Lottie.network(
'https://telegram.org/file/464001484/1/bzi7gr7XRGU.10147/815df2ef527132dd23',
decoder: LottieComposition.decodeGZip,
),
Lottie.asset(
'assets/LightningBug_file.tgs',
decoder: LottieComposition.decodeGZip,
),
],
);
}
````
You can select the correct .json file from a dotlottie (.lottie) archive by providing a custom decoder
````dart
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return Lottie.asset(
'assets/cat.lottie',
decoder: customDecoder,
);
}
}
Future<LottieComposition?> customDecoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, filePicker: (files) {
return files.firstWhereOrNull(
(f) => f.name.startsWith('animations/') && f.name.endsWith('.json'));
});
}
````
## Performance or excessive CPU/GPU usage
Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).

View File

@ -76,6 +76,42 @@ For each `ValueDelegate` we can either provide a static `value` or a `callback`
import 'example/lib/examples/simple_dynamic_properties.dart#example';
````
### Frame rate
By default, the animation is played at the frame rate exported by AfterEffect.
This is the most power-friendly as generally the animation is exported at 10 or 30 FPS compared to the phone's 60 or 120 FPS.
If the result is not good, you can change the frame rate
````dart
Lottie.asset('anim.json',
// Use the device frame rate (up to 120FPS)
frameRate: FrameRate.max,
// Use the exported frame rate (default)
frameRate: FrameRate.composition,
// Specific frame rate
frameRate: FrameRate(10),
)
````
### Telegram Stickers (.tgs) and DotLottie (.lottie)
TGS file can be loaded by providing a special decoder
````dart
import 'example/lib/examples/telegram_stickers.dart#example';
````
You can select the correct .json file from a dotlottie (.lottie) archive by providing a custom decoder
````dart
import 'example/lib/examples/dotlottie.dart#example';
````
## Performance or excessive CPU/GPU usage
Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).

View File

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

View File

@ -1,3 +1,9 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -21,12 +22,10 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace "com.github.xvrh.lottie.example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -44,6 +43,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.github.xvrh.lottie.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
@ -63,6 +64,4 @@ flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
dependencies {}

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.example">
<!-- Flutter needs it to communicate with the running application
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,6 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.example">
<application
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">

View File

@ -3,7 +3,7 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -3,7 +3,7 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.example">
<!-- Flutter needs it to communicate with the running application
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -1,6 +1,5 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

View File

@ -1,11 +1,20 @@
include ':app'
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
include ":app"
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"

View File

@ -0,0 +1,441 @@
{
"v": "4.8.0",
"meta": {
"g": "LottieFiles AE 3.1.1",
"a": "",
"k": "",
"d": "",
"tc": ""
},
"fr": 30,
"ip": 0,
"op": 151,
"w": 430,
"h": 932,
"nm": "Sticker Unpack",
"ddd": 1,
"assets": [
{
"id": "comp_0",
"layers": [
]
}
],
"layers": [
{
"ddd": 1,
"ind": 1,
"ty": 0,
"nm": "Pc_Sticker Pack",
"refId": "comp_0",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"rx": {
"a": 1,
"k": [
{
"i": {
"x": [
0.421
],
"y": [
1
]
},
"o": {
"x": [
0.55
],
"y": [
0
]
},
"t": 1,
"s": [
0
]
},
{
"i": {
"x": [
0.985
],
"y": [
0.168
]
},
"o": {
"x": [
0.857
],
"y": [
0
]
},
"t": 21,
"s": [
-1.9
]
},
{
"t": 41,
"s": [
24.3
]
}
],
"ix": 8
},
"ry": {
"a": 1,
"k": [
{
"i": {
"x": [
0.421
],
"y": [
1
]
},
"o": {
"x": [
0.55
],
"y": [
0
]
},
"t": 1,
"s": [
0
]
},
{
"i": {
"x": [
0.985
],
"y": [
-0.28
]
},
"o": {
"x": [
0.857
],
"y": [
0
]
},
"t": 21,
"s": [
3.7
]
},
{
"t": 41,
"s": [
16
]
}
],
"ix": 9
},
"rz": {
"a": 0,
"k": 0,
"ix": 10
},
"or": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 7
},
"p": {
"s": true,
"x": {
"a": 0,
"k": 215,
"ix": 3
},
"y": {
"a": 0,
"k": 466,
"ix": 4
},
"z": {
"a": 0,
"k": 0,
"ix": 5
}
},
"a": {
"a": 0,
"k": [
221,
307.5,
0
],
"ix": 1
},
"s": {
"a": 1,
"k": [
{
"i": {
"x": [
0.421,
0.421,
0.667
],
"y": [
1,
1,
1
]
},
"o": {
"x": [
0.55,
0.55,
0.333
],
"y": [
0,
0,
0
]
},
"t": 0,
"s": [
64.6,
64.6,
100
]
},
{
"i": {
"x": [
0.985,
0.985,
0.667
],
"y": [
0.192,
0.192,
1
]
},
"o": {
"x": [
0.857,
0.857,
0.333
],
"y": [
0,
0,
0
]
},
"t": 20,
"s": [
74.2,
74.2,
100
]
},
{
"t": 40,
"s": [
7,
7,
100
]
}
],
"ix": 6
}
},
"ao": 0,
"w": 442,
"h": 615,
"ip": 0,
"op": 41,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 14,
"ty": 4,
"nm": "Shape Layer 2",
"parent": 1,
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
221,
307.5,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
538.543,
538.543,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "rc",
"d": 1,
"s": {
"a": 0,
"k": [
294,
403
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 4
},
"nm": "Rectangle Path 1",
"mn": "ADBE Vector Shape - Rect",
"hd": false
},
{
"ty": "gf",
"o": {
"a": 0,
"k": 100,
"ix": 10
},
"r": 1,
"bm": 0,
"g": {
"p": 3,
"k": {
"a": 0,
"k": [
0,
0.373,
0.024,
0.898,
0.359,
0.186,
0.012,
0.449,
0.998,
0,
0,
0,
0,
1,
0.5,
0.5,
1,
0
],
"ix": 9
}
},
"s": {
"a": 0,
"k": [
0,
0
],
"ix": 5
},
"e": {
"a": 0,
"k": [
149,
0
],
"ix": 6
},
"t": 2,
"h": {
"a": 0,
"k": 0,
"ix": 7
},
"a": {
"a": 0,
"k": 0,
"ix": 8
},
"nm": "Gradient Fill 1",
"mn": "ADBE Vector Graphic - G-Fill",
"hd": false
}
],
"nm": "Rectangle 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 42,
"st": 0,
"bm": 0
}
],
"markers": []
}

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,264 @@
{
"v": "5.12.1",
"fr": 60,
"ip": 0,
"op": 240,
"w": 430,
"h": 795,
"nm": "gradient",
"ddd": 0,
"assets": [
{
"id": "comp_0",
"nm": "gradient",
"fr": 60,
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "1",
"sr": 1,
"ks": {
"o": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 0,
"s": [100]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 30,
"s": [75]
},
{ "t": 59, "s": [100] }
],
"ix": 11
},
"r": { "a": 0, "k": 0, "ix": 10 },
"p": { "a": 0, "k": [51.5, 58, 0], "ix": 2, "l": 2 },
"a": { "a": 0, "k": [-4.5, -130.5, 0], "ix": 1, "l": 2 },
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"d": 1,
"ty": "el",
"s": {
"a": 1,
"k": [
{
"i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
"o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
"t": 0,
"s": [50, 50]
},
{
"i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
"o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
"t": 28,
"s": [46, 46]
},
{
"i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
"o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
"t": 59,
"s": [50, 50]
},
{
"i": { "x": [0.833, 0.833], "y": [0.833, 0.833] },
"o": { "x": [0.167, 0.167], "y": [0.167, 0.167] },
"t": 120,
"s": [73.252, 73.252]
},
{ "t": 239, "s": [86.252, 86.252] }
],
"ix": 2
},
"p": { "a": 0, "k": [0, 0], "ix": 3 },
"nm": "1",
"mn": "ADBE Vector Shape - Ellipse",
"hd": false
},
{
"ty": "gf",
"o": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 0,
"s": [0]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 15,
"s": [83]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 44,
"s": [100]
},
{ "t": 59, "s": [0] }
],
"ix": 10
},
"r": 1,
"bm": 0,
"g": {
"p": 3,
"k": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [
0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.1, 0.5,
0.3, 1, 0.5
]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 15,
"s": [
0, 0.992, 0.995, 1, 0.5, 0.996, 0.998, 1, 1, 1, 1,
1, 0, 0.2, 0.5, 0.45, 1, 0.7
]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 31,
"s": [
0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.1, 0.5,
0.35, 1, 0.6
]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 59,
"s": [
0, 1, 1, 1, 0.5, 1, 1, 1, 1, 1, 1, 1, 0, 0.2, 0.5,
0.45, 1, 0.7
]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 160,
"s": [
0, 0.302, 0.391, 0.586, 0.529, 0.17, 0.239, 0.393,
1, 0.039, 0.086, 0.2, 0, 1, 0.5, 1, 1, 1
]
},
{
"t": 239,
"s": [
0, 0.053, 0.362, 0.77, 0.5, 0.046, 0.224, 0.485, 1,
0.039, 0.086, 0.2, 0, 1, 0.5, 1, 1, 1
]
}
],
"ix": 9
}
},
"s": { "a": 0, "k": [0, 0], "ix": 5 },
"e": { "a": 0, "k": [29.461, 0.027], "ix": 6 },
"t": 2,
"h": { "a": 0, "k": 0, "ix": 7 },
"a": { "a": 0, "k": 0, "ix": 8 },
"nm": "1",
"mn": "ADBE Vector Graphic - G-Fill",
"hd": false
},
{
"ty": "tr",
"p": { "a": 0, "k": [-3.372, -136.224], "ix": 2 },
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [110.999, 110.999], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 60,
"st": 0,
"ct": 1,
"bm": 0
}
]
}
],
"layers": [
{
"ddd": 0,
"ind": 36,
"ty": 0,
"nm": "",
"refId": "comp_0",
"sr": 1,
"ks": {
"o": {
"a": 1,
"k": [
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 11,
"s": [0]
},
{
"i": { "x": [0.833], "y": [0.833] },
"o": { "x": [0.167], "y": [0.167] },
"t": 130,
"s": [100]
},
{ "t": 213, "s": [0] }
],
"ix": 11
},
"r": { "a": 0, "k": 0, "ix": 10 },
"p": { "a": 0, "k": [168, 285.5, 0], "ix": 2, "l": 2 },
"a": { "a": 0, "k": [50, 51, 0], "ix": 1, "l": 2 },
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
},
"ao": 0,
"w": 100,
"h": 102,
"ip": 0,
"op": 60,
"st": 0,
"bm": 0
}
],
"markers": [],
"props": {}
}

View File

@ -0,0 +1 @@
{"v":"5.12.1","fr":60,"ip":0,"op":301,"w":850,"h":850,"nm":"Learn-icon-circle","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Path 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[327,475.038,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-1800,1800,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-14.672,-13.86],[1.815,-3.469],[5.3,11.535]],"o":[[0,0],[7.167,6.77],[-5.891,11.262],[-2.639,-5.743]],"v":[[-23.844,-20.05],[12.222,-18.383],[14.169,9.792],[-21.578,9.407]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":358.2,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false}],"ip":0,"op":3600,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because one or more lines are too long

1094
example/assets/blub.json Normal file

File diff suppressed because it is too large Load Diff

BIN
example/assets/cat.lottie Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
{"v":"5.12.1","fr":60,"ip":0,"op":120,"w":853,"h":480,"nm":"Comp 1","ddd":0,"assets":[],"fonts":{"list":[{"fName":"Pretendard-Bold","fFamily":"Pretendard","fStyle":"Bold","ascent":70.7131249997765}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"PASS","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[426,235.889,0],"ix":2,"l":2},"a":{"a":0,"k":[6.157,-60.098,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":170,"f":"Pretendard-Bold","t":"PASS","ca":0,"j":2,"tr":49,"lh":317,"ls":0,"fc":[1,1,1],"sc":[0.941,0.941,0.941],"sw":0.00999999977648,"of":true},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"sm":{"a":0,"k":100,"ix":6},"s":{"a":1,"k":[{"i":{"x":[0.205],"y":[1]},"o":{"x":[0.77],"y":[0]},"t":10,"s":[0]},{"t":47,"s":[100]}],"ix":1},"r":1},"a":{"p":{"a":0,"k":[0,88,0],"ix":2},"o":{"a":0,"k":0,"ix":9},"t":{"a":0,"k":-66,"ix":89}}},{"nm":"Animator 2","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"sm":{"a":0,"k":100,"ix":6},"r":1},"a":{"t":{"a":1,"k":[{"i":{"x":[0.092],"y":[1]},"o":{"x":[0.887],"y":[0]},"t":46,"s":[-7]},{"t":90,"s":[4]}],"ix":89}}}]},"ip":10,"op":130,"st":10,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Dark Turquoise Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[426.5,240,0],"ix":2,"l":2},"a":{"a":0,"k":[75,50,0],"ix":1,"l":2},"s":{"a":0,"k":[568.667,480,100],"ix":6,"l":2}},"ao":0,"sw":150,"sh":100,"sc":"#003626","ip":0,"op":120,"st":0,"bm":0}],"markers":[],"props":{},"chars":[{"ch":"P","size":170,"style":"Bold","w":62.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,14.16],[16.113,0],[0,0]],"o":[[0,0],[0,0],[0,0],[16.309,0],[0,-13.965],[0,0],[0,0]],"v":[[5.957,0],[20.605,0],[20.605,-23.047],[33.301,-23.047],[58.984,-46.875],[33.691,-70.703],[5.957,-70.703]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,-7.031],[8.691,0]],"o":[[0,0],[0,0],[8.691,0],[0,7.129],[0,0]],"v":[[20.605,-34.863],[20.605,-58.691],[30.957,-58.691],[43.848,-46.875],[30.957,-34.863]],"c":true},"ix":2},"nm":"P","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"P","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Pretendard"},{"ch":"A","size":170,"style":"Bold","w":71.78,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[17.773,0],[23.145,-16.309],[48.73,-16.309],[54.004,0],[69.727,0],[45.312,-70.703],[26.465,-70.703],[1.953,0]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[26.855,-27.734],[35.645,-54.785],[36.133,-54.785],[45.02,-27.734]],"c":true},"ix":2},"nm":"A","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"A","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Pretendard"},{"ch":"S","size":170,"style":"Bold","w":62.99,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.586,-5.566],[0,0],[15.625,0],[0,-12.891],[-11.914,-2.93],[0,0],[0,-4.785],[7.715,0],[0.488,7.129],[0,0],[-17.09,0],[0,12.988],[12.109,2.734],[0,0],[-0.098,5.078],[-7.422,0]],"o":[[0,0],[-0.195,-12.5],[-15.43,0],[0,10.352],[0,0],[7.715,1.953],[0,5.273],[-7.812,0],[0,0],[0.391,15.137],[17.188,0],[0,-11.816],[0,0],[-6.055,-1.367],[0.098,-4.688],[7.129,0]],"v":[[44.336,-50.488],[58.398,-50.488],[32.227,-71.68],[5.469,-50.293],[24.707,-31.055],[32.422,-29.199],[44.531,-20.215],[31.836,-11.328],[17.871,-22.07],[3.613,-22.07],[32.031,0.977],[59.375,-20.215],[38.184,-40.918],[31.836,-42.48],[20.508,-51.27],[32.129,-59.375]],"c":true},"ix":2},"nm":"S","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"S","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Pretendard"}]}

Binary file not shown.

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>11.0</string>
</dict>
</plist>

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@ -10,78 +10,32 @@ project 'Runner', {
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
generated_key_values
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
# Flutter Pod
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
flutter_additional_ios_build_settings(target)
end
end

View File

@ -1,28 +1,23 @@
PODS:
- Flutter (1.0.0)
- path_provider (0.0.1):
- Flutter
- path_provider_macos (0.0.1):
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- Flutter (from `Flutter`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
path_provider_macos:
:path: ".symlinks/plugins/path_provider_macos/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a
PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d
COCOAPODS: 1.9.1
COCOAPODS: 1.14.2

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -68,7 +68,6 @@
31ACBCF01D379565237D9814 /* Pods-Runner.release.xcconfig */,
5A699D4B67473B61D811D350 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@ -164,12 +163,11 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = FFDVVDAQL4;
LastSwiftMigration = 1100;
};
};
@ -209,10 +207,12 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@ -245,6 +245,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -264,13 +265,11 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../Flutter/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@ -313,7 +312,6 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -353,7 +351,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -369,19 +367,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FFDVVDAQL4;
DEVELOPMENT_TEAM = TC6K7794M3;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -391,7 +392,6 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -437,7 +437,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -447,7 +447,6 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -487,7 +486,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -504,19 +503,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FFDVVDAQL4;
DEVELOPMENT_TEAM = TC6K7794M3;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -532,19 +534,22 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FFDVVDAQL4;
DEVELOPMENT_TEAM = TC6K7794M3;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.sample;
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
@ -22,6 +24,8 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>

View File

@ -0,0 +1,39 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Example(),
),
);
}
}
//---- example
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return Lottie.asset(
'assets/cat.lottie',
decoder: customDecoder,
);
}
}
Future<LottieComposition?> customDecoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, filePicker: (files) {
return files.firstWhereOrNull(
(f) => f.name.startsWith('animations/') && f.name.endsWith('.json'));
});
}
//----

View File

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

View File

@ -0,0 +1,219 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
// ignore: implementation_imports
import 'package:lottie/src/render_cache.dart';
void main() {
globalRenderCache.enableDebugBackground = true;
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true,
color: Colors.white,
home: Column(
children: [
Container(
height: 170,
color: Colors.white,
),
Expanded(
child: Scaffold(
appBar: AppBar(
title: const Text('Render cache'),
),
drawer: const Drawer(
child: Column(
children: [
Expanded(
child: RenderCacheDebugPanel(),
),
],
),
),
body: _Example(),
),
),
],
),
);
}
}
class _Example extends StatefulWidget {
static String _text(a) => '';
@override
State<_Example> createState() => _ExampleState();
}
class _ExampleState extends State<_Example> {
int _animationCount = 1;
@override
Widget build(BuildContext context) {
return ListView(
children: [
ElevatedButton(
onPressed: () {
setState(() {
++_animationCount;
});
},
child: Text('Add animation $_animationCount'),
),
Row(
children: [
Expanded(
child: Stack(
children: [
for (var i = 0; i < _animationCount; i++)
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(60),
enableRenderCache: true,
fit: BoxFit.cover,
delegates: LottieDelegates(
text: _Example._text,
values: [
ValueDelegate.color(['*'], value: Color(i)),
],
),
),
),
],
),
),
Expanded(
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.cover,
),
),
],
),
Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Lottie.asset('assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
enableRenderCache: true),
),
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 2,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
enableRenderCache: true,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
),
),
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 0.5,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
enableRenderCache: true,
fit: BoxFit.fill,
),
),
),
),
),
],
),
],
);
}
}
class RenderCacheDebugPanel extends StatefulWidget {
const RenderCacheDebugPanel({super.key});
@override
State<RenderCacheDebugPanel> createState() => _RenderCacheDebugPanelState();
}
class _RenderCacheDebugPanelState extends State<RenderCacheDebugPanel> {
@override
Widget build(BuildContext context) {
return StreamBuilder<void>(
stream: globalRenderCache.onUpdate,
builder: (context, snapshot) {
return ListView(
children: [
Text('Images: ${globalRenderCache.imageCount}'),
Text(
'Memory: ${(globalRenderCache.totalMemory / 1000000).toStringAsFixed(1)}MB'),
const Divider(),
ElevatedButton(
onPressed: () {
globalRenderCache.clear();
},
child: const Text('Clear'),
),
const Divider(),
SwitchListTile(
title: const Text('Enable debug background'),
value: globalRenderCache.enableDebugBackground,
onChanged: (v) {
setState(() {
globalRenderCache.enableDebugBackground = v;
});
},
)
],
);
});
}
@override
void dispose() {
super.dispose();
}
}

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Example(),
),
);
}
}
class Example extends StatelessWidget {
const Example({super.key});
@override
//--- example
Widget build(BuildContext context) {
return ListView(
children: [
Lottie.network(
'https://telegram.org/file/464001484/1/bzi7gr7XRGU.10147/815df2ef527132dd23',
decoder: LottieComposition.decodeGZip,
),
Lottie.asset(
'assets/LightningBug_file.tgs',
decoder: LottieComposition.decodeGZip,
),
],
);
}
//---
}

View File

@ -19,7 +19,7 @@ class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
//showPerformanceOverlay: true,
showPerformanceOverlay: true,
home: Scaffold(
appBar: AppBar(
title: const Text('Lottie Flutter'),
@ -42,6 +42,7 @@ class App extends StatelessWidget {
child: Lottie.asset(
assetName,
fit: BoxFit.contain,
enableRenderCache: true,
onWarning: (w) => _logger.info('$assetName - $w'),
frameBuilder: (context, child, composition) {
return AnimatedOpacity(

View File

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

View File

@ -203,7 +203,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
url: "https://pub.dev"
source: hosted
version: "3.3.6"
version: "3.4.9"
async:
dependency: transitive
description:
@ -42,13 +42,13 @@ packages:
source: hosted
version: "1.1.1"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
url: "https://pub.dev"
source: hosted
version: "1.17.1"
version: "1.17.2"
convert:
dependency: transitive
description:
@ -61,10 +61,10 @@ packages:
dependency: transitive
description:
name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
fake_async:
dependency: transitive
description:
@ -77,18 +77,10 @@ packages:
dependency: transitive
description:
name: ffi
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
version: "2.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -106,10 +98,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -127,10 +119,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
version: "1.1.0"
http_parser:
dependency: transitive
description:
@ -151,41 +143,41 @@ packages:
dependency: transitive
description:
name: lints
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.0"
logging:
dependency: "direct main"
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
lottie:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "2.3.2"
version: "3.0.0-alpha.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev"
source: hosted
version: "0.12.15"
version: "0.12.16"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
version: "0.5.0"
meta:
dependency: transitive
description:
@ -206,82 +198,74 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.0.14"
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7"
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev"
source: hosted
version: "2.0.24"
version: "2.2.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "818b2dc38b0f178e0ea3f7cf3b28146faab11375985d815942a68eee11c2d0f7"
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.1"
platform:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.3"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.6"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
url: "https://pub.dev"
source: hosted
version: "3.7.2"
process:
dependency: transitive
description:
name: process
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
version: "3.7.3"
sky_engine:
dependency: transitive
description: flutter
@ -291,10 +275,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
stack_trace:
dependency: transitive
description:
@ -331,18 +315,18 @@ packages:
dependency: transitive
description:
name: test_api
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "0.6.0"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.2"
vector_math:
dependency: transitive
description:
@ -351,22 +335,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
win32:
dependency: transitive
description:
name: win32
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "5.1.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "1.0.3"
sdks:
dart: ">=3.0.0-0 <4.0.0"
flutter: ">=3.3.0"
dart: ">=3.1.0 <4.0.0"
flutter: ">=3.13.0"

View File

@ -2,10 +2,13 @@ name: lottie_example
description: A sample app for the Lottie player
publish_to: none
version: 2.7.0+1
environment:
sdk: ">=2.18.0 <3.0.0"
sdk: "^3.0.0"
dependencies:
collection:
flutter:
sdk: flutter
flutter_colorpicker:

View File

@ -1,4 +1,4 @@
export 'src/composition.dart' show LottieComposition;
export 'src/composition.dart' show LottieComposition, LottieDecoder;
export 'src/frame_rate.dart' show FrameRate;
export 'src/lottie.dart' show Lottie;
export 'src/lottie_builder.dart' show LottieBuilder;
@ -10,7 +10,7 @@ export 'src/options.dart' show LottieOptions;
export 'src/providers/asset_provider.dart' show AssetLottie;
export 'src/providers/file_provider.dart' show FileLottie;
export 'src/providers/load_image.dart' show LottieImageProviderFactory;
export 'src/providers/lottie_provider.dart' show LottieProvider, LottieCache;
export 'src/providers/lottie_provider.dart' show LottieCache, LottieProvider;
export 'src/providers/memory_provider.dart' show MemoryLottie;
export 'src/providers/network_provider.dart' show NetworkLottie;
export 'src/raw_lottie.dart' show RawLottie;

View File

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

View File

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

View File

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

View File

@ -135,7 +135,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
_paint.setAlpha(alpha.clamp(0, 255).toInt());
_paint.setAlpha(alpha.clamp(0, 255));
if (lottieDrawable.antiAliasingSuggested) {
_paint.isAntiAlias = true;
}
@ -167,7 +167,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
Gradient _getLinearGradient() {
var gradientHash = _getGradientHash();
var gradient = _linearGradientCache[gradientHash];
if (gradient != null) {
if (gradient != null && _colorCallbackAnimation == null) {
return gradient;
}
var startPoint = _startPointAnimation.value;
@ -175,9 +175,10 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
var gradientColor = _colorAnimation.value;
var colors = _applyDynamicColorsIfNeeded(gradientColor.colors);
var positions = gradientColor.positions;
gradient = Gradient.linear(
startPoint, endPoint, colors, positions, TileMode.clamp);
_linearGradientCache[gradientHash] = gradient;
gradient = Gradient.linear(startPoint, endPoint, colors, positions);
if (gradientHash != null) {
_linearGradientCache[gradientHash] = gradient;
}
return gradient;
}
@ -200,13 +201,17 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
if (radius <= 0) {
radius = 0.001;
}
gradient =
Gradient.radial(startPoint, radius, colors, positions, TileMode.clamp);
_radialGradientCache[gradientHash] = gradient;
gradient = Gradient.radial(startPoint, radius, colors, positions);
if (gradientHash != null) {
_radialGradientCache[gradientHash] = gradient;
}
return gradient;
}
int _getGradientHash() {
int? _getGradientHash() {
// Don't cache gradient if ValueDelegate.gradient is used
if (_colorCallbackAnimation != null) return null;
var startPointProgress =
(_startPointAnimation.progress * _cacheSteps).round();
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();

View File

@ -93,7 +93,9 @@ class GradientStrokeContent extends BaseStrokeContent {
gradient = Gradient.linear(startPoint, endPoint, colors, positions,
TileMode.clamp, parentMatrix.storage);
_linearGradientCache[gradientHash] = gradient;
if (gradientHash != null) {
_linearGradientCache[gradientHash] = gradient;
}
return gradient;
}
@ -115,14 +117,19 @@ class GradientStrokeContent extends BaseStrokeContent {
var radius = hypot(x1 - x0, y1 - y0).toDouble();
gradient = Gradient.radial(startPoint, radius, colors, positions,
TileMode.clamp, parentMatrix.storage);
_radialGradientCache[gradientHash] = gradient;
if (gradientHash != null) {
_radialGradientCache[gradientHash] = gradient;
}
return gradient;
}
//TODO(xha): cache the shader based on the input parameters and not the animation
// progress.
// At first, log when there is too many cache miss.
int _getGradientHash(Matrix4 parentMatrix) {
int? _getGradientHash(Matrix4 parentMatrix) {
// Don't cache gradient if ValueDelegate.gradient is used
if (_colorCallbackAnimation != null) return null;
var startPointProgress =
(_startPointAnimation.progress * _cacheSteps).round();
var endPointProgress = (_endPointAnimation.progress * _cacheSteps).round();

View File

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

View File

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

View File

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

View File

@ -107,7 +107,7 @@ class RoundedCornersContent implements ShapeModifierContent {
// We can't round the corner of the end of a non-closed curve.
var isEndOfCurve = !startingShapeData.isClosed &&
(i == 0 && i == startingCurves.length - 1);
(i == 0 || i == startingCurves.length - 1);
if (inPoint == vertex && outPoint == vertex && !isEndOfCurve) {
// This vertex is a point. Round its corners
var dxToPreviousVertex = vertex.dx - previousVertex.dx;
@ -200,7 +200,7 @@ class RoundedCornersContent implements ShapeModifierContent {
var outPoint = startingCurve.controlPoint1;
var isEndOfCurve = !startingShapeData.isClosed &&
(i == 0 && i == startingCurves.length - 1);
(i == 0 || i == startingCurves.length - 1);
if (inPoint == vertex && outPoint == vertex && !isEndOfCurve) {
vertices += 2;
} else {
@ -216,7 +216,7 @@ class RoundedCornersContent implements ShapeModifierContent {
this.shapeData = shapeData =
ShapeData(newCurves, initialPoint: Offset.zero, closed: false);
}
shapeData.setClosed(isClosed);
shapeData.isClosed = isClosed;
return shapeData;
}

View File

@ -1,3 +1,4 @@
import 'dart:math' as math;
import 'dart:ui';
import '../../model/content/gradient_color.dart';
import '../../value/keyframe.dart';
@ -8,8 +9,17 @@ class GradientColorKeyframeAnimation extends KeyframeAnimation<GradientColor> {
GradientColorKeyframeAnimation(List<Keyframe<GradientColor>> keyframes)
: super(keyframes) {
var startValue = keyframes.first.startValue;
var size = startValue == null ? 0 : startValue.size;
// Not all keyframes that this GradientColor are used for will have the same length.
// AnimatableGradientColorValue.ensureInterpolatableKeyframes may add extra positions
// for some keyframes but not others to ensure that it is interpolatable.
// Ensure that there is enough space for the largest keyframe.
var size = 0;
for (var i = 0; i < keyframes.length; i++) {
var startValue = keyframes[i].startValue;
if (startValue != null) {
size = math.max(size, startValue.size);
}
}
_gradientColor = GradientColor(List<double>.filled(size, 0.0),
List<Color>.filled(size, const Color(0x00000000)));
}

View File

@ -1,5 +1,6 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'package:archive/archive.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
@ -17,6 +18,9 @@ import 'utils.dart';
typedef WarningCallback = void Function(String);
/// A function that knows how to transform a list of bytes to a `LottieComposition`
typedef LottieDecoder = Future<LottieComposition?> Function(List<int> bytes);
class CompositionParameters {
MutableRectangle<int> bounds = MutableRectangle<int>(0, 0, 0, 0);
double startFrame = 0.0;
@ -35,25 +39,48 @@ class CompositionParameters {
}
class LottieComposition {
static Future<LottieComposition> fromByteData(ByteData data,
{String? name, LottieImageProviderFactory? imageProviderFactory}) {
return fromBytes(data.buffer.asUint8List(),
name: name, imageProviderFactory: imageProviderFactory);
static LottieComposition parseJsonBytes(List<int> bytes) {
return LottieCompositionParser.parse(
LottieComposition._(), JsonReader.fromBytes(bytes));
}
static Future<LottieComposition> fromBytes(Uint8List bytes,
{String? name, LottieImageProviderFactory? imageProviderFactory}) async {
Archive? archive;
if (bytes[0] == 0x50 && bytes[1] == 0x4B) {
archive = ZipDecoder().decodeBytes(bytes);
var jsonFile = archive.files.firstWhere((e) => e.name.endsWith('.json'));
bytes = jsonFile.content as Uint8List;
static Future<LottieComposition> fromByteData(
ByteData data, {
LottieImageProviderFactory? imageProviderFactory,
LottieDecoder? decoder,
}) {
return fromBytes(data.buffer.asUint8List(), decoder: decoder);
}
static Future<LottieComposition> fromBytes(
List<int> bytes, {
LottieDecoder? decoder,
}) async {
decoder ??= decodeZip;
var compositionFuture = await decoder(bytes);
if (compositionFuture != null) {
return compositionFuture;
}
return parseJsonBytes(bytes);
}
var composition = LottieCompositionParser.parse(
LottieComposition._(name), JsonReader.fromBytes(bytes));
static Future<LottieComposition?> decodeZip(
List<int> bytes, {
LottieImageProviderFactory? imageProviderFactory,
ArchiveFile? Function(List<ArchiveFile>)? filePicker,
}) async {
if (bytes[0] == 0x50 && bytes[1] == 0x4B) {
var archive = ZipDecoder().decodeBytes(bytes);
ArchiveFile? jsonFile;
if (filePicker != null) {
jsonFile = filePicker(archive.files);
}
jsonFile ??= archive.files.firstWhere((e) => e.name.endsWith('.json'));
var composition = parseJsonBytes(jsonFile.content as Uint8List);
if (archive != null) {
for (var image in composition.images.values) {
var imagePath = p.posix.join(image.dirName, image.fileName);
var found = archive.files.firstWhereOrNull(
@ -73,14 +100,29 @@ class LottieComposition {
composition, image, MemoryImage(found.content as Uint8List));
}
}
}
return composition;
for (var font in archive.files.where((f) => f.name.endsWith('.ttf'))) {
var fileName = p.basenameWithoutExtension(font.name).toLowerCase();
var existingFont = composition.fonts.values
.firstWhereOrNull((f) => f.family.toLowerCase() == fileName);
await loadFontFromList(font.content as Uint8List,
fontFamily: existingFont?.family);
}
return composition;
}
return null;
}
LottieComposition._(this.name);
static Future<LottieComposition?> decodeGZip(List<int> bytes) async {
if (bytes[0] == 31 && bytes[1] == 139) {
var decodedBytes = GZipDecoder().decodeBytes(bytes);
return LottieComposition.parseJsonBytes(decodedBytes);
}
return null;
}
LottieComposition._();
final String? name;
final _performanceTracker = PerformanceTracker();
// This is stored as a set to avoid duplicates.
final _warnings = <String>{};

View File

@ -1,9 +1,28 @@
import 'package:flutter/foundation.dart';
@immutable
class FrameRate {
static final max = FrameRate._special(0);
static final composition = FrameRate._special(-1);
static const max = FrameRate._special(0);
static const composition = FrameRate._special(-1);
final double framesPerSecond;
FrameRate(this.framesPerSecond) : assert(framesPerSecond > 0);
FrameRate._special(this.framesPerSecond);
const FrameRate(this.framesPerSecond) : assert(framesPerSecond > 0);
const FrameRate._special(this.framesPerSecond);
@override
int get hashCode => framesPerSecond.hashCode;
@override
bool operator ==(other) =>
other is FrameRate && other.framesPerSecond == framesPerSecond;
@override
String toString() {
return 'FrameRate(${switch (framesPerSecond) {
0 => 'max',
-1 => 'composition',
_ => framesPerSecond
}})';
}
}

View File

@ -31,10 +31,12 @@ class Lottie extends StatefulWidget {
this.options,
bool? addRepaintBoundary,
this.filterQuality,
bool? enableRenderCache,
}) : animate = animate ?? true,
reverse = reverse ?? false,
repeat = repeat ?? true,
addRepaintBoundary = addRepaintBoundary ?? true;
addRepaintBoundary = addRepaintBoundary ?? true,
enableRenderCache = enableRenderCache ?? false;
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
static LottieBuilder asset(
@ -60,6 +62,8 @@ class Lottie extends StatefulWidget {
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
}) =>
LottieBuilder.asset(
name,
@ -84,6 +88,8 @@ class Lottie extends StatefulWidget {
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
@ -108,6 +114,8 @@ class Lottie extends StatefulWidget {
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
}) =>
LottieBuilder.file(
file,
@ -130,6 +138,8 @@ class Lottie extends StatefulWidget {
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
@ -154,6 +164,8 @@ class Lottie extends StatefulWidget {
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
}) =>
LottieBuilder.memory(
bytes,
@ -176,6 +188,8 @@ class Lottie extends StatefulWidget {
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
);
/// Creates a widget that displays an [LottieComposition] obtained from the network.
@ -200,6 +214,8 @@ class Lottie extends StatefulWidget {
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
}) =>
LottieBuilder.network(
url,
@ -222,6 +238,8 @@ class Lottie extends StatefulWidget {
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
);
/// The Lottie composition to animate.
@ -302,6 +320,7 @@ class Lottie extends StatefulWidget {
/// Some options to enable/disable some feature of Lottie
/// - enableMergePaths: Enable merge path support
/// - enableApplyingOpacityToLayers: Enable layer-level opacity
final LottieOptions? options;
/// Indicate to automatically add a `RepaintBoundary` widget around the animation.
@ -317,6 +336,27 @@ class Lottie extends StatefulWidget {
/// Defaults to [FilterQuality.low]
final FilterQuality? filterQuality;
/// Opt-in a special render mode where the frames of the animation are
/// lazily rendered in offscreen images.
/// Subsequent runs of the animation will be very cheap to render.
///
/// This is useful is the animation is complex and can consume lot of energy
/// from the battery.
/// This is will trade an excessive CPU usage for an increase memory usage.
///
/// The render cache is managed internally and will release the memory once the
/// animation is disposed. The cache is shared between all animations. If 2 `Lottie`
/// widget are rendered at the same size, they will render only once.
///
/// Any change in the configuration of the animation (delegates, frame rate etc...)
/// will clear the cache.
/// Any change in the size will invalidate the cache. The cache use the final size
/// visible on the screen (with all transforms applied).
///
/// In order to not exceed the memory limit of a device, the cache is constrained
/// to maximum 100MiB. After that, animations are not cached anymore.
final bool enableRenderCache;
static bool get traceEnabled => L.traceEnabled;
static set traceEnabled(bool enabled) {
L.traceEnabled = enabled;
@ -385,6 +425,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
fit: widget.fit,
alignment: widget.alignment,
filterQuality: widget.filterQuality,
enableRenderCache: widget.enableRenderCache,
);
},
);

View File

@ -60,6 +60,7 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
this.enableRenderCache,
});
/// Creates a widget that displays an [LottieComposition] obtained from the network.
@ -85,8 +86,12 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = NetworkLottie(src,
headers: headers, imageProviderFactory: imageProviderFactory);
headers: headers,
imageProviderFactory: imageProviderFactory,
decoder: decoder);
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
///
@ -119,7 +124,10 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory);
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = FileLottie(file,
imageProviderFactory: imageProviderFactory, decoder: decoder);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
LottieBuilder.asset(
@ -145,10 +153,13 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = AssetLottie(name,
bundle: bundle,
package: package,
imageProviderFactory: imageProviderFactory);
imageProviderFactory: imageProviderFactory,
decoder: decoder);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
LottieBuilder.memory(
@ -172,7 +183,10 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie = MemoryLottie(bytes, imageProviderFactory: imageProviderFactory);
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = MemoryLottie(bytes,
imageProviderFactory: imageProviderFactory, decoder: decoder);
/// The lottie animation to load.
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
@ -219,6 +233,7 @@ class LottieBuilder extends StatefulWidget {
/// Some options to enable/disable some feature of Lottie
/// - enableMergePaths: Enable merge path support
/// - enableApplyingOpacityToLayers: Enable layer-level opacity
final LottieOptions? options;
/// A builder function responsible for creating the widget that represents
@ -402,6 +417,27 @@ class LottieBuilder extends StatefulWidget {
/// ```
final ImageErrorWidgetBuilder? errorBuilder;
/// Opt-in a special render mode where the frames of the animation are
/// lazily rendered in offscreen images.
/// Subsequent runs of the animation will be very cheap to render.
///
/// This is useful is the animation is complex and can consume lot of energy
/// from the battery.
/// This is will trade an excessive CPU usage for an increase memory usage.
///
/// The render cache is managed internally and will release the memory once the
/// animation is disposed. The cache is shared between all animations. If 2 `Lottie`
/// widget are rendered at the same size, they will render only once.
///
/// Any change in the configuration of the animation (delegates, frame rate etc...)
/// will clear the cache.
/// Any change in the size will invalidate the cache. The cache use the final size
/// visible on the screen (with all transforms applied).
///
/// In order to not exceed the memory limit of a device, the cache is constrained
/// to maximum 100MiB. After that, animations are not cached anymore.
final bool? enableRenderCache;
@override
State<LottieBuilder> createState() => _LottieBuilderState();
@ -422,13 +458,6 @@ class LottieBuilder extends StatefulWidget {
class _LottieBuilderState extends State<LottieBuilder> {
Future<LottieComposition>? _loadingFuture;
@override
void initState() {
super.initState();
_load();
}
@override
void didUpdateWidget(LottieBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
@ -440,8 +469,8 @@ class _LottieBuilderState extends State<LottieBuilder> {
void _load() {
var provider = widget.lottie;
_loadingFuture = widget.lottie.load().then((composition) {
// LottieProvier.load() can return a Synchronous future and the onLoaded
_loadingFuture = widget.lottie.load(context: context).then((composition) {
// LottieProvider.load() can return a Synchronous future and the onLoaded
// callback can call setState, so we wrap it in a microtask to avoid an
// "!_isDirty" error.
scheduleMicrotask(() {
@ -464,6 +493,11 @@ class _LottieBuilderState extends State<LottieBuilder> {
@override
Widget build(BuildContext context) {
// We need to wait the first build instead of initState because AssetLottie
// provider may call DefaultAssetBundle.of
if (_loadingFuture == null) {
_load();
}
return FutureBuilder<LottieComposition>(
future: _loadingFuture,
builder: (context, snapshot) {
@ -472,7 +506,11 @@ class _LottieBuilderState extends State<LottieBuilder> {
if (errorBuilder != null) {
return errorBuilder(context, snapshot.error!, snapshot.stackTrace);
} else if (kDebugMode) {
return ErrorWidget(snapshot.error!);
return SizedBox(
width: widget.width,
height: widget.height,
child: ErrorWidget(snapshot.error!),
);
}
}
@ -495,6 +533,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
alignment: widget.alignment,
addRepaintBoundary: widget.addRepaintBoundary,
filterQuality: widget.filterQuality,
enableRenderCache: widget.enableRenderCache,
);
if (widget.frameBuilder != null) {

View File

@ -6,6 +6,7 @@ import 'lottie_delegates.dart';
import 'model/key_path.dart';
import 'model/layer/composition_layer.dart';
import 'parser/layer_parser.dart';
import 'render_cache.dart';
import 'utils.dart';
import 'value_delegate.dart';
@ -14,6 +15,7 @@ class LottieDrawable {
final _matrix = Matrix4.identity();
late CompositionLayer _compositionLayer;
final Size size;
final FrameRate? frameRate;
LottieDelegates? _delegates;
bool _isDirty = true;
bool enableMergePaths = false;
@ -22,7 +24,7 @@ class LottieDrawable {
/// Gives a suggestion whether to paint with anti-aliasing, or not. Default is true.
bool antiAliasingSuggested = true;
LottieDrawable(this.composition, {LottieDelegates? delegates})
LottieDrawable(this.composition, {LottieDelegates? delegates, this.frameRate})
: size = Size(composition.bounds.width.toDouble(),
composition.bounds.height.toDouble()) {
this.delegates = delegates;
@ -50,8 +52,8 @@ class LottieDrawable {
double get progress => _progress ?? 0.0;
double? _progress;
bool setProgress(double value, {FrameRate? frameRate}) {
frameRate ??= FrameRate.composition;
bool setProgress(double value) {
var frameRate = this.frameRate ?? FrameRate.composition;
var roundedProgress =
composition.roundProgress(value, frameRate: frameRate);
if (roundedProgress != _progress) {
@ -64,14 +66,48 @@ class LottieDrawable {
}
}
int _delegatesHash = 0;
LottieDelegates? get delegates => _delegates;
set delegates(LottieDelegates? delegates) {
if (_delegates != delegates) {
_delegates = delegates;
_updateValueDelegates(delegates?.values);
_delegatesHash = _computeValueDelegateHash(delegates);
}
}
List<Object?> _configHash() {
return [
enableMergePaths,
filterQuality,
frameRate,
isApplyingOpacityToLayersEnabled,
];
}
int _computeValueDelegateHash(LottieDelegates? delegates) {
if (delegates == null) return 0;
var valuesHash = <int>[];
if (delegates.values case var values?) {
for (var value in values) {
valuesHash.add(Object.hash(
value.value,
value.callbackHash,
value.property,
Object.hashAll(value.keyPath),
));
}
}
return Object.hash(
delegates.image,
delegates.text,
delegates.textStyle,
Object.hashAll(valuesHash),
);
}
bool get useTextGlyphs {
return delegates?.text == null && composition.characters.isNotEmpty;
}
@ -139,8 +175,14 @@ class LottieDrawable {
return keyPaths;
}
void draw(ui.Canvas canvas, ui.Rect rect,
{BoxFit? fit, Alignment? alignment}) {
static final _normalPaint = Paint();
void draw(
ui.Canvas canvas,
ui.Rect rect, {
BoxFit? fit,
Alignment? alignment,
RenderCacheContext? renderCache,
}) {
if (rect.isEmpty) {
return;
}
@ -160,13 +202,41 @@ class LottieDrawable {
var destinationRect = destinationPosition & destinationSize;
var sourceRect = alignment.inscribe(sourceSize, Offset.zero & inputSize);
canvas.save();
canvas.translate(destinationRect.left, destinationRect.top);
_matrix.setIdentity();
_matrix.scale(destinationRect.size.width / sourceRect.width,
destinationRect.size.height / sourceRect.height);
_compositionLayer.draw(canvas, rect.size, _matrix, parentAlpha: 255);
canvas.restore();
var cacheUsed = false;
if (renderCache != null) {
var rect = Rect.fromPoints(renderCache.localToGlobal(destinationPosition),
renderCache.localToGlobal(destinationRect.bottomRight));
var cacheImageSize = Size(
(rect.size.width * renderCache.devicePixelRatio).roundToDouble(),
(rect.size.height * renderCache.devicePixelRatio).roundToDouble());
var cacheKey = CacheKey(
composition: composition,
size: cacheImageSize,
config: _configHash(),
delegates: _delegatesHash);
var cache = renderCache.handle.withKey(cacheKey);
var cachedImage = cache.imageForProgress(progress, (cacheCanvas) {
_matrix.scale(cacheImageSize.width / sourceSize.width,
cacheImageSize.height / sourceSize.height);
_compositionLayer.draw(cacheCanvas, cacheImageSize, _matrix,
parentAlpha: 255);
});
if (cachedImage != null) {
cacheUsed = true;
canvas.drawImageRect(cachedImage, Offset.zero & cacheImageSize,
destinationRect, _normalPaint);
}
}
if (!cacheUsed) {
canvas.save();
canvas.translate(destinationRect.left, destinationRect.top);
_matrix.scale(destinationSize.width / sourceRect.width,
destinationSize.height / sourceRect.height);
_compositionLayer.draw(canvas, rect.size, _matrix, parentAlpha: 255);
canvas.restore();
}
}
}
@ -175,3 +245,14 @@ class LottieFontStyle {
LottieFontStyle({required this.fontFamily, required this.style});
}
class RenderCacheContext {
final RenderCacheHandle handle;
final Offset Function(Offset) localToGlobal;
final double devicePixelRatio;
RenderCacheContext(
{required this.handle,
required this.localToGlobal,
required this.devicePixelRatio});
}

View File

@ -21,6 +21,14 @@ class GradientColor {
colors[i] =
GammaEvaluator.evaluate(progress, gc1.colors[i], gc2.colors[i]);
}
// Not all keyframes that this GradientColor are used for will have the same length.
// AnimatableGradientColorValue.ensureInterpolatableKeyframes may add extra positions
// for some keyframes but not others to ensure that it is interpolatable.
// If there are extra positions here, just duplicate the last value in the gradient.
for (var i = gc1.colors.length; i < positions.length; i++) {
positions[i] = positions[gc1.colors.length - 1];
colors[i] = colors[gc1.colors.length - 1];
}
}
GradientColor copyWithPositions(List<double> positions) {

View File

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

View File

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

View File

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

View File

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

View File

@ -61,7 +61,7 @@ class KeyPath {
}
/// Returns a {@link KeyPathElement} that this has been resolved to. KeyPaths get resolved with
/// resolveKeyPath on LottieDrawable or LottieAnimationView.
/// resolveKeyPath on LottieDrawable.
KeyPathElement? get resolvedElement {
return _resolvedElement;
}

View File

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

View File

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

View File

@ -14,6 +14,7 @@ class SolidLayer extends BaseLayer {
final Paint paint = Paint()..style = PaintingStyle.fill;
final Path path = PathFactory.create();
BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation;
BaseKeyframeAnimation<Color, Color?>? _colorAnimation;
SolidLayer(LottieDrawable lottieDrawable, Layer layerModel)
: super(lottieDrawable, layerModel) {
@ -35,6 +36,9 @@ class SolidLayer extends BaseLayer {
255.0)
.round();
paint.setAlpha(alpha);
if (_colorAnimation?.value case var color?) {
paint.color = color;
}
if (_colorFilterAnimation != null) {
paint.colorFilter = _colorFilterAnimation!.value;
}
@ -76,6 +80,14 @@ class SolidLayer extends BaseLayer {
_colorFilterAnimation = ValueCallbackKeyframeAnimation(
callback as LottieValueCallback<ColorFilter>, null);
}
} else if (property == LottieProperty.color) {
if (callback == null) {
_colorAnimation = null;
paint.color = layerModel.solidColor;
} else {
_colorAnimation = ValueCallbackKeyframeAnimation(
callback as LottieValueCallback<Color>, null);
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -69,17 +69,13 @@ class GradientColorParser {
case 0:
// position
positions[colorIndex] = value;
break;
case 1:
r = (value * 255).round();
break;
case 2:
g = (value * 255).round();
break;
case 3:
var b = (value * 255).round();
colors[colorIndex] = Color.fromARGB(255, r, g, b);
break;
}
}
@ -165,6 +161,9 @@ class GradientColorParser {
if (colorStopPosition < position && i != colorStopPositions.length - 1) {
continue;
}
if (i == colorStopPositions.length - 1 && position >= colorStopPosition) {
return colorStopColors[i].withOpacity(opacity);
}
// We found the position in which position is between i - 1 and i.
var distanceBetweenColors =
colorStopPositions[i] - colorStopPositions[i - 1];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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