Compare commits

...

8 Commits

Author SHA1 Message Date
d8f5b872ef [feat] support image layer quality setting (#214) 2022-07-27 11:11:26 +02:00
bc3eb4621b Add latest feature/fixes from Lottie-Android (#209)
- Added support for rounded corners on shapes and rects
- Add support for text in dynamic properties (ValueDelegate)
- Improve stroke with offset
- Add support for reversed polystar paths
- Enforce order of operations to avoid rounding errors
2022-04-14 22:20:22 +02:00
08d7da747a Add pubignore 2022-02-14 10:07:39 +01:00
b2ebd2058d Fix new lints in Flutter 2.10 (#196) 2022-02-14 09:31:30 +01:00
2979b62dc0 fix: Revert Cubic to PathInterpolator.cubic (#173) 2021-09-24 23:42:56 +02:00
340f0d2f27 Format changelog 2021-09-19 20:51:42 +02:00
34fef26eb2 Fix changelog 2021-09-19 20:50:56 +02:00
89c62122bf Add support for Blur & DropShadow (#170) 2021-09-19 20:48:01 +02:00
467 changed files with 10817 additions and 327 deletions

View File

@ -10,11 +10,11 @@ jobs:
name: Flutter analyze
strategy:
matrix:
flutter: ['beta']
flutter: ['stable']
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
- uses: subosito/flutter-action@v2
with:
channel: ${{ matrix.flutter }}
- run: flutter doctor
@ -22,8 +22,8 @@ jobs:
- run: flutter pub get
working-directory: example
- run: flutter analyze
- run: flutter test test # https://github.com/flutter/flutter/issues/20907
- run: flutter test test
- run: flutter test
- run: flutter test
working-directory: example
- run: flutter pub run tool/prepare_submit.dart
- name: "check for uncommitted changes"
@ -38,10 +38,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
- uses: subosito/flutter-action@v2
with:
channel: 'beta'
- run: flutter config --enable-web
channel: 'stable'
- run: flutter precache web
- run: flutter pub get
working-directory: example

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ _*
!.gitignore
!.github
!.pubignore
**/failures/*.png

15
.pubignore Normal file
View File

@ -0,0 +1,15 @@
.dart_tool/
.packages
build/
doc/api/
*.iml
_*
.*
**/failures/*.png
test/golden/
test/goldens/
example/assets/
example/android/
example/ios/
example/macos/
example/web/

View File

@ -1,3 +1,71 @@
## [1.4.0]
- Added `filterQuality` property to control the performance vs quality trade-off to use when drawing images
## [1.3.0]
- Added support for rounded corners on shapes and rects
- Add support for text in dynamic properties (`ValueDelegate`)
Example:
```dart
Lottie.asset(
'assets/DynamicText.json',
delegates: LottieDelegates(values: [
ValueDelegate.text(
['Text layer'], // The path to the text element to change
value: 'The new text',
),
]),
)
```
- Improve stroke with offset
- Add support for reversed polystar paths
- Enforce order of operations to avoid rounding errors
## [1.2.2]
- Internal maintenance: fix lints for Flutter 2.10
## [1.2.1]
- Fix: Revert Cubic to `PathInterpolator.cubic`
## [1.2.0]
- Add support for gaussian blurs.
Example to blur some elements dynamically:
```dart
Lottie.asset(
'assets/AndroidWave.json',
delegates: LottieDelegates(values: [
ValueDelegate.blurRadius(
['**'], // The path to the element to blur
value: 20,
),
]),
)
```
- Add support for drop shadows.
Example to add a shadow dynamically:
```dart
Lottie.asset(
'assets/animation.json',
delegates: LottieDelegates(values: [
ValueDelegate.dropShadow(
['**'], // The path to the elements with shadow
value: const DropShadow(
color: Colors.blue,
direction: 140,
distance: 60,
radius: 10,
),
),
]),
)
```
## [1.1.0]
- Add `errorBuilder` callback to provide an alternative widget in case an error occurs during loading.
```dart

View File

@ -64,7 +64,7 @@ class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
@ -139,7 +139,7 @@ class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
_MyWidgetState createState() => _MyWidgetState();
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {

View File

@ -6,7 +6,6 @@ analyzer:
linter:
rules:
avoid_print: false
unnecessary_overrides: false # remove when mockito is fixed
always_declare_return_types: true
avoid_dynamic_calls: true

View File

@ -5,3 +5,9 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@ -26,23 +26,28 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "com.github.xvrh.lottie.sample"
minSdkVersion 16
targetSdkVersion 28
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.github.xvrh.lottie.example"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -60,7 +65,4 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.sample">
package="com.github.xvrh.lottie.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,21 +1,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.sample">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
package="com.github.xvrh.lottie.example">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

View File

@ -0,0 +1,6 @@
package com.github.xvrh.lottie.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,12 +0,0 @@
package com.github.xvrh.lottie.sample
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 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 -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,8 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- 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 -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.sample">
package="com.github.xvrh.lottie.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -14,7 +14,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View File

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

View File

@ -1,15 +1,11 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
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"

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":181.000007372281,"w":375,"h":375,"nm":"Square","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow2","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0,0,0,1]},{"t":151.000006150356,"s":[0,0.371857702732,1,1]}],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[127.5]},{"t":151.000006150356,"s":[127.5]}],"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":151.000006150356,"s":[360]}],"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[10]},{"t":151.000006150356,"s":[15]}],"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[20]},{"t":151.000006150356,"s":[50]}],"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[217.641,217.641],"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":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-4.68,-1.68],"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":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184.000007494474,"st":0,"bm":0}],"markers":[]}

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":121.000004928431,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":29,"nm":"Gaussian Blur2","np":5,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24.00000097754,"s":[120.7]}],"ix":1}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}}]},{"ty":5,"nm":"Bulge","np":9,"mn":"ADBE Bulge","ix":2,"en":1,"ef":[{"ty":0,"nm":"Horizontal Radius","mn":"ADBE Bulge-0001","ix":1,"v":{"a":0,"k":50,"ix":1}},{"ty":0,"nm":"Vertical Radius","mn":"ADBE Bulge-0002","ix":2,"v":{"a":0,"k":50,"ix":2}},{"ty":3,"nm":"Bulge Center","mn":"ADBE Bulge-0003","ix":3,"v":{"a":0,"k":[200,200],"ix":3}},{"ty":0,"nm":"Bulge Height","mn":"ADBE Bulge-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Taper Radius","mn":"ADBE Bulge-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Antialiasing (Best Qual Only)","mn":"ADBE Bulge-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":7,"nm":"Pinning","mn":"ADBE Bulge-0007","ix":7,"v":{"a":0,"k":0,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[274.975,274.975],"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":"tr","p":{"a":0,"k":[-4.513,7.487],"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":"Rectangle 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":121.000004928431,"st":0,"bm":0}],"markers":[]}

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":121.000004928431,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":29,"nm":"Gaussian Blur2","np":5,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24.00000097754,"s":[120.7]}],"ix":1}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}}]},{"ty":5,"nm":"Bulge","np":9,"mn":"ADBE Bulge","ix":2,"en":1,"ef":[{"ty":0,"nm":"Horizontal Radius","mn":"ADBE Bulge-0001","ix":1,"v":{"a":0,"k":50,"ix":1}},{"ty":0,"nm":"Vertical Radius","mn":"ADBE Bulge-0002","ix":2,"v":{"a":0,"k":50,"ix":2}},{"ty":3,"nm":"Bulge Center","mn":"ADBE Bulge-0003","ix":3,"v":{"a":0,"k":[200,200],"ix":3}},{"ty":0,"nm":"Bulge Height","mn":"ADBE Bulge-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Taper Radius","mn":"ADBE Bulge-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Antialiasing (Best Qual Only)","mn":"ADBE Bulge-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":7,"nm":"Pinning","mn":"ADBE Bulge-0007","ix":7,"v":{"a":0,"k":0,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[274.975,274.975],"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":"tr","p":{"a":0,"k":[-4.513,7.487],"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":"Rectangle 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,0,0,0.5,0.505,0,0.5,1,0.009,0,1],"ix":9}},"s":{"a":0,"k":[-125,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":121.000004928431,"st":0,"bm":0}],"markers":[]}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":121.000004928431,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":29,"nm":"Gaussian Blur2","np":5,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24.00000097754,"s":[120.7]}],"ix":1}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}}]},{"ty":5,"nm":"Bulge","np":9,"mn":"ADBE Bulge","ix":2,"en":1,"ef":[{"ty":0,"nm":"Horizontal Radius","mn":"ADBE Bulge-0001","ix":1,"v":{"a":0,"k":50,"ix":1}},{"ty":0,"nm":"Vertical Radius","mn":"ADBE Bulge-0002","ix":2,"v":{"a":0,"k":50,"ix":2}},{"ty":3,"nm":"Bulge Center","mn":"ADBE Bulge-0003","ix":3,"v":{"a":0,"k":[200,200],"ix":3}},{"ty":0,"nm":"Bulge Height","mn":"ADBE Bulge-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Taper Radius","mn":"ADBE Bulge-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Antialiasing (Best Qual Only)","mn":"ADBE Bulge-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":7,"nm":"Pinning","mn":"ADBE Bulge-0007","ix":7,"v":{"a":0,"k":0,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[274.975,274.975],"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":"tr","p":{"a":0,"k":[-4.513,7.487],"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":"Rectangle 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gs","o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":41,"ix":10},"g":{"p":3,"k":{"a":0,"k":[0,0.01,0,1,0.5,0.496,0,0.5,1,0.982,0,0],"ix":8}},"s":{"a":0,"k":[-45,0],"ix":4},"e":{"a":0,"k":[100,0],"ix":5},"t":1,"lc":1,"lj":1,"ml":4,"ml2":{"a":0,"k":4,"ix":13},"bm":0,"nm":"Gradient Stroke 1","mn":"ADBE Vector Graphic - G-Stroke","hd":false}],"ip":0,"op":121.000004928431,"st":0,"bm":0}],"markers":[]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,288 @@
{
"v": "5.8.2",
"fr": 24,
"ip": 0,
"op": 94,
"w": 150,
"h": 150,
"nm": "Anim_load",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "LF03",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": -19,
"ix": 10
},
"p": {
"a": 0,
"k": [
75,
76.005,
0
],
"ix": 2,
"l": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1,
"l": 2
},
"s": {
"a": 0,
"k": [
19.986,
19.986,
100
],
"ix": 6,
"l": 2
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ty": "sr",
"sy": 1,
"d": 3,
"pt": {
"a": 0,
"k": 14,
"ix": 3
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 4
},
"r": {
"a": 0,
"k": 0,
"ix": 5
},
"ir": {
"a": 0,
"k": 91.612,
"ix": 6
},
"is": {
"a": 0,
"k": 0,
"ix": 8
},
"or": {
"a": 0,
"k": 113.225,
"ix": 7
},
"os": {
"a": 0,
"k": 0,
"ix": 9
},
"ix": 1,
"nm": "Polystar Path 1",
"mn": "ADBE Vector Shape - Star",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
0.937254905701,
0.89411765337,
0.850980401039,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 30,
"ix": 5
},
"lc": 1,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "Stroke 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-1.979,
-0.604
],
"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": "Polystar 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 1,
"k": [
{
"i": {
"x": [
0.667
],
"y": [
1
]
},
"o": {
"x": [
0.333
],
"y": [
0
]
},
"t": -0.127,
"s": [
100
]
},
{
"t": 32,
"s": [
0
]
}
],
"ix": 1
},
"e": {
"a": 1,
"k": [
{
"i": {
"x": [
0.833
],
"y": [
1
]
},
"o": {
"x": [
0.333
],
"y": [
0
]
},
"t": 36,
"s": [
100
]
},
{
"t": 68,
"s": [
0
]
}
],
"ix": 2
},
"o": {
"a": 0,
"k": -43,
"ix": 3
},
"m": 1,
"ix": 2,
"nm": "Trim Paths 1",
"mn": "ADBE Vector Filter - Trim",
"hd": false
}
],
"ip": 0,
"op": 94,
"st": 18,
"bm": 0
}
],
"markers": []
}

View File

@ -0,0 +1,390 @@
{
"v": "5.8.1",
"fr": 29.9700012207031,
"ip": 0,
"op": 900.000036657751,
"w": 1000,
"h": 1000,
"nm": "Rounded Corners",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Shape Layer 1",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
500,
500,
0
],
"ix": 2,
"l": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1,
"l": 2
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6,
"l": 2
}
},
"ao": 0,
"shapes": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"o": [
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"v": [
[
-75,
-475
],
[
-75,
-75
],
[
-475,
-75
],
[
-475,
-475
]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "rc",
"d": 1,
"s": {
"a": 0,
"k": [
400,
400
],
"ix": 2
},
"p": {
"a": 0,
"k": [
275,
-275
],
"ix": 3
},
"r": {
"a": 0,
"k": 0,
"ix": 4
},
"nm": "Rectangle Path 1",
"mn": "ADBE Vector Shape - Rect",
"hd": false
},
{
"ty": "sr",
"sy": 1,
"d": 1,
"pt": {
"a": 0,
"k": 5,
"ix": 3
},
"p": {
"a": 0,
"k": [
275,
275
],
"ix": 4
},
"r": {
"a": 0,
"k": 0,
"ix": 5
},
"ir": {
"a": 0,
"k": 107,
"ix": 6
},
"is": {
"a": 0,
"k": 0,
"ix": 8
},
"or": {
"a": 0,
"k": 275,
"ix": 7
},
"os": {
"a": 0,
"k": 0,
"ix": 9
},
"ix": 3,
"nm": "Polystar Path 1",
"mn": "ADBE Vector Shape - Star",
"hd": false
},
{
"ind": 3,
"ty": "sh",
"ix": 4,
"ks": {
"a": 0,
"k": {
"i": [
[
0,
0
],
[
0,
0
],
[
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
],
[
0,
0
],
[
0,
0
]
],
"v": [
[
-275,
0
],
[
-212.107,
188.435
],
[
-13.459,
190.02
],
[
-173.237,
308.065
],
[
-113.359,
497.48
],
[
-275,
382
],
[
-436.641,
497.48
],
[
-376.763,
308.065
],
[
-536.541,
190.02
],
[
-337.893,
188.435
]
],
"c": true
},
"ix": 2
},
"nm": "Path 2",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "rd",
"nm": "Round Corners 1",
"r": {
"a": 0,
"k": 136,
"ix": 1
},
"ix": 5,
"mn": "ADBE Vector Filter - RC",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0,
0,
1
],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
}
],
"ip": 0,
"op": 900.000036657751,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":181.000007372281,"w":375,"h":375,"nm":"Square","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow2","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,1,0.609851837158,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":127.5,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":10,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":20,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[217.641,217.641],"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":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-4.68,-1.68],"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":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":184.000007494474,"st":0,"bm":0}],"markers":[]}

View File

@ -0,0 +1 @@
{"v":"5.7.7","fr":29.9700012207031,"ip":0,"op":121.000004928431,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":29,"nm":"Gaussian Blur2","np":5,"mn":"ADBE Gaussian Blur 2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Blurriness","mn":"ADBE Gaussian Blur 2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24.00000097754,"s":[120.7]}],"ix":1}},{"ty":7,"nm":"Blur Dimensions","mn":"ADBE Gaussian Blur 2-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Repeat Edge Pixels","mn":"ADBE Gaussian Blur 2-0003","ix":3,"v":{"a":0,"k":0,"ix":3}}]},{"ty":5,"nm":"Bulge","np":9,"mn":"ADBE Bulge","ix":2,"en":1,"ef":[{"ty":0,"nm":"Horizontal Radius","mn":"ADBE Bulge-0001","ix":1,"v":{"a":0,"k":50,"ix":1}},{"ty":0,"nm":"Vertical Radius","mn":"ADBE Bulge-0002","ix":2,"v":{"a":0,"k":50,"ix":2}},{"ty":3,"nm":"Bulge Center","mn":"ADBE Bulge-0003","ix":3,"v":{"a":0,"k":[200,200],"ix":3}},{"ty":0,"nm":"Bulge Height","mn":"ADBE Bulge-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Taper Radius","mn":"ADBE Bulge-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Antialiasing (Best Qual Only)","mn":"ADBE Bulge-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":7,"nm":"Pinning","mn":"ADBE Bulge-0007","ix":7,"v":{"a":0,"k":0,"ix":7}}]}],"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[274.975,274.975],"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":"tr","p":{"a":0,"k":[-4.513,7.487],"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":"Rectangle 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector 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":46,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":121.000004928431,"st":0,"bm":0}],"markers":[]}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -25,7 +25,7 @@ class __PageState extends State<_Page> {
void initState() {
super.initState();
SchedulerBinding.instance!.addPostFrameCallback((_) => _showLoader());
SchedulerBinding.instance.addPostFrameCallback((_) => _showLoader());
}
void _showLoader() {

View File

@ -7,7 +7,7 @@ class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

@ -15,7 +15,7 @@ class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [
Lottie.asset(
'assets/AndroidWave.json',
height: 300,
delegates: LottieDelegates(values: [
ValueDelegate.blurRadius(
['**'],
value: 20,
),
]),
),
],
),
),
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp());
@ -22,7 +21,7 @@ class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
_MyWidgetState createState() => _MyWidgetState();
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp());
@ -23,7 +22,7 @@ class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
_MyWidgetState createState() => _MyWidgetState();
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [
Lottie.asset(
'assets/Tests/Fill.json',
height: 300,
delegates: LottieDelegates(values: [
ValueDelegate.dropShadow(
['**'],
value: const DropShadow(
color: Colors.blue,
direction: 140,
distance: 60,
radius: 10,
),
),
]),
),
],
),
),
);
}
}

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:lottie/lottie.dart';
@ -11,7 +10,7 @@ class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
State<App> createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {
@ -72,7 +71,7 @@ class _AppState extends State<App> with TickerProviderStateMixin {
_color = newColor;
});
},
showLabel: false,
labelTypes: const [],
enableAlpha: false,
pickerAreaHeightPercent: 0.8,
),

View File

@ -10,7 +10,7 @@ class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
State<App> createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {

View File

@ -9,7 +9,7 @@ class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
State<App> createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {

View File

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

View File

@ -7,7 +7,7 @@ class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

@ -15,7 +15,7 @@ class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
State<App> createState() => _AppState();
}
class _AppState extends State<App> with TickerProviderStateMixin {

View File

@ -13,7 +13,7 @@ class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:lottie/lottie.dart';
@ -93,7 +92,7 @@ class Detail extends StatefulWidget {
const Detail(this.assetName, {Key? key}) : super(key: key);
@override
_DetailState createState() => _DetailState();
State<Detail> createState() => _DetailState();
}
class _DetailState extends State<Detail> with TickerProviderStateMixin {

View File

@ -15,8 +15,8 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
COCOAPODS: 1.10.1
COCOAPODS: 1.11.3

View File

@ -7,14 +7,14 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
version: "3.3.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@ -28,14 +28,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
clock:
dependency: transitive
description:
@ -49,28 +49,28 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.0.2"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "2.0.1"
file:
dependency: transitive
description:
@ -89,14 +89,14 @@ packages:
name: flutter_colorpicker
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
version: "1.0.3"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "2.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -108,126 +108,140 @@ packages:
name: golden_toolkit
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0"
version: "0.13.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.3"
version: "0.13.4"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.1"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "2.0.0"
logging:
dependency: "direct main"
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
lottie:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "1.1.0"
version: "1.4.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.7.0"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.11"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.16"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.10"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.7"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.0.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "2.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
version: "2.1.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.1.2"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.1"
version: "4.2.4"
sky_engine:
dependency: transitive
description: flutter
@ -239,7 +253,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -274,35 +288,35 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.9"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.2"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.5"
version: "2.7.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
version: "0.2.0+1"
sdks:
dart: ">=2.13.0 <3.0.0"
flutter: ">=1.20.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"

View File

@ -13,6 +13,7 @@ dependencies:
logging:
lottie:
path: ../
path:
path_provider:
dev_dependencies:

BIN
example/web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

35
example/web/manifest.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@ -14,4 +14,5 @@ export 'src/providers/lottie_provider.dart' show LottieProvider;
export 'src/providers/memory_provider.dart' show MemoryLottie;
export 'src/providers/network_provider.dart' show NetworkLottie;
export 'src/raw_lottie.dart' show RawLottie;
export 'src/value/drop_shadow.dart' show DropShadow;
export 'src/value_delegate.dart' show ValueDelegate;

View File

@ -7,6 +7,7 @@ import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
import '../../model/animatable/animatable_double_value.dart';
import '../../model/animatable/animatable_integer_value.dart';
import '../../model/content/drop_shadow_effect.dart';
import '../../model/content/shape_trim_path.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
@ -15,8 +16,10 @@ import '../../utils/dash_path.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../utils/utils.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/drop_shadow_keyframe_animation.dart';
import '../keyframe/value_callback_keyframe_animation.dart';
import 'content.dart';
import 'drawing_content.dart';
@ -39,6 +42,9 @@ abstract class BaseStrokeContent
final List<BaseKeyframeAnimation<Object, double>> _dashPatternAnimations;
final BaseKeyframeAnimation<Object, double>? _dashPatternOffsetAnimation;
BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation;
BaseKeyframeAnimation<double, double>? _blurAnimation;
double _blurMaskFilterRadius = 0;
DropShadowKeyframeAnimation? dropShadowAnimation;
BaseStrokeContent(this.lottieDrawable, this.layer,
{required StrokeCap cap,
@ -77,6 +83,17 @@ abstract class BaseStrokeContent
if (_dashPatternOffsetAnimation != null) {
_dashPatternOffsetAnimation!.addUpdateListener(onUpdateListener);
}
var blurEffect = layer.blurEffect;
if (blurEffect != null) {
_blurAnimation = blurEffect.blurriness.createAnimation()
..addUpdateListener(onUpdateListener);
layer.addAnimation(_blurAnimation);
}
var dropShadowEffect = layer.dropShadowEffect;
if (dropShadowEffect != null) {
dropShadowAnimation = DropShadowKeyframeAnimation(
onUpdateListener, layer, dropShadowEffect);
}
}
void onUpdateListener() {
@ -139,6 +156,18 @@ abstract class BaseStrokeContent
paint.colorFilter = _colorFilterAnimation!.value;
}
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
var blurRadius = blurAnimation.value;
if (blurRadius == 0) {
paint.maskFilter = null;
} else if (blurRadius != _blurMaskFilterRadius) {
var blur = layer.getBlurMaskFilter(blurRadius);
paint.maskFilter = blur;
}
_blurMaskFilterRadius = blurRadius;
}
for (var i = 0; i < _pathGroups.length; i++) {
var pathGroup = _pathGroups[i];
@ -153,6 +182,10 @@ abstract class BaseStrokeContent
}
L.endSection('StrokeContent#buildPath');
L.beginSection('StrokeContent#drawPath');
var dropShadow = dropShadowAnimation;
if (dropShadow != null) {
dropShadow.draw(canvas, _path);
}
canvas.drawPath(_withDashPattern(_path, parentMatrix), paint);
L.endSection('StrokeContent#drawPath');
}
@ -163,7 +196,8 @@ abstract class BaseStrokeContent
void _applyTrimPath(
Canvas canvas, _PathGroup pathGroup, Matrix4 parentMatrix) {
L.beginSection('StrokeContent#applyTrimPath');
if (pathGroup.trimPath == null) {
var trimPath = pathGroup.trimPath;
if (trimPath == null) {
L.endSection('StrokeContent#applyTrimPath');
return;
}
@ -172,13 +206,24 @@ abstract class BaseStrokeContent
_path.addPath(pathGroup.paths[j].getPath(), Offset.zero,
matrix4: parentMatrix.storage);
}
var animStartValue = trimPath.start.value / 100;
var animEndValue = trimPath.end.value / 100;
var animOffsetValue = trimPath.offset.value / 360;
// If the start-end is ~100, consider it to be the full path.
if (animStartValue < 0.01 && animEndValue > 0.99) {
canvas.drawPath(_path, paint);
L.endSection('StrokeContent#applyTrimPath');
return;
}
var pathMetrics = _path.computeMetrics().toList();
var totalLength = pathMetrics.fold<double>(0.0, (a, b) => a + b.length);
var trimPath = pathGroup.trimPath!;
var offsetLength = totalLength * trimPath.offset.value / 360.0;
var startLength = totalLength * trimPath.start.value / 100.0 + offsetLength;
var endLength = totalLength * trimPath.end.value / 100.0 + offsetLength;
var offsetLength = totalLength * animOffsetValue;
var startLength = totalLength * animStartValue + offsetLength;
var endLength = min(totalLength * animEndValue + offsetLength,
startLength + totalLength - 1);
var currentLength = 0.0;
for (var j = pathGroup.paths.length - 1; j >= 0; j--) {
@ -308,10 +353,31 @@ abstract class BaseStrokeContent
} else {
_colorFilterAnimation =
ValueCallbackKeyframeAnimation<ColorFilter, ColorFilter?>(
callback as LottieValueCallback<ColorFilter>, null);
_colorFilterAnimation!.addUpdateListener(onUpdateListener);
callback as LottieValueCallback<ColorFilter>, null)
..addUpdateListener(onUpdateListener);
layer.addAnimation(_colorFilterAnimation);
}
} else if (property == LottieProperty.blurRadius) {
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
blurAnimation
.setValueCallback(callback as LottieValueCallback<double>?);
} else {
_blurAnimation = blurAnimation = ValueCallbackKeyframeAnimation(
callback as LottieValueCallback<double>?, 0)
..addUpdateListener(onUpdateListener);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.dropShadow) {
var dropShadowAnimation = this.dropShadowAnimation;
if (dropShadowAnimation == null) {
var effect = DropShadowEffect.createEmpty();
this.dropShadowAnimation = dropShadowAnimation = dropShadowAnimation =
DropShadowKeyframeAnimation(onUpdateListener, layer, effect);
}
dropShadowAnimation
.setCallback(callback as LottieValueCallback<DropShadow>?);
}
}
}

View File

@ -3,14 +3,17 @@ import 'package:vector_math/vector_math_64.dart';
import '../../l.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
import '../../model/content/drop_shadow_effect.dart';
import '../../model/content/shape_fill.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/drop_shadow_keyframe_animation.dart';
import '../keyframe/value_callback_keyframe_animation.dart';
import 'content.dart';
import 'drawing_content.dart';
@ -29,10 +32,25 @@ class FillContent implements DrawingContent, KeyPathElementContent {
late final BaseKeyframeAnimation<int, int> _opacityAnimation;
BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation;
final LottieDrawable lottieDrawable;
BaseKeyframeAnimation<double, double>? _blurAnimation;
double _blurMaskFilterRadius = 0;
DropShadowKeyframeAnimation? dropShadowAnimation;
FillContent(this.lottieDrawable, this.layer, ShapeFill fill)
: name = fill.name,
_hidden = fill.hidden {
var blurEffect = layer.blurEffect;
if (blurEffect != null) {
_blurAnimation = blurEffect.blurriness.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_blurAnimation);
}
var dropShadowEffect = layer.dropShadowEffect;
if (dropShadowEffect != null) {
dropShadowAnimation =
DropShadowKeyframeAnimation(onValueChanged, layer, dropShadowEffect);
}
if (fill.color == null || fill.opacity == null) {
return;
}
@ -80,6 +98,18 @@ class FillContent implements DrawingContent, KeyPathElementContent {
_paint.colorFilter = _colorFilterAnimation!.value;
}
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
var blurRadius = blurAnimation.value;
if (blurRadius == 0) {
_paint.maskFilter = null;
} else if (blurRadius != _blurMaskFilterRadius) {
var blur = layer.getBlurMaskFilter(blurRadius);
_paint.maskFilter = blur;
}
_blurMaskFilterRadius = blurRadius;
}
_path.reset();
for (var i = 0; i < _paths.length; i++) {
_path.addPath(_paths[i].getPath(), Offset.zero);
@ -87,6 +117,10 @@ class FillContent implements DrawingContent, KeyPathElementContent {
canvas.save();
canvas.transform(parentMatrix.storage);
var dropShadow = dropShadowAnimation;
if (dropShadow != null) {
dropShadow.draw(canvas, _path);
}
canvas.drawPath(_path, _paint);
canvas.restore();
@ -132,6 +166,28 @@ class FillContent implements DrawingContent, KeyPathElementContent {
..addUpdateListener(onValueChanged);
layer.addAnimation(_colorFilterAnimation);
}
} else if (property == LottieProperty.blurRadius) {
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
blurAnimation
.setValueCallback(callback as LottieValueCallback<double>?);
} else {
var callbackBlur = callback as LottieValueCallback<double>?;
_blurAnimation = blurAnimation = ValueCallbackKeyframeAnimation(
callbackBlur, callbackBlur?.value ?? 0)
..addUpdateListener(onValueChanged);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.dropShadow) {
var dropShadowAnimation = this.dropShadowAnimation;
if (dropShadowAnimation == null) {
var effect = DropShadowEffect.createEmpty();
this.dropShadowAnimation = dropShadowAnimation = dropShadowAnimation =
DropShadowKeyframeAnimation(onValueChanged, layer, effect);
}
dropShadowAnimation
.setCallback(callback as LottieValueCallback<DropShadow>?);
}
}
}

View File

@ -3,6 +3,7 @@ import 'package:vector_math/vector_math_64.dart';
import '../../l.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
import '../../model/content/drop_shadow_effect.dart';
import '../../model/content/gradient_color.dart';
import '../../model/content/gradient_fill.dart';
import '../../model/content/gradient_type.dart';
@ -11,8 +12,10 @@ import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/drop_shadow_keyframe_animation.dart';
import '../keyframe/value_callback_keyframe_animation.dart';
import 'content.dart';
import 'drawing_content.dart';
@ -38,6 +41,9 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
_colorCallbackAnimation;
final LottieDrawable lottieDrawable;
final int _cacheSteps;
BaseKeyframeAnimation<double, double>? _blurAnimation;
double _blurMaskFilterRadius = 0;
DropShadowKeyframeAnimation? dropShadowAnimation;
GradientFillContent(this.lottieDrawable, this.layer, this._fill)
: _cacheSteps =
@ -59,6 +65,18 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
_endPointAnimation.addUpdateListener(invalidate);
layer.addAnimation(_endPointAnimation);
var blurEffect = layer.blurEffect;
if (blurEffect != null) {
_blurAnimation = blurEffect.blurriness.createAnimation()
..addUpdateListener(invalidate);
layer.addAnimation(_blurAnimation);
}
var dropShadowEffect = layer.dropShadowEffect;
if (dropShadowEffect != null) {
dropShadowAnimation =
DropShadowKeyframeAnimation(invalidate, layer, dropShadowEffect);
}
}
@override
@ -103,6 +121,18 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
_paint.colorFilter = _colorFilterAnimation!.value;
}
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
var blurRadius = blurAnimation.value;
if (blurRadius == 0) {
_paint.maskFilter = null;
} else if (blurRadius != _blurMaskFilterRadius) {
var blur = layer.getBlurMaskFilter(blurRadius);
_paint.maskFilter = blur;
}
_blurMaskFilterRadius = blurRadius;
}
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
_paint.setAlpha(alpha.clamp(0, 255).toInt());
@ -112,6 +142,10 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
canvas.save();
canvas.transform(parentMatrix.storage);
var dropShadow = dropShadowAnimation;
if (dropShadow != null) {
dropShadow.draw(canvas, _path);
}
canvas.drawPath(_path, _paint);
canvas.restore();
L.endSection('GradientFillContent#draw');
@ -246,6 +280,27 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
..addUpdateListener(invalidate);
layer.addAnimation(_colorCallbackAnimation);
}
} else if (property == LottieProperty.blurRadius) {
var blurAnimation = _blurAnimation;
if (blurAnimation != null) {
blurAnimation
.setValueCallback(callback as LottieValueCallback<double>?);
} else {
_blurAnimation = blurAnimation = ValueCallbackKeyframeAnimation(
callback as LottieValueCallback<double>?, 0)
..addUpdateListener(invalidate);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.dropShadow) {
var dropShadowAnimation = this.dropShadowAnimation;
if (dropShadowAnimation == null) {
var effect = DropShadowEffect.createEmpty();
this.dropShadowAnimation = dropShadowAnimation = dropShadowAnimation =
DropShadowKeyframeAnimation(invalidate, layer, effect);
}
dropShadowAnimation
.setCallback(callback as LottieValueCallback<DropShadow>?);
}
}
}

View File

@ -134,6 +134,9 @@ class PolystarContent implements PathContent, KeyPathElementContent {
currentAngle = radians(currentAngle);
// adjust current angle for partial points
var anglePerPoint = 2 * pi / points;
if (_polystarShape.isReversed) {
anglePerPoint *= -1;
}
var halfAnglePerPoint = anglePerPoint / 2.0;
var partialPointAmount = points - points.toInt();
if (partialPointAmount != 0) {

View File

@ -15,6 +15,7 @@ import 'compound_trim_path_content.dart';
import 'content.dart';
import 'key_path_element_content.dart';
import 'path_content.dart';
import 'rounded_corners_content.dart';
import 'trim_path_content.dart';
class RectangleContent implements KeyPathElementContent, PathContent {
@ -29,6 +30,9 @@ class RectangleContent implements KeyPathElementContent, PathContent {
final BaseKeyframeAnimation<Object, double> _cornerRadiusAnimation;
final CompoundTrimPathContent _trimPaths = CompoundTrimPathContent();
/// This corner radius is from a layer item. The first one is from the roundedness on this specific rect.
BaseKeyframeAnimation<double, double>? _roundedCornersAnimation;
bool _isPathValid = false;
RectangleContent(
@ -61,6 +65,8 @@ class RectangleContent implements KeyPathElementContent, PathContent {
var trimPath = content;
_trimPaths.addTrimPath(trimPath);
trimPath.addListener(invalidate);
} else if (content is RoundedCornersContent) {
_roundedCornersAnimation = content.roundedCorners;
}
}
}
@ -82,6 +88,10 @@ class RectangleContent implements KeyPathElementContent, PathContent {
var halfWidth = size.dx / 2.0;
var halfHeight = size.dy / 2.0;
var radius = _cornerRadiusAnimation.value;
var roundedCornersAnimation = _roundedCornersAnimation;
if (radius == 0 && roundedCornersAnimation != null) {
radius = min(roundedCornersAnimation.value, min(halfWidth, halfHeight));
}
var maxRadius = min(halfWidth, halfHeight);
if (radius > maxRadius) {
radius = maxRadius;

View File

@ -0,0 +1,236 @@
import 'dart:math' as math;
import 'dart:ui';
import '../../lottie_drawable.dart';
import '../../model/content/rounded_corners.dart';
import '../../model/content/shape_data.dart';
import '../../model/cubic_curve_data.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'content.dart';
import 'shape_modifier_content.dart';
class RoundedCornersContent implements ShapeModifierContent {
/// Copied from:
/// https://github.com/airbnb/lottie-web/blob/bb71072a26e03f1ca993da60915860f39aae890b/player/js/utils/common.js#L47
static const _roundedCornerMagicNumber = 0.5519;
final LottieDrawable lottieDrawable;
@override
final String name;
final BaseKeyframeAnimation<double, double> roundedCorners;
ShapeData? shapeData;
RoundedCornersContent(
this.lottieDrawable, BaseLayer layer, RoundedCorners roundedCorners)
: name = roundedCorners.name,
roundedCorners = roundedCorners.cornerRadius.createAnimation() {
layer.addAnimation(this.roundedCorners);
this.roundedCorners.addUpdateListener(_onValueChanged);
}
void _onValueChanged() {
lottieDrawable.invalidateSelf();
}
@override
void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
// Do nothing.
}
/// Rounded corner algorithm:
/// Iterate through each vertex.
/// If a vertex is a sharp corner, it rounds it.
/// If a vertex has control points, it is already rounded, so it does nothing.
/// <p>
/// To round a vertex:
/// Split the vertex into two.
/// Move vertex 1 directly towards the previous vertex.
/// Set vertex 1's in control point to itself so it is not rounded on that side.
/// Extend vertex 1's out control point towards the original vertex.
/// <p>
/// Repeat for vertex 2:
/// Move vertex 2 directly towards the next vertex.
/// Set vertex 2's out point to itself so it is not rounded on that side.
/// Extend vertex 2's in control point towards the original vertex.
/// <p>
/// The distance that the vertices and control points are moved are relative to the
/// shape's vertex distances and the roundedness set in the animation.
@override
ShapeData modifyShape(ShapeData startingShapeData) {
var startingCurves = startingShapeData.curves;
if (startingCurves.length <= 2) {
return startingShapeData;
}
var roundedness = roundedCorners.value;
if (roundedness == 0) {
return startingShapeData;
}
var modifiedShapeData = _getShapeData(startingShapeData);
modifiedShapeData.setInitialPoint(
startingShapeData.initialPoint.dx, startingShapeData.initialPoint.dy);
var modifiedCurves = modifiedShapeData.curves;
var modifiedCurvesIndex = 0;
var isClosed = startingShapeData.isClosed;
// i represents which vertex we are currently on. Refer to the docs of CubicCurveData prior to working with
// this code.
// When i == 0
// vertex=ShapeData.initialPoint
// inCp=if closed vertex else curves[size - 1].cp2
// outCp=curves[0].cp1
// When i == 1
// vertex=curves[0].vertex
// inCp=curves[0].cp2
// outCp=curves[1].cp1.
// When i == size - 1
// vertex=curves[size - 1].vertex
// inCp=curves[size - 1].cp2
// outCp=if closed vertex else curves[0].cp1
for (var i = 0; i < startingCurves.length; i++) {
var startingCurve = startingCurves[i];
var previousCurve =
startingCurves[floorMod(i - 1, startingCurves.length)];
var previousPreviousCurve =
startingCurves[floorMod(i - 2, startingCurves.length)];
var vertex = (i == 0 && !isClosed)
? startingShapeData.initialPoint
: previousCurve.vertex;
var inPoint =
(i == 0 && !isClosed) ? vertex : previousCurve.controlPoint2;
var outPoint = startingCurve.controlPoint1;
var previousVertex = previousPreviousCurve.vertex;
var nextVertex = startingCurve.vertex;
// We can't round the corner of the end of a non-closed curve.
var isEndOfCurve = !startingShapeData.isClosed &&
(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;
var dyToPreviousVertex = vertex.dy - previousVertex.dy;
var dxToNextVertex = nextVertex.dx - vertex.dx;
var dyToNextVertex = nextVertex.dy - vertex.dy;
var dToPreviousVertex = hypot(dxToPreviousVertex, dyToPreviousVertex);
var dToNextVertex = hypot(dxToNextVertex, dyToNextVertex);
double previousVertexPercent =
math.min(roundedness / dToPreviousVertex, 0.5);
double nextVertexPercent = math.min(roundedness / dToNextVertex, 0.5);
// Split the vertex into two and move each vertex towards the previous/next vertex.
var newVertex1X =
vertex.dx + (previousVertex.dx - vertex.dx) * previousVertexPercent;
var newVertex1Y =
vertex.dy + (previousVertex.dy - vertex.dy) * previousVertexPercent;
var newVertex2X =
vertex.dx + (nextVertex.dx - vertex.dx) * nextVertexPercent;
var newVertex2Y =
vertex.dy + (nextVertex.dy - vertex.dy) * nextVertexPercent;
// Extend the new vertex control point towards the original vertex.
var newVertex1OutPointX =
newVertex1X - (newVertex1X - vertex.dx) * _roundedCornerMagicNumber;
var newVertex1OutPointY =
newVertex1Y - (newVertex1Y - vertex.dy) * _roundedCornerMagicNumber;
var newVertex2InPointX =
newVertex2X - (newVertex2X - vertex.dx) * _roundedCornerMagicNumber;
var newVertex2InPointY =
newVertex2Y - (newVertex2Y - vertex.dy) * _roundedCornerMagicNumber;
// Remap vertex/in/out point to CubicCurveData.
// Refer to the docs for CubicCurveData for more info on the difference.
var previousCurveData = modifiedCurves[
floorMod(modifiedCurvesIndex - 1, modifiedCurves.length)];
var currentCurveData = modifiedCurves[modifiedCurvesIndex];
previousCurveData.controlPoint2 = Offset(newVertex1X, newVertex1Y);
previousCurveData.vertex = Offset(newVertex1X, newVertex1Y);
if (i == 0) {
modifiedShapeData.setInitialPoint(newVertex1X, newVertex1Y);
}
currentCurveData.controlPoint1 =
Offset(newVertex1OutPointX, newVertex1OutPointY);
modifiedCurvesIndex++;
previousCurveData = currentCurveData;
currentCurveData = modifiedCurves[modifiedCurvesIndex];
previousCurveData.controlPoint2 =
Offset(newVertex2InPointX, newVertex2InPointY);
previousCurveData.vertex = Offset(newVertex2X, newVertex2Y);
currentCurveData.controlPoint1 = Offset(newVertex2X, newVertex2Y);
modifiedCurvesIndex++;
} else {
// This vertex is not a point. Don't modify it. Refer to the documentation above and for CubicCurveData for mapping a vertex
// oriented point to CubicCurveData (path segments).
var previousCurveData = modifiedCurves[
floorMod(modifiedCurvesIndex - 1, modifiedCurves.length)];
var currentCurveData = modifiedCurves[modifiedCurvesIndex];
previousCurveData.controlPoint2 =
Offset(previousCurve.vertex.dx, previousCurve.vertex.dy);
previousCurveData.vertex =
Offset(previousCurve.vertex.dx, previousCurve.vertex.dy);
currentCurveData.controlPoint1 =
Offset(startingCurve.vertex.dx, startingCurve.vertex.dy);
modifiedCurvesIndex++;
}
}
return modifiedShapeData;
}
/// Returns a shape data with the correct number of vertices for the rounded corners shape.
/// This just returns the object. It does not update any values within the shape.
ShapeData _getShapeData(ShapeData startingShapeData) {
var startingCurves = startingShapeData.curves;
var isClosed = startingShapeData.isClosed;
var vertices = 0;
for (var i = startingCurves.length - 1; i >= 0; i--) {
var startingCurve = startingCurves[i];
var previousCurve =
startingCurves[floorMod(i - 1, startingCurves.length)];
var vertex = (i == 0 && !isClosed)
? startingShapeData.initialPoint
: previousCurve.vertex;
var inPoint =
(i == 0 && !isClosed) ? vertex : previousCurve.controlPoint2;
var outPoint = startingCurve.controlPoint1;
var isEndOfCurve = !startingShapeData.isClosed &&
(i == 0 && i == startingCurves.length - 1);
if (inPoint == vertex && outPoint == vertex && !isEndOfCurve) {
vertices += 2;
} else {
vertices += 1;
}
}
var shapeData = this.shapeData;
if (shapeData == null || shapeData.curves.length != vertices) {
var newCurves = <CubicCurveData>[];
for (var i = 0; i < vertices; i++) {
newCurves.add(CubicCurveData());
}
this.shapeData = shapeData =
ShapeData(newCurves, initialPoint: Offset.zero, closed: false);
}
shapeData.setClosed(isClosed);
return shapeData;
}
/// Copied from the API 24+ AOSP source.
static int floorMod(int x, int y) {
return x - floorDiv(x, y) * y;
}
/// Copied from the API 24+ AOSP source.
static int floorDiv(int x, int y) {
var r = x ~/ y;
// if the signs are different and modulo not zero, round down
if ((x ^ y) < 0 && (r * y != x)) {
r--;
}
return r;
}
}

View File

@ -5,10 +5,11 @@ import '../../model/content/shape_trim_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/path_factory.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/shape_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
import 'content.dart';
import 'path_content.dart';
import 'shape_modifier_content.dart';
import 'trim_path_content.dart';
class ShapeContent implements PathContent {
@ -17,7 +18,7 @@ class ShapeContent implements PathContent {
final ShapePath _shape;
final LottieDrawable lottieDrawable;
final BaseKeyframeAnimation<Object, Path> _shapeAnimation;
final ShapeKeyframeAnimation _shapeAnimation;
bool _isPathValid = false;
final _trimPaths = CompoundTrimPathContent();
@ -35,6 +36,7 @@ class ShapeContent implements PathContent {
@override
void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
List<ShapeModifierContent>? shapeModifierContents;
for (var i = 0; i < contentsBefore.length; i++) {
var content = contentsBefore[i];
if (content is TrimPathContent &&
@ -43,8 +45,12 @@ class ShapeContent implements PathContent {
var trimPath = content;
_trimPaths.addTrimPath(trimPath);
trimPath.addListener(_invalidate);
} else if (content is ShapeModifierContent) {
shapeModifierContents ??= [];
shapeModifierContents.add(content);
}
}
_shapeAnimation.setShapeModifiers(shapeModifierContents);
}
@override

View File

@ -0,0 +1,6 @@
import '../../model/content/shape_data.dart';
import 'content.dart';
abstract class ShapeModifierContent extends Content {
ShapeData modifyShape(ShapeData shapeData);
}

View File

@ -0,0 +1,108 @@
import 'dart:math' as math;
import 'dart:ui';
import '../../lottie_property.dart';
import '../../model/content/drop_shadow_effect.dart';
import '../../model/layer/base_layer.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_frame_info.dart';
import '../../value/lottie_value_callback.dart';
import 'base_keyframe_animation.dart';
import 'color_keyframe_animation.dart';
class DropShadowKeyframeAnimation {
static const double _degToRad = math.pi / 180.0;
final void Function() listener;
late final ColorKeyframeAnimation _color;
late final BaseKeyframeAnimation<double, double> _opacity;
late final BaseKeyframeAnimation<double, double> _direction;
late final BaseKeyframeAnimation<double, double> _distance;
late final BaseKeyframeAnimation<double, double> _radius;
Paint? _paint;
DropShadowKeyframeAnimation(
this.listener, BaseLayer layer, DropShadowEffect dropShadowEffect) {
_color = dropShadowEffect.color.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_color);
_opacity = dropShadowEffect.opacity.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_opacity);
_direction = dropShadowEffect.direction.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_direction);
_distance = dropShadowEffect.distance.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_distance);
_radius = dropShadowEffect.radius.createAnimation()
..addUpdateListener(onValueChanged);
layer.addAnimation(_radius);
}
void onValueChanged() {
_paint = null;
listener();
}
void draw(Canvas canvas, Path path) {
var directionRad = _direction.value * _degToRad;
var distance = _distance.value;
var x = math.sin(directionRad) * distance;
var y = math.cos(directionRad + math.pi) * distance;
var baseColor = _color.value;
var opacity = _opacity.value.round();
var color = baseColor.withAlpha(opacity);
var radius = _radius.value;
var sigma = radius * 0.57735 + 0.5;
var paint = _paint;
paint ??= _paint = Paint()
..color = color
..maskFilter = MaskFilter.blur(BlurStyle.normal, sigma);
canvas.drawPath(path.shift(Offset(x, y)), paint);
}
void setCallback(LottieValueCallback<DropShadow>? callback) {
if (callback != null) {
_color.setValueCallback(_createCallback(
callback, (c) => c?.color ?? const Color(0xff000000)));
_opacity.setValueCallback(
_createCallback(callback, (c) => c?.color.alpha.toDouble() ?? 255));
_direction.setValueCallback(
_createCallback(callback, (c) => c?.direction ?? 0));
_distance
.setValueCallback(_createCallback(callback, (c) => c?.distance ?? 0));
_radius
.setValueCallback(_createCallback(callback, (c) => c?.radius ?? 0));
} else {
_color.setValueCallback(null);
_opacity.setValueCallback(null);
_direction.setValueCallback(null);
_distance.setValueCallback(null);
_radius.setValueCallback(null);
}
}
LottieValueCallback<T> _createCallback<T>(
LottieValueCallback<DropShadow> callback,
T Function(DropShadow?) selector) {
return LottieValueCallback<T>(null)
..callback = (info) {
onValueChanged();
var frameInfo = LottieFrameInfo<DropShadow>(
info.startFrame,
info.endFrame,
LottieProperty.dropShadow,
LottieProperty.dropShadow,
info.linearKeyframeProgress,
info.interpolatedKeyframeProgress,
info.overallProgress,
);
var dropShadow = callback.getValue(frameInfo);
return selector(dropShadow);
};
}
}

View File

@ -3,11 +3,13 @@ import '../../model/content/shape_data.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/keyframe.dart';
import '../content/shape_modifier_content.dart';
import 'base_keyframe_animation.dart';
class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
final ShapeData _tempShapeData = ShapeData.empty();
final Path _tempPath = PathFactory.create();
List<ShapeModifierContent>? _shapeModifiers;
ShapeKeyframeAnimation(List<Keyframe<ShapeData>> keyframes)
: super(keyframes);
@ -19,7 +21,18 @@ class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
_tempShapeData.interpolateBetween(
startShapeData, endShapeData, keyframeProgress);
MiscUtils.getPathFromData(_tempShapeData, _tempPath);
var modifiedShapeData = _tempShapeData;
var shapeModifiers = _shapeModifiers;
if (shapeModifiers != null) {
for (var i = shapeModifiers.length - 1; i >= 0; i--) {
modifiedShapeData = shapeModifiers[i].modifyShape(modifiedShapeData);
}
}
MiscUtils.getPathFromData(modifiedShapeData, _tempPath);
return _tempPath;
}
void setShapeModifiers(List<ShapeModifierContent>? shapeModifiers) {
_shapeModifiers = shapeModifiers;
}
}

View File

@ -1,5 +1,7 @@
import '../../model/document_data.dart';
import '../../value/keyframe.dart';
import '../../value/lottie_frame_info.dart';
import '../../value/lottie_value_callback.dart';
import 'keyframe_animation.dart';
class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> {
@ -9,10 +11,58 @@ class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> {
@override
DocumentData getValue(
Keyframe<DocumentData> keyframe, double keyframeProgress) {
if (keyframeProgress != 1.0 || keyframe.endValue == null) {
var valueCallback = this.valueCallback;
if (valueCallback != null) {
return valueCallback.getValueInternal(
keyframe.startFrame,
keyframe.endFrame ?? double.maxFinite,
keyframe.startValue,
keyframe.endValue ?? keyframe.startValue,
keyframeProgress,
getInterpolatedCurrentKeyframeProgress(),
progress)!;
} else if (keyframeProgress != 1.0 || keyframe.endValue == null) {
return keyframe.startValue!;
} else {
return keyframe.endValue!;
}
}
void setStringValueCallback(LottieValueCallback<String> valueCallback) {
super.setValueCallback(_DocumentDataValueCallback(valueCallback));
}
}
class _DocumentDataValueCallback extends LottieValueCallback<DocumentData> {
final LottieValueCallback<String> valueCallback;
_DocumentDataValueCallback(this.valueCallback) : super(null);
@override
DocumentData getValue(LottieFrameInfo<DocumentData> frameInfo) {
var stringFrameInfo = LottieFrameInfo<String>(
frameInfo.startFrame,
frameInfo.endFrame,
frameInfo.startValue!.text,
frameInfo.endValue!.text,
frameInfo.linearKeyframeProgress,
frameInfo.interpolatedKeyframeProgress,
frameInfo.overallProgress);
var text = valueCallback.getValue(stringFrameInfo)!;
var baseDocumentData = frameInfo.interpolatedKeyframeProgress == 1
? frameInfo.endValue!
: frameInfo.startValue!;
return DocumentData(
text: text,
fontName: baseDocumentData.fontName,
size: baseDocumentData.size,
justification: baseDocumentData.justification,
tracking: baseDocumentData.tracking,
lineHeight: baseDocumentData.lineHeight,
baselineShift: baseDocumentData.baselineShift,
color: baseDocumentData.color,
strokeColor: baseDocumentData.strokeColor,
strokeWidth: baseDocumentData.strokeWidth,
strokeOverFill: baseDocumentData.strokeOverFill);
}
}

View File

@ -1,13 +1,9 @@
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'composition.dart';
import 'frame_rate.dart';
import 'l.dart';
import 'lottie_builder.dart';
import 'options.dart';
import 'providers/load_image.dart';
/// A widget to display a loaded [LottieComposition].
/// The [controller] property allows to specify a custom AnimationController that
@ -30,6 +26,7 @@ class Lottie extends StatefulWidget {
this.delegates,
this.options,
bool? addRepaintBoundary,
this.filterQuality,
}) : animate = animate ?? true,
reverse = reverse ?? false,
repeat = repeat ?? true,
@ -58,6 +55,7 @@ class Lottie extends StatefulWidget {
Alignment? alignment,
String? package,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
}) =>
LottieBuilder.asset(
@ -81,6 +79,7 @@ class Lottie extends StatefulWidget {
alignment: alignment,
package: package,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
@ -104,6 +103,7 @@ class Lottie extends StatefulWidget {
BoxFit? fit,
Alignment? alignment,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
}) =>
LottieBuilder.file(
@ -125,6 +125,7 @@ class Lottie extends StatefulWidget {
fit: fit,
alignment: alignment,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
@ -148,6 +149,7 @@ class Lottie extends StatefulWidget {
BoxFit? fit,
Alignment? alignment,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
}) =>
LottieBuilder.memory(
@ -169,6 +171,7 @@ class Lottie extends StatefulWidget {
fit: fit,
alignment: alignment,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
@ -192,6 +195,7 @@ class Lottie extends StatefulWidget {
BoxFit? fit,
Alignment? alignment,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning,
}) =>
LottieBuilder.network(
@ -213,6 +217,7 @@ class Lottie extends StatefulWidget {
fit: fit,
alignment: alignment,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
@ -303,13 +308,19 @@ class Lottie extends StatefulWidget {
/// This property is `true` by default.
final bool addRepaintBoundary;
/// The quality of the image layer. See [FilterQuality]
/// [FilterQuality.high] is highest quality but slowest.
///
/// Defaults to [FilterQuality.none]
final FilterQuality? filterQuality;
static bool get traceEnabled => L.traceEnabled;
static set traceEnabled(bool enabled) {
L.traceEnabled = enabled;
}
@override
_LottieState createState() => _LottieState();
State<Lottie> createState() => _LottieState();
}
class _LottieState extends State<Lottie> with TickerProviderStateMixin {
@ -370,6 +381,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
height: widget.height,
fit: widget.fit,
alignment: widget.alignment,
filterQuality: widget.filterQuality,
);
},
);

View File

@ -1,12 +1,12 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'composition.dart';
import 'frame_rate.dart';
import 'lottie.dart';
import 'lottie_delegates.dart';
import 'options.dart';
import 'providers/asset_provider.dart';
import 'providers/file_provider.dart';
import 'providers/load_image.dart';
@ -59,6 +59,7 @@ class LottieBuilder extends StatefulWidget {
this.fit,
this.alignment,
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : super(key: key);
@ -83,6 +84,7 @@ class LottieBuilder extends StatefulWidget {
this.fit,
this.alignment,
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie = NetworkLottie(src,
headers: headers, imageProviderFactory: imageProviderFactory),
@ -117,6 +119,7 @@ class LottieBuilder extends StatefulWidget {
this.fit,
this.alignment,
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory),
super(key: key);
@ -143,6 +146,7 @@ class LottieBuilder extends StatefulWidget {
this.alignment,
String? package,
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie = AssetLottie(name,
bundle: bundle,
@ -170,6 +174,7 @@ class LottieBuilder extends StatefulWidget {
this.fit,
this.alignment,
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
}) : lottie =
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
@ -361,6 +366,12 @@ class LottieBuilder extends StatefulWidget {
/// This property is `true` by default.
final bool? addRepaintBoundary;
/// The quality of the image layer. See [FilterQuality]
/// [FilterQuality.high] is highest quality but slowest.
///
/// Defaults to [FilterQuality.none]
final FilterQuality? filterQuality;
/// A callback called when there is a warning during the loading or painting
/// of the animation.
final WarningCallback? onWarning;
@ -398,7 +409,7 @@ class LottieBuilder extends StatefulWidget {
final ImageErrorWidgetBuilder? errorBuilder;
@override
_LottieBuilderState createState() => _LottieBuilderState();
State<LottieBuilder> createState() => _LottieBuilderState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
@ -482,6 +493,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
fit: widget.fit,
alignment: widget.alignment,
addRepaintBoundary: widget.addRepaintBoundary,
filterQuality: widget.filterQuality,
);
if (widget.frameBuilder != null) {

View File

@ -1,7 +1,8 @@
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'composition.dart';
import 'lottie_drawable.dart';
import 'lottie_image_asset.dart';
import 'value_delegate.dart';
// TODO(xha): recognize style Bold, Medium, Regular, SemiBold, etc...

View File

@ -1,6 +1,5 @@
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:vector_math/vector_math_64.dart';
import 'composition.dart';
import 'frame_rate.dart';
import 'lottie_delegates.dart';
@ -17,16 +16,15 @@ class LottieDrawable {
final Size size;
LottieDelegates? _delegates;
bool _isDirty = true;
final bool enableMergePaths;
bool enableMergePaths = false;
FilterQuality? filterQuality;
/// Gives a suggestion whether to paint with anti-aliasing, or not. Default is true.
bool antiAliasingSuggested = true;
LottieDrawable(this.composition,
{LottieDelegates? delegates, bool? enableMergePaths})
LottieDrawable(this.composition, {LottieDelegates? delegates})
: size = Size(composition.bounds.width.toDouble(),
composition.bounds.height.toDouble()),
enableMergePaths = enableMergePaths ?? false {
composition.bounds.height.toDouble()) {
this.delegates = delegates;
_compositionLayer = CompositionLayer(
this, LayerParser.parse(composition), composition.layers, composition);

View File

@ -1,4 +1,5 @@
import 'dart:ui';
import 'value/drop_shadow.dart';
/// Property values are the same type as the generic type of their corresponding
/// {@link LottieValueCallback}. With this, we can use generics to maintain type safety
@ -122,8 +123,18 @@ abstract class LottieProperty {
/// In Dp
static const double textSize = 28.0;
/// In Px
static const double blurRadius = 29.0;
static const dropShadow = DropShadow(
color: Color(0x00000000), direction: 0, distance: 0, radius: 0);
/// Set the color filter for an entire drawable content. Can be applied to fills, strokes, images, and solids.
static const ColorFilter colorFilter =
ColorFilter.mode(Color(0xFF000000), BlendMode.dst);
static final List<Color> gradientColor = [];
/// Replace the text for a text layer.
static const text = 'dynamic_text';
}

View File

@ -1,5 +1,4 @@
import 'dart:ui';
import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/shape_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../content/shape_data.dart';
@ -10,7 +9,7 @@ class AnimatableShapeValue extends BaseAnimatableValue<ShapeData, Path> {
: super.fromKeyframes(keyframes);
@override
BaseKeyframeAnimation<ShapeData, Path> createAnimation() {
ShapeKeyframeAnimation createAnimation() {
return ShapeKeyframeAnimation(keyframes);
}
}

View File

@ -22,7 +22,9 @@ abstract class BaseAnimatableValue<V extends Object, O extends Object>
String toString() {
final sb = StringBuffer();
if (keyframes.isNotEmpty) {
sb..write('values=')..write('$keyframes');
sb
..write('values=')
..write('$keyframes');
}
return sb.toString();
}

View File

@ -0,0 +1,7 @@
import '../animatable/animatable_double_value.dart';
class BlurEffect {
final AnimatableDoubleValue blurriness;
BlurEffect(this.blurriness);
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/painting.dart';
import '../../value/keyframe.dart';
import '../animatable/animatable_color_value.dart';
import '../animatable/animatable_double_value.dart';
class DropShadowEffect {
final AnimatableColorValue color;
final AnimatableDoubleValue opacity;
final AnimatableDoubleValue direction;
final AnimatableDoubleValue distance;
final AnimatableDoubleValue radius;
DropShadowEffect({
required this.color,
required this.opacity,
required this.direction,
required this.distance,
required this.radius,
});
static DropShadowEffect createEmpty() => DropShadowEffect(
color: AnimatableColorValue.fromKeyframes(
[Keyframe.nonAnimated(const Color(0x00000000))]),
direction: AnimatableDoubleValue(),
radius: AnimatableDoubleValue(),
distance: AnimatableDoubleValue(),
opacity: AnimatableDoubleValue(),
);
}

View File

@ -37,6 +37,7 @@ class PolystarShape implements ContentModel {
final AnimatableDoubleValue? innerRoundedness;
final AnimatableDoubleValue outerRoundedness;
final bool hidden;
final bool isReversed;
PolystarShape({
this.name,
@ -49,6 +50,7 @@ class PolystarShape implements ContentModel {
this.innerRoundedness,
required this.outerRoundedness,
required this.hidden,
required this.isReversed,
});
@override

View File

@ -0,0 +1,18 @@
import '../../animation/content/content.dart';
import '../../animation/content/rounded_corners_content.dart';
import '../../lottie_drawable.dart';
import '../animatable/animatable_value.dart';
import '../layer/base_layer.dart';
import 'content_model.dart';
class RoundedCorners implements ContentModel {
final String name;
final AnimatableValue<double, double> cornerRadius;
RoundedCorners(this.name, this.cornerRadius);
@override
Content toContent(LottieDrawable drawable, BaseLayer layer) {
return RoundedCornersContent(drawable, layer, this);
}
}

View File

@ -22,6 +22,10 @@ class ShapeData {
return _initialPoint;
}
void setClosed(bool closed) {
_closed = closed;
}
bool get isClosed {
return _closed;
}

View File

@ -1,7 +1,44 @@
import 'dart:ui';
/// One cubic path operation. CubicCurveData is structured such that it is easy to iterate through
/// it and build a path. However, it is modeled differently than most path operations.
///
/// CubicCurveData
/// | - vertex
/// | /
/// | cp1 cp2
/// | /
/// | |
/// | /
/// --------------------------
///
/// When incrementally building a path, it will already have a "current point" so that is
/// not captured in this data structure.
/// The control points here represent {@link android.graphics.Path#cubicTo(float, float, float, float, float, float)}.
///
/// Most path operations are centered around a vertex and its in control point and out control point like this:
/// | outCp
/// | /
/// | |
/// | v
/// | /
/// | inCp
/// --------------------------
class CubicCurveData {
Offset controlPoint1 = Offset.zero;
Offset controlPoint2 = Offset.zero;
Offset vertex = Offset.zero;
void setFrom(CubicCurveData curveData) {
vertex = Offset(curveData.vertex.dx, curveData.vertex.dy);
controlPoint1 =
Offset(curveData.controlPoint1.dx, curveData.controlPoint1.dy);
controlPoint2 =
Offset(curveData.controlPoint2.dx, curveData.controlPoint2.dy);
}
@override
String toString() {
return 'v=$vertex cp1$controlPoint1 cp2=$controlPoint2';
}
}

View File

@ -2,7 +2,6 @@ import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart' hide Layer;
import 'package:vector_math/vector_math_64.dart';
import '../../animation/content/content.dart';
import '../../animation/content/drawing_content.dart';
import '../../animation/keyframe/base_keyframe_animation.dart';
@ -14,6 +13,8 @@ import '../../l.dart';
import '../../lottie_drawable.dart';
import '../../utils.dart';
import '../../value/lottie_value_callback.dart';
import '../content/blur_effect.dart';
import '../content/drop_shadow_effect.dart';
import '../content/mask.dart';
import '../content/shape_data.dart';
import '../key_path.dart';
@ -27,11 +28,14 @@ import 'solid_layer.dart';
import 'text_layer.dart';
abstract class BaseLayer implements DrawingContent, KeyPathElement {
static BaseLayer? forModel(Layer layerModel, LottieDrawable drawable,
static BaseLayer? forModel(
CompositionLayer compositionLayer,
Layer layerModel,
LottieDrawable drawable,
LottieComposition composition) {
switch (layerModel.layerType) {
case LayerType.shape:
return ShapeLayer(drawable, layerModel);
return ShapeLayer(drawable, layerModel, compositionLayer);
case LayerType.preComp:
return CompositionLayer(drawable, layerModel,
composition.getPrecomps(layerModel.refId)!, composition);
@ -77,6 +81,9 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
final TransformKeyframeAnimation transform;
bool _visible = true;
double blurMaskFilterRadius = 0;
MaskFilter? blurMaskFilter;
BaseLayer(this.lottieDrawable, this.layerModel)
: _drawTraceName = '${layerModel.name}#draw',
transform = layerModel.transform.createAnimation() {
@ -547,6 +554,22 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
return layerModel.name;
}
BlurEffect? get blurEffect {
return layerModel.blurEffect;
}
MaskFilter? getBlurMaskFilter(double radius) {
if (blurMaskFilterRadius == radius) {
return blurMaskFilter;
}
var sigma = radius * 0.57735 + 0.5;
blurMaskFilter = MaskFilter.blur(BlurStyle.normal, sigma);
blurMaskFilterRadius = radius;
return blurMaskFilter;
}
DropShadowEffect? get dropShadowEffect => layerModel.dropShadowEffect;
@override
void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
// Do nothing

View File

@ -36,7 +36,7 @@ class CompositionLayer extends BaseLayer {
BaseLayer? mattedLayer;
for (var i = layerModels.length - 1; i >= 0; i--) {
var lm = layerModels[i];
var layer = BaseLayer.forModel(lm, lottieDrawable, composition);
var layer = BaseLayer.forModel(this, lm, lottieDrawable, composition);
if (layer == null) {
continue;
}

View File

@ -25,6 +25,7 @@ class ImageLayer extends BaseLayer {
}
var density = window.devicePixelRatio;
paint.filterQuality = lottieDrawable.filterQuality ?? FilterQuality.none;
paint.setAlpha(parentAlpha);
if (_colorFilterAnimation != null) {
paint.colorFilter = _colorFilterAnimation!.value;

View File

@ -5,7 +5,9 @@ import '../animatable/animatable_double_value.dart';
import '../animatable/animatable_text_frame.dart';
import '../animatable/animatable_text_properties.dart';
import '../animatable/animatable_transform.dart';
import '../content/blur_effect.dart';
import '../content/content_model.dart';
import '../content/drop_shadow_effect.dart';
import '../content/mask.dart';
enum LayerType { preComp, solid, image, nullLayer, shape, text, unknown }
@ -35,6 +37,8 @@ class Layer {
final MatteType matteType;
final AnimatableDoubleValue? timeRemapping;
final bool isHidden;
final BlurEffect? blurEffect;
final DropShadowEffect? dropShadowEffect;
double get startProgress {
return startFrame / composition.durationFrames;
@ -63,6 +67,8 @@ class Layer {
required this.matteType,
this.timeRemapping,
required this.isHidden,
this.blurEffect,
this.dropShadowEffect,
});
@override
@ -71,19 +77,32 @@ class Layer {
}
String toStringWithPrefix(String prefix) {
var sb = StringBuffer()..write(prefix)..write(name)..write('\n');
var sb = StringBuffer()
..write(prefix)
..write(name)
..write('\n');
var parent = composition.layerModelForId(parentId);
if (parent != null) {
sb..write('\t\tParents: ')..write(parent.name);
sb
..write('\t\tParents: ')
..write(parent.name);
parent = composition.layerModelForId(parent.parentId);
while (parent != null) {
sb..write('->')..write(parent.name);
sb
..write('->')
..write(parent.name);
parent = composition.layerModelForId(parent.parentId);
}
sb..write(prefix)..write('\n');
sb
..write(prefix)
..write('\n');
}
if (masks.isNotEmpty) {
sb..write(prefix)..write('\tMasks: ')..write(masks.length)..write('\n');
sb
..write(prefix)
..write('\tMasks: ')
..write(masks.length)
..write('\n');
}
if (solidWidth != 0 && solidHeight != 0) {
sb
@ -92,9 +111,15 @@ class Layer {
..write('${solidWidth}x$solidHeight $solidColor');
}
if (shapes.isNotEmpty) {
sb..write(prefix)..write('\tShapes:\n');
sb
..write(prefix)
..write('\tShapes:\n');
for (Object shape in shapes) {
sb..write(prefix)..write('\t\t')..write(shape)..write('\n');
sb
..write(prefix)
..write('\t\t')
..write(shape)
..write('\n');
}
}
return sb.toString();

View File

@ -3,15 +3,20 @@ import 'package:vector_math/vector_math_64.dart';
import '../../animation/content/content.dart';
import '../../animation/content/content_group.dart';
import '../../lottie_drawable.dart';
import '../content/blur_effect.dart';
import '../content/drop_shadow_effect.dart';
import '../content/shape_group.dart';
import '../key_path.dart';
import 'base_layer.dart';
import 'composition_layer.dart';
import 'layer.dart';
class ShapeLayer extends BaseLayer {
late ContentGroup _contentGroup;
final CompositionLayer _compositionLayer;
ShapeLayer(LottieDrawable lottieDrawable, Layer layerModel)
ShapeLayer(
LottieDrawable lottieDrawable, Layer layerModel, this._compositionLayer)
: super(lottieDrawable, layerModel) {
// Naming this __container allows it to be ignored in KeyPath matching.
var shapeGroup =
@ -34,6 +39,24 @@ class ShapeLayer extends BaseLayer {
return bounds;
}
@override
BlurEffect? get blurEffect {
var layerBlur = super.blurEffect;
if (layerBlur != null) {
return layerBlur;
}
return _compositionLayer.blurEffect;
}
@override
DropShadowEffect? get dropShadowEffect {
var layerDropShadow = super.dropShadowEffect;
if (layerDropShadow != null) {
return layerDropShadow;
}
return _compositionLayer.dropShadowEffect;
}
@override
void resolveChildKeyPath(KeyPath keyPath, int depth,
List<KeyPath> accumulator, KeyPath currentPartialKeyPath) {

View File

@ -1,7 +1,5 @@
import 'dart:ui';
import 'package:characters/characters.dart';
import 'package:flutter/widgets.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../animation/content/content_group.dart';
import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/text_keyframe_animation.dart';
@ -486,6 +484,11 @@ class TextLayer extends BaseLayer {
..addUpdateListener(invalidateSelf);
addAnimation(_textSizeCallbackAnimation);
}
} else if (property == LottieProperty.text) {
if (callback != null) {
_textAnimation
.setStringValueCallback(callback as LottieValueCallback<String>);
}
}
}
}

View File

@ -0,0 +1,60 @@
import '../composition.dart';
import '../model/content/blur_effect.dart';
import 'animatable_value_parser.dart';
import 'moshi/json_reader.dart';
class BlurEffectParser {
static final JsonReaderOptions _blurEffectNames =
JsonReaderOptions.of(['ef']);
static final JsonReaderOptions _innerBlurEffectNames =
JsonReaderOptions.of(['ty', 'v']);
static BlurEffect? parse(JsonReader reader, LottieComposition composition) {
BlurEffect? blurEffect;
while (reader.hasNext()) {
switch (reader.selectName(_blurEffectNames)) {
case 0:
reader.beginArray();
while (reader.hasNext()) {
var be = _maybeParseInnerEffect(reader, composition);
if (be != null) {
blurEffect = be;
}
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
}
}
return blurEffect;
}
static BlurEffect? _maybeParseInnerEffect(
JsonReader reader, LottieComposition composition) {
BlurEffect? blurEffect;
var isCorrectType = false;
reader.beginObject();
while (reader.hasNext()) {
switch (reader.selectName(_innerBlurEffectNames)) {
case 0:
isCorrectType = reader.nextInt() == 0;
break;
case 1:
if (isCorrectType) {
blurEffect = BlurEffect(
AnimatableValueParser.parseFloat(reader, composition));
} else {
reader.skipValue();
}
break;
default:
reader.skipName();
reader.skipValue();
}
}
reader.endObject();
return blurEffect;
}
}

View File

@ -9,6 +9,7 @@ import 'moshi/json_reader.dart';
import 'polysar_shape_parser.dart';
import 'rectangle_shape_parser.dart';
import 'repeat_parser.dart';
import 'rounded_corners_parser.dart';
import 'shape_fill_parser.dart';
import 'shape_group_parser.dart';
import 'shape_path_parser.dart';
@ -80,7 +81,7 @@ class ContentModelParser {
model = ShapeTrimPathParser.parse(reader, composition);
break;
case 'sr':
model = PolystarShapeParser.parse(reader, composition);
model = PolystarShapeParser.parse(reader, composition, d: d);
break;
case 'mm':
model = MergePathsParser.parse(reader);
@ -88,6 +89,9 @@ class ContentModelParser {
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

@ -3,8 +3,19 @@ import '../model/document_data.dart';
import 'json_utils.dart';
import 'moshi/json_reader.dart';
final JsonReaderOptions _names = JsonReaderOptions.of(
['t', 'f', 's', 'j', 'tr', 'lh', 'ls', 'fc', 'sc', 'sw', 'of']);
final JsonReaderOptions _names = JsonReaderOptions.of([
't', // 0
'f', // 1
's', // 2
'j', // 3
'tr', // 4
'lh', // 5
'ls', // 6
'fc', // 7
'sc', // 8
'sw', // 9
'of', // 10
]);
DocumentData documentDataParser(JsonReader reader, {required double scale}) {
String? text;

View File

@ -0,0 +1,97 @@
import '../composition.dart';
import '../model/animatable/animatable_color_value.dart';
import '../model/animatable/animatable_double_value.dart';
import '../model/content/drop_shadow_effect.dart';
import 'animatable_value_parser.dart';
import 'moshi/json_reader.dart';
class DropShadowEffectParser {
static final JsonReaderOptions _dropShadowEffectNames =
JsonReaderOptions.of(['ef']);
static final JsonReaderOptions _innerEffectNames =
JsonReaderOptions.of(['nm', 'v']);
AnimatableColorValue? _color;
AnimatableDoubleValue? _opacity;
AnimatableDoubleValue? _direction;
AnimatableDoubleValue? _distance;
AnimatableDoubleValue? _radius;
DropShadowEffect? parse(JsonReader reader, LottieComposition composition) {
while (reader.hasNext()) {
switch (reader.selectName(_dropShadowEffectNames)) {
case 0:
reader.beginArray();
while (reader.hasNext()) {
_maybeParseInnerEffect(reader, composition);
}
reader.endArray();
break;
default:
reader.skipName();
reader.skipValue();
}
}
var color = _color;
var opacity = _opacity;
var direction = _direction;
var distance = _distance;
var radius = _radius;
if (color != null &&
opacity != null &&
direction != null &&
distance != null &&
radius != null) {
return DropShadowEffect(
color: color,
opacity: opacity,
direction: direction,
distance: distance,
radius: radius);
}
return null;
}
void _maybeParseInnerEffect(
JsonReader reader, LottieComposition composition) {
var currentEffectName = '';
reader.beginObject();
while (reader.hasNext()) {
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,
isDp: false);
break;
case 'Direction':
_direction = AnimatableValueParser.parseFloat(reader, composition,
isDp: false);
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();
}
}
reader.endObject();
}
}

View File

@ -37,6 +37,17 @@ class GradientColorParser {
while (reader.hasNext()) {
array.add(reader.nextDouble());
}
if (array.length == 4 && array[0] == 1) {
// If a gradient color only contains one color at position 1, add a second stop with the same
// color at position 0. Android's LinearGradient shader requires at least two colors.
// https://github.com/airbnb/lottie-android/issues/1967
array[0] = 0;
array.add(1);
array.add(array[1]);
array.add(array[2]);
array.add(array[3]);
_colorPoints = 2;
}
if (isArray) {
reader.endArray();
}

View File

@ -13,7 +13,7 @@ class KeyframeParser {
/// PathInterpolator fails to create the interpolator in those cases and hangs.
/// Clamping the cp helps prevent that.
static const _maxCpValue = 100.0;
static final _linearInterpolator = Curves.linear;
static const _linearInterpolator = Curves.linear;
static final _pathInterpolatorCache = <int, Curve>{};
static final JsonReaderOptions _names =
@ -302,7 +302,7 @@ class KeyframeParser {
interpolator = _pathInterpolatorCache.putIfAbsent(hash, () {
try {
return Cubic(cp1.dx, cp1.dy, cp2.dx, cp2.dy);
return PathInterpolator.cubic(cp1.dx, cp1.dy, cp2.dx, cp2.dy);
} catch (e) {
debugPrint('DEBUG: Path interpolator error $e');
//TODO(xha): check the error message for Flutter

View File

@ -4,7 +4,9 @@ import '../model/animatable/animatable_double_value.dart';
import '../model/animatable/animatable_text_frame.dart';
import '../model/animatable/animatable_text_properties.dart';
import '../model/animatable/animatable_transform.dart';
import '../model/content/blur_effect.dart';
import '../model/content/content_model.dart';
import '../model/content/drop_shadow_effect.dart';
import '../model/content/mask.dart';
import '../model/layer/layer.dart';
import '../utils/misc.dart';
@ -12,7 +14,9 @@ import '../value/keyframe.dart';
import 'animatable_text_properties_parser.dart';
import 'animatable_transform_parser.dart';
import 'animatable_value_parser.dart';
import 'blur_effect_parser.dart';
import 'content_model_parser.dart';
import 'drop_shadow_effect_parser.dart';
import 'mask_parser.dart';
import 'moshi/json_reader.dart';
@ -74,7 +78,8 @@ class LayerParser {
static final JsonReaderOptions _textNames = JsonReaderOptions.of(['d', 'a']);
static final JsonReaderOptions _effectsNames = JsonReaderOptions.of(['nm']);
static final JsonReaderOptions _effectsNames =
JsonReaderOptions.of(['ty', 'nm']);
static Layer parseJson(JsonReader reader, LottieComposition composition) {
// This should always be set by After Effects. However, if somebody wants to minify
@ -95,6 +100,8 @@ class LayerParser {
var outFrame = 0.0;
String? cl;
var hidden = false;
BlurEffect? blurEffect;
DropShadowEffect? dropShadowEffect;
var matteType = MatteType.none;
AnimatableTransform? transform;
@ -207,7 +214,17 @@ class LayerParser {
while (reader.hasNext()) {
switch (reader.selectName(_effectsNames)) {
case 0:
effectNames.add(reader.nextString());
var type = reader.nextInt();
if (type == 29) {
blurEffect = BlurEffectParser.parse(reader, composition);
} else if (type == 25) {
dropShadowEffect =
DropShadowEffectParser().parse(reader, composition);
}
break;
case 1:
var effectName = reader.nextString();
effectNames.add(effectName);
break;
default:
reader.skipName();
@ -292,27 +309,30 @@ class LayerParser {
}
return Layer(
shapes: shapes,
composition: composition,
name: layerName,
id: layerId,
layerType: layerType,
parentId: parentId,
refId: refId,
masks: masks,
transform: transform!,
solidWidth: solidWidth,
solidHeight: solidHeight,
solidColor: solidColor,
timeStretch: timeStretch,
startFrame: startFrame,
preCompWidth: preCompWidth,
preCompHeight: preCompHeight,
text: text,
textProperties: textProperties,
inOutKeyframes: inOutKeyframes,
matteType: matteType,
timeRemapping: timeRemapping,
isHidden: hidden);
shapes: shapes,
composition: composition,
name: layerName,
id: layerId,
layerType: layerType,
parentId: parentId,
refId: refId,
masks: masks,
transform: transform!,
solidWidth: solidWidth,
solidHeight: solidHeight,
solidColor: solidColor,
timeStretch: timeStretch,
startFrame: startFrame,
preCompWidth: preCompWidth,
preCompHeight: preCompHeight,
text: text,
textProperties: textProperties,
inOutKeyframes: inOutKeyframes,
matteType: matteType,
timeRemapping: timeRemapping,
isHidden: hidden,
blurEffect: blurEffect,
dropShadowEffect: dropShadowEffect,
);
}
}

View File

@ -39,7 +39,10 @@ class JsonScope {
switch (stack[i]) {
case emptyArray:
case nonEmptyArray:
result..write('[')..write(pathIndices[i])..write(']');
result
..write('[')
..write(pathIndices[i])
..write(']');
break;
case emptyObject:

View File

@ -9,11 +9,12 @@ import 'moshi/json_reader.dart';
class PolystarShapeParser {
static final JsonReaderOptions _names = JsonReaderOptions.of(
['nm', 'sy', 'pt', 'p', 'r', 'or', 'os', 'ir', 'is', 'hd']);
['nm', 'sy', 'pt', 'p', 'r', 'or', 'os', 'ir', 'is', 'hd', 'd']);
PolystarShapeParser._();
static PolystarShape parse(JsonReader reader, LottieComposition composition) {
static PolystarShape parse(JsonReader reader, LottieComposition composition,
{required int d}) {
String? name;
PolystarShapeType? type;
late AnimatableDoubleValue points;
@ -24,6 +25,7 @@ class PolystarShapeParser {
AnimatableDoubleValue? innerRadius;
AnimatableDoubleValue? innerRoundedness;
var hidden = false;
var reversed = d == 3;
while (reader.hasNext()) {
switch (reader.selectName(_names)) {
@ -64,6 +66,10 @@ class PolystarShapeParser {
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();
@ -81,6 +87,7 @@ class PolystarShapeParser {
innerRoundedness: innerRoundedness,
outerRoundedness: outerRoundedness,
hidden: hidden,
isReversed: reversed,
);
}
}

View File

@ -0,0 +1,39 @@
import '../composition.dart';
import '../model/animatable/animatable_value.dart';
import '../model/content/rounded_corners.dart';
import 'animatable_value_parser.dart';
import 'moshi/json_reader.dart';
class RoundedCornersParser {
static final _names = JsonReaderOptions.of([
'nm', // 0
'r', // 1
'hd' // 1
]);
static RoundedCorners? parse(
JsonReader reader, LottieComposition composition) {
String? name;
AnimatableValue<double, double>? cornerRadius;
var hidden = false;
while (reader.hasNext()) {
switch (reader.selectName(_names)) {
case 0: //nm
name = reader.nextString();
break;
case 1: // r
cornerRadius =
AnimatableValueParser.parseFloat(reader, composition, isDp: true);
break;
case 2: // hd
hidden = reader.nextBoolean();
break;
default:
reader.skipValue();
}
}
return hidden ? null : RoundedCorners(name!, cornerRadius!);
}
}

View File

@ -1,8 +1,10 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'composition.dart';
import 'frame_rate.dart';
import 'lottie_delegates.dart';
import 'lottie_drawable.dart';
import 'options.dart';
import 'render_lottie.dart';
/// A widget that displays a [LottieDrawable] directly.
@ -21,6 +23,7 @@ class RawLottie extends LeafRenderObjectWidget {
this.height,
this.fit,
AlignmentGeometry? alignment,
this.filterQuality,
}) : progress = progress ?? 0.0,
alignment = alignment ?? Alignment.center,
super(key: key);
@ -76,6 +79,8 @@ class RawLottie extends LeafRenderObjectWidget {
/// relative to text direction.
final AlignmentGeometry alignment;
final FilterQuality? filterQuality;
@override
RenderLottie createRenderObject(BuildContext context) {
return RenderLottie(
@ -88,17 +93,21 @@ class RawLottie extends LeafRenderObjectWidget {
height: height,
fit: fit,
alignment: alignment,
filterQuality: filterQuality,
);
}
@override
void updateRenderObject(BuildContext context, RenderLottie renderObject) {
renderObject
..setComposition(composition,
progress: progress,
frameRate: frameRate,
delegates: delegates,
enableMergePaths: options?.enableMergePaths)
..setComposition(
composition,
progress: progress,
frameRate: frameRate,
delegates: delegates,
enableMergePaths: options?.enableMergePaths,
filterQuality: filterQuality,
)
..width = width
..height = height
..alignment = alignment

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