Compare commits

...

11 Commits

Author SHA1 Message Date
8dcb052fe1 Rework the cache so animation can be loaded without flickering 1 frame (#246)
Now, the cache can return a `SynchronousFuture` when the composition is
already available.
2022-12-14 14:06:34 +01:00
a333a42f01 Enable use_super_parameters lint (#242) 2022-11-10 14:14:28 +01:00
8ce429cdf4 Breaking change: Remove window.devicePixelRatio from the parser (#240) 2022-11-09 14:38:50 +01:00
fc450a88f4 Fix some warnings with Flutter 3.3 (#235) 2022-09-14 13:21:29 +02:00
c0bc257f4f use FilterQuality.low to default when draw image layer to fix some image render sawtooth (#230) 2022-08-25 11:55:26 +02:00
0e7499d82e Allow AlignmentGeometry for alignment (#228)
Thanks for the contribution
2022-08-03 11:52:58 +02:00
4cd9ec759a Update publish-on-pub action 2022-07-27 11:15:31 +02:00
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
508 changed files with 10619 additions and 647 deletions

View File

@ -10,11 +10,11 @@ jobs:
name: Flutter analyze name: Flutter analyze
strategy: strategy:
matrix: matrix:
flutter: ['beta'] flutter: ['stable']
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v2
with: with:
channel: ${{ matrix.flutter }} channel: ${{ matrix.flutter }}
- run: flutter doctor - run: flutter doctor
@ -22,8 +22,8 @@ jobs:
- run: flutter pub get - run: flutter pub get
working-directory: example working-directory: example
- run: flutter analyze - run: flutter analyze
- run: flutter test test # https://github.com/flutter/flutter/issues/20907 - run: flutter test
- run: flutter test test - run: flutter test
working-directory: example working-directory: example
- run: flutter pub run tool/prepare_submit.dart - run: flutter pub run tool/prepare_submit.dart
- name: "check for uncommitted changes" - name: "check for uncommitted changes"
@ -34,14 +34,13 @@ jobs:
&& exit 1) && exit 1)
shell: bash shell: bash
build_web_version: build_web_version:
name: Check that the web version compile name: Check that the web version can compile
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v2
with: with:
channel: 'beta' channel: 'stable'
- run: flutter config --enable-web
- run: flutter precache web - run: flutter precache web
- run: flutter pub get - run: flutter pub get
working-directory: example working-directory: example

View File

@ -8,9 +8,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: subosito/flutter-action@v1 - uses: subosito/flutter-action@v2
with: with:
channel: 'beta' channel: 'stable'
- run: flutter pub get - run: flutter pub get
- run: flutter pub run tool/publish/comment_dependency_overrides.dart - run: flutter pub run tool/publish/comment_dependency_overrides.dart
- run: flutter pub get - run: flutter pub get

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ _*
!.gitignore !.gitignore
!.github !.github
!.pubignore
**/failures/*.png **/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,77 @@
## [2.1.0]
- Improve the cache to ensure that there is not an empty frame each time we load an animation.
The method `AssetLottie('anim.json').load()` returns a `SynchronousFuture` if it has been loaded previously.
- Expose the `LottieCache` singleton.
It allows to change the cache behaviour and clear the entries.
```dart
void main() {
Lottie.cache.maximumSize = 10;
Lottie.cache.clear();
Lottie.cache.evict(NetworkLottie('https://lottie.com/anim.json'));
}
```
## [2.0.0]
- **Breaking change**: the lottie widget will be smaller if it relies on the intrinsic size of the composition.
Previously the lottie parser was automatically multiplying the size of the composition by `window.devicePixelRatio`.
This was incorrect as it results in a widget of a different size depending on the pixel ratio of the monitor.
Furthermore, it created some bugs when the property `window.devicePixelRatio` was not available immediately at the start
of the app (on Android release builds).
The code can be adapted to specify explicitly the size of the animation with `width`, `height` and `fit` properties.
```dart
Scaffold(
body: Center(
child: Lottie.asset(
'assets/LottieLogo1.json',
height: 800,
fit: BoxFit.contain,
),
),
);
```
## [1.4.3]
- Fixed some lints with Flutter 3.3.
## [1.4.2]
- Use `FilterQuality.low` as default to draw image layers.
## [1.4.1]
- Allow `AlignmentGeometry` for `alignment`.
## [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] ## [1.2.1]
- Fix: Revert Cubic to `PathInterpolator.cubic` - Fix: Revert Cubic to `PathInterpolator.cubic`

View File

@ -23,7 +23,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -61,10 +61,10 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
_MyAppState createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> with TickerProviderStateMixin { class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
@ -136,10 +136,10 @@ This example shows how to load and parse a Lottie composition from a json file.
```dart ```dart
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
_MyWidgetState createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();
} }
class _MyWidgetState extends State<MyWidget> { class _MyWidgetState extends State<MyWidget> {
@ -182,7 +182,7 @@ a specific position and size.
class CustomDrawer extends StatelessWidget { class CustomDrawer extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const CustomDrawer(this.composition, {Key? key}) : super(key: key); const CustomDrawer(this.composition, {super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -10,14 +10,16 @@ linter:
always_declare_return_types: true always_declare_return_types: true
avoid_dynamic_calls: true avoid_dynamic_calls: true
avoid_escaping_inner_quotes: true avoid_escaping_inner_quotes: true
avoid_returning_null_for_future: true
avoid_setters_without_getters: true avoid_setters_without_getters: true
cancel_subscriptions: true
cast_nullable_to_non_nullable: true cast_nullable_to_non_nullable: true
close_sinks: true close_sinks: true
no_adjacent_strings_in_list: true no_adjacent_strings_in_list: true
no_default_cases: true no_default_cases: true
omit_local_variable_types: true omit_local_variable_types: true
only_throw_errors: true only_throw_errors: true
prefer_interpolation_to_compose_strings: true prefer_relative_imports: true
prefer_single_quotes: true prefer_single_quotes: true
sort_child_properties_last: true sort_child_properties_last: true
sort_pub_dependencies: true sort_pub_dependencies: true
@ -27,3 +29,4 @@ linter:
unnecessary_statements: true unnecessary_statements: true
unsafe_html: true unsafe_html: true
use_raw_strings: true use_raw_strings: true
use_super_parameters: true

View File

@ -9,7 +9,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -5,3 +5,9 @@ gradle-wrapper.jar
/gradlew.bat /gradlew.bat
/local.properties /local.properties
GeneratedPluginRegistrant.java 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') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { 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') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@ -26,23 +26,28 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
lintOptions {
disable 'InvalidPackage'
}
defaultConfig { defaultConfig {
applicationId "com.github.xvrh.lottie.sample" // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
minSdkVersion 16 applicationId "com.github.xvrh.lottie.example"
targetSdkVersion 28 minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -60,7 +65,4 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 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" <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 <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->

View File

@ -1,21 +1,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.xvrh.lottie.sample"> package="com.github.xvrh.lottie.example">
<!-- 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 <application
android:name="io.flutter.app.FlutterApplication"
android:label="example" android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> 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> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <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"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <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 <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </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> </resources>

View File

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

View File

@ -1,12 +1,12 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.6.10'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { 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" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
@ -14,7 +14,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

View File

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

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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' include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
def plugins = new Properties() assert localPropertiesFile.exists()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path -> def flutterSdkPath = properties.getProperty("flutter.sdk")
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
include ":$name" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
project(":$name").projectDir = pluginDirectory
}

File diff suppressed because it is too large Load Diff

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": []
}

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

@ -5,7 +5,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const App()); void main() => runApp(const App());
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -25,7 +25,7 @@ class __PageState extends State<_Page> {
void initState() { void initState() {
super.initState(); super.initState();
SchedulerBinding.instance!.addPostFrameCallback((_) => _showLoader()); SchedulerBinding.instance.addPostFrameCallback((_) => _showLoader());
} }
void _showLoader() { void _showLoader() {

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,10 +4,10 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
_MyAppState createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> with TickerProviderStateMixin { class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

@ -12,10 +12,10 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
_MyAppState createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> with TickerProviderStateMixin { class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body: _Page()),
);
}
}
class _Page extends StatefulWidget {
@override
__PageState createState() => __PageState();
}
class __PageState extends State<_Page> {
var _animationKey = UniqueKey();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Lottie.network(
'https://assets10.lottiefiles.com/datafiles/QeC7XD39x4C1CIj/data.json',
key: _animationKey,
fit: BoxFit.contain,
width: 200,
height: 200,
),
ElevatedButton(
onPressed: () {
Lottie.cache.clear();
Lottie.cache.maximumSize = 10;
},
child: const Text('Clear cache'),
),
ElevatedButton(
onPressed: () {
setState(() {
_animationKey = UniqueKey();
});
},
child: const Text('Recreate animation'),
),
],
);
}
}

View File

@ -1,12 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,10 +18,10 @@ class MyApp extends StatelessWidget {
} }
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
_MyWidgetState createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();
} }
class _MyWidgetState extends State<MyWidget> { class _MyWidgetState extends State<MyWidget> {
@ -61,7 +60,7 @@ class _MyWidgetState extends State<MyWidget> {
class CustomDrawer extends StatelessWidget { class CustomDrawer extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const CustomDrawer(this.composition, {Key? key}) : super(key: key); const CustomDrawer(this.composition, {super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,12 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -20,10 +19,10 @@ class MyApp extends StatelessWidget {
//--- example //--- example
class MyWidget extends StatefulWidget { class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key); const MyWidget({super.key});
@override @override
_MyWidgetState createState() => _MyWidgetState(); State<MyWidget> createState() => _MyWidgetState();
} }
class _MyWidgetState extends State<MyWidget> { class _MyWidgetState extends State<MyWidget> {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

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

View File

@ -9,10 +9,10 @@ void main() async {
} }
class App extends StatefulWidget { class App extends StatefulWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
_AppState createState() => _AppState(); State<App> createState() => _AppState();
} }
class _AppState extends State<App> with TickerProviderStateMixin { class _AppState extends State<App> with TickerProviderStateMixin {
@ -48,7 +48,7 @@ class _AppState extends State<App> with TickerProviderStateMixin {
class _LottieDetails extends StatefulWidget { class _LottieDetails extends StatefulWidget {
final LottieComposition composition; final LottieComposition composition;
const _LottieDetails(this.composition, {Key? key}) : super(key: key); const _LottieDetails(this.composition);
@override @override
_LottieDetailsState createState() => _LottieDetailsState(); _LottieDetailsState createState() => _LottieDetailsState();

View File

@ -4,10 +4,10 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
_MyAppState createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> with TickerProviderStateMixin { class _MyAppState extends State<MyApp> with TickerProviderStateMixin {

View File

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

View File

@ -10,10 +10,10 @@ import 'package:path_provider/path_provider.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
_MyAppState createState() => _MyAppState(); State<MyApp> createState() => _MyAppState();
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -4,7 +4,7 @@ import 'package:lottie/lottie.dart';
void main() => runApp(const MyApp()); void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
@ -15,7 +14,7 @@ void main() {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -25,8 +24,8 @@ class App extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: const Text('Lottie Flutter'), title: const Text('Lottie Flutter'),
), ),
body: Scrollbar( body: GridView.builder(
child: GridView.builder( primary: true,
itemCount: files.length, itemCount: files.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4), crossAxisCount: 4),
@ -42,6 +41,7 @@ class App extends StatelessWidget {
child: _Item( child: _Item(
child: Lottie.asset( child: Lottie.asset(
assetName, assetName,
fit: BoxFit.contain,
onWarning: (w) => _logger.info('$assetName - $w'), onWarning: (w) => _logger.info('$assetName - $w'),
frameBuilder: (context, child, composition) { frameBuilder: (context, child, composition) {
return AnimatedOpacity( return AnimatedOpacity(
@ -57,7 +57,6 @@ class App extends StatelessWidget {
}, },
), ),
), ),
),
); );
} }
} }
@ -65,7 +64,7 @@ class App extends StatelessWidget {
class _Item extends StatelessWidget { class _Item extends StatelessWidget {
final Widget child; final Widget child;
const _Item({Key? key, required this.child}) : super(key: key); const _Item({required this.child});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -90,10 +89,10 @@ class _Item extends StatelessWidget {
class Detail extends StatefulWidget { class Detail extends StatefulWidget {
final String assetName; final String assetName;
const Detail(this.assetName, {Key? key}) : super(key: key); const Detail(this.assetName, {super.key});
@override @override
_DetailState createState() => _DetailState(); State<Detail> createState() => _DetailState();
} }
class _DetailState extends State<Detail> with TickerProviderStateMixin { class _DetailState extends State<Detail> with TickerProviderStateMixin {

View File

@ -13,7 +13,7 @@ void main() async {
class App extends StatelessWidget { class App extends StatelessWidget {
final LottieComposition composition; final LottieComposition composition;
const App({Key? key, required this.composition}) : super(key: key); const App({super.key, required this.composition});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -103,8 +103,7 @@ class _Lottie extends StatefulWidget {
final AlignmentGeometry? alignment; final AlignmentGeometry? alignment;
const _Lottie(this.composition, const _Lottie(this.composition,
{Key? key, this.width, this.height, this.fit, this.alignment}) {this.width, this.height, this.fit, this.alignment});
: super(key: key);
@override @override
__LottieState createState() => __LottieState(); __LottieState createState() => __LottieState();

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -6,7 +6,7 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key); const App({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -0,0 +1,11 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}

View File

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@ -14,9 +14,9 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
SPEC CHECKSUMS: SPEC CHECKSUMS:
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
COCOAPODS: 1.10.1 COCOAPODS: 1.11.3

View File

@ -7,14 +7,14 @@ packages:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.2" version: "3.3.5"
async: async:
dependency: transitive dependency: transitive
description: description:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.5.0" version: "2.9.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -28,56 +28,56 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.2.1"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.15.0" version: "1.16.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.1"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.2" version: "2.0.1"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.2" version: "6.1.4"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -89,14 +89,14 @@ packages:
name: flutter_colorpicker name: flutter_colorpicker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.0" version: "1.0.3"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "2.0.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -108,126 +108,154 @@ packages:
name: golden_toolkit name: golden_toolkit
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.0" version: "0.14.0"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.3" version: "0.13.5"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.0.2"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.5"
lints: lints:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "2.0.1"
logging: logging:
dependency: "direct main" dependency: "direct main"
description: description:
name: logging name: logging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.1.0"
lottie: lottie:
dependency: "direct main" dependency: "direct main"
description: description:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "1.2.0" version: "2.1.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.10" version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.8.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.2"
path_provider: path_provider:
dependency: "direct main" dependency: "direct main"
description: description:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted 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.22"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.1.7"
path_provider_macos: path_provider_macos:
dependency: transitive dependency: transitive
description: description:
name: path_provider_macos name: path_provider_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.6"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: path_provider_platform_interface name: path_provider_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.5"
path_provider_windows: path_provider_windows:
dependency: transitive dependency: transitive
description: description:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.1.3"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
platform: platform:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.1.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.1.3"
pointycastle:
dependency: transitive
description:
name: pointycastle
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2"
process: process:
dependency: transitive dependency: transitive
description: description:
name: process name: process
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.1" version: "4.2.4"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -239,7 +267,7 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.1" version: "1.9.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -260,49 +288,49 @@ packages:
name: string_scanner name: string_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.4.12"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.5" version: "3.1.2"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0+2"
sdks: sdks:
dart: ">=2.13.0 <3.0.0" dart: ">=2.18.4 <3.0.0"
flutter: ">=1.20.0" flutter: ">=3.3.0"

View File

@ -3,7 +3,7 @@ description: A sample app for the Lottie player
publish_to: none publish_to: none
environment: environment:
sdk: ">=2.12.0-0 <3.0.0" sdk: ">=2.18.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
@ -13,6 +13,7 @@ dependencies:
logging: logging:
lottie: lottie:
path: ../ path: ../
path:
path_provider: path_provider:
dev_dependencies: 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

@ -10,7 +10,7 @@ export 'src/options.dart' show LottieOptions;
export 'src/providers/asset_provider.dart' show AssetLottie; export 'src/providers/asset_provider.dart' show AssetLottie;
export 'src/providers/file_provider.dart' show FileLottie; export 'src/providers/file_provider.dart' show FileLottie;
export 'src/providers/load_image.dart' show LottieImageProviderFactory; export 'src/providers/load_image.dart' show LottieImageProviderFactory;
export 'src/providers/lottie_provider.dart' show LottieProvider; export 'src/providers/lottie_provider.dart' show LottieProvider, LottieCache;
export 'src/providers/memory_provider.dart' show MemoryLottie; export 'src/providers/memory_provider.dart' show MemoryLottie;
export 'src/providers/network_provider.dart' show NetworkLottie; export 'src/providers/network_provider.dart' show NetworkLottie;
export 'src/raw_lottie.dart' show RawLottie; export 'src/raw_lottie.dart' show RawLottie;

View File

@ -196,7 +196,8 @@ abstract class BaseStrokeContent
void _applyTrimPath( void _applyTrimPath(
Canvas canvas, _PathGroup pathGroup, Matrix4 parentMatrix) { Canvas canvas, _PathGroup pathGroup, Matrix4 parentMatrix) {
L.beginSection('StrokeContent#applyTrimPath'); L.beginSection('StrokeContent#applyTrimPath');
if (pathGroup.trimPath == null) { var trimPath = pathGroup.trimPath;
if (trimPath == null) {
L.endSection('StrokeContent#applyTrimPath'); L.endSection('StrokeContent#applyTrimPath');
return; return;
} }
@ -205,13 +206,24 @@ abstract class BaseStrokeContent
_path.addPath(pathGroup.paths[j].getPath(), Offset.zero, _path.addPath(pathGroup.paths[j].getPath(), Offset.zero,
matrix4: parentMatrix.storage); 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 pathMetrics = _path.computeMetrics().toList();
var totalLength = pathMetrics.fold<double>(0.0, (a, b) => a + b.length); var totalLength = pathMetrics.fold<double>(0.0, (a, b) => a + b.length);
var trimPath = pathGroup.trimPath!; var offsetLength = totalLength * animOffsetValue;
var offsetLength = totalLength * trimPath.offset.value / 360.0; var startLength = totalLength * animStartValue + offsetLength;
var startLength = totalLength * trimPath.start.value / 100.0 + offsetLength; var endLength = min(totalLength * animEndValue + offsetLength,
var endLength = totalLength * trimPath.end.value / 100.0 + offsetLength; startLength + totalLength - 1);
var currentLength = 0.0; var currentLength = 0.0;
for (var j = pathGroup.paths.length - 1; j >= 0; j--) { for (var j = pathGroup.paths.length - 1; j >= 0; j--) {

View File

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

View File

@ -15,6 +15,7 @@ import 'compound_trim_path_content.dart';
import 'content.dart'; import 'content.dart';
import 'key_path_element_content.dart'; import 'key_path_element_content.dart';
import 'path_content.dart'; import 'path_content.dart';
import 'rounded_corners_content.dart';
import 'trim_path_content.dart'; import 'trim_path_content.dart';
class RectangleContent implements KeyPathElementContent, PathContent { class RectangleContent implements KeyPathElementContent, PathContent {
@ -29,6 +30,9 @@ class RectangleContent implements KeyPathElementContent, PathContent {
final BaseKeyframeAnimation<Object, double> _cornerRadiusAnimation; final BaseKeyframeAnimation<Object, double> _cornerRadiusAnimation;
final CompoundTrimPathContent _trimPaths = CompoundTrimPathContent(); 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; bool _isPathValid = false;
RectangleContent( RectangleContent(
@ -61,6 +65,8 @@ class RectangleContent implements KeyPathElementContent, PathContent {
var trimPath = content; var trimPath = content;
_trimPaths.addTrimPath(trimPath); _trimPaths.addTrimPath(trimPath);
trimPath.addListener(invalidate); 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 halfWidth = size.dx / 2.0;
var halfHeight = size.dy / 2.0; var halfHeight = size.dy / 2.0;
var radius = _cornerRadiusAnimation.value; var radius = _cornerRadiusAnimation.value;
var roundedCornersAnimation = _roundedCornersAnimation;
if (radius == 0 && roundedCornersAnimation != null) {
radius = min(roundedCornersAnimation.value, min(halfWidth, halfHeight));
}
var maxRadius = min(halfWidth, halfHeight); var maxRadius = min(halfWidth, halfHeight);
if (radius > maxRadius) { if (radius > maxRadius) {
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 '../../model/layer/base_layer.dart';
import '../../utils.dart'; import '../../utils.dart';
import '../../utils/path_factory.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 'compound_trim_path_content.dart';
import 'content.dart'; import 'content.dart';
import 'path_content.dart'; import 'path_content.dart';
import 'shape_modifier_content.dart';
import 'trim_path_content.dart'; import 'trim_path_content.dart';
class ShapeContent implements PathContent { class ShapeContent implements PathContent {
@ -17,7 +18,7 @@ class ShapeContent implements PathContent {
final ShapePath _shape; final ShapePath _shape;
final LottieDrawable lottieDrawable; final LottieDrawable lottieDrawable;
final BaseKeyframeAnimation<Object, Path> _shapeAnimation; final ShapeKeyframeAnimation _shapeAnimation;
bool _isPathValid = false; bool _isPathValid = false;
final _trimPaths = CompoundTrimPathContent(); final _trimPaths = CompoundTrimPathContent();
@ -35,6 +36,7 @@ class ShapeContent implements PathContent {
@override @override
void setContents(List<Content> contentsBefore, List<Content> contentsAfter) { void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
List<ShapeModifierContent>? shapeModifierContents;
for (var i = 0; i < contentsBefore.length; i++) { for (var i = 0; i < contentsBefore.length; i++) {
var content = contentsBefore[i]; var content = contentsBefore[i];
if (content is TrimPathContent && if (content is TrimPathContent &&
@ -43,8 +45,12 @@ class ShapeContent implements PathContent {
var trimPath = content; var trimPath = content;
_trimPaths.addTrimPath(trimPath); _trimPaths.addTrimPath(trimPath);
trimPath.addListener(_invalidate); trimPath.addListener(_invalidate);
} else if (content is ShapeModifierContent) {
shapeModifierContents ??= [];
shapeModifierContents.add(content);
} }
} }
_shapeAnimation.setShapeModifiers(shapeModifierContents);
} }
@override @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

@ -4,7 +4,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class ColorKeyframeAnimation extends KeyframeAnimation<Color> { class ColorKeyframeAnimation extends KeyframeAnimation<Color> {
ColorKeyframeAnimation(List<Keyframe<Color>> keyframes) : super(keyframes); ColorKeyframeAnimation(super.keyframes);
@override @override
Color getValue(Keyframe<Color> keyframe, double keyframeProgress) { Color getValue(Keyframe<Color> keyframe, double keyframeProgress) {

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class DoubleKeyframeAnimation extends KeyframeAnimation<double> { class DoubleKeyframeAnimation extends KeyframeAnimation<double> {
DoubleKeyframeAnimation(List<Keyframe<double>> keyframes) : super(keyframes); DoubleKeyframeAnimation(super.keyframes);
@override @override
double getValue(Keyframe<double> keyframe, double keyframeProgress) { double getValue(Keyframe<double> keyframe, double keyframeProgress) {

View File

@ -10,7 +10,7 @@ import 'base_keyframe_animation.dart';
import 'color_keyframe_animation.dart'; import 'color_keyframe_animation.dart';
class DropShadowKeyframeAnimation { class DropShadowKeyframeAnimation {
static final double _degToRad = math.pi / 180.0; static const double _degToRad = math.pi / 180.0;
final void Function() listener; final void Function() listener;
late final ColorKeyframeAnimation _color; late final ColorKeyframeAnimation _color;

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class IntegerKeyframeAnimation extends KeyframeAnimation<int> { class IntegerKeyframeAnimation extends KeyframeAnimation<int> {
IntegerKeyframeAnimation(List<Keyframe<int>> keyframes) : super(keyframes); IntegerKeyframeAnimation(super.keyframes);
@override @override
int getValue(Keyframe<int> keyframe, double keyframeProgress) { int getValue(Keyframe<int> keyframe, double keyframeProgress) {

View File

@ -1,7 +1,6 @@
import '../../value/keyframe.dart';
import 'base_keyframe_animation.dart'; import 'base_keyframe_animation.dart';
abstract class KeyframeAnimation<T extends Object> abstract class KeyframeAnimation<T extends Object>
extends BaseKeyframeAnimation<T, T> { extends BaseKeyframeAnimation<T, T> {
KeyframeAnimation(List<Keyframe<T>> keyframes) : super(keyframes); KeyframeAnimation(super.keyframes);
} }

View File

@ -7,9 +7,9 @@ class PathKeyframe extends Keyframe<Offset> {
Path? _path; Path? _path;
final Keyframe<Offset> _pointKeyFrame; final Keyframe<Offset> _pointKeyFrame;
PathKeyframe(LottieComposition composition, Keyframe<Offset> keyframe) PathKeyframe(LottieComposition super.composition, Keyframe<Offset> keyframe)
: _pointKeyFrame = keyframe, : _pointKeyFrame = keyframe,
super(composition, super(
startValue: keyframe.startValue, startValue: keyframe.startValue,
endValue: keyframe.endValue, endValue: keyframe.endValue,
interpolator: keyframe.interpolator, interpolator: keyframe.interpolator,

View File

@ -7,7 +7,7 @@ class PathKeyframeAnimation extends KeyframeAnimation<Offset> {
PathKeyframe? _pathMeasureKeyframe; PathKeyframe? _pathMeasureKeyframe;
late PathMetric _pathMeasure; late PathMetric _pathMeasure;
PathKeyframeAnimation(List<Keyframe<Offset>> keyframes) : super(keyframes); PathKeyframeAnimation(super.keyframes);
@override @override
Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) { Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) {

View File

@ -3,7 +3,7 @@ import '../../value/keyframe.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class PointKeyframeAnimation extends KeyframeAnimation<Offset> { class PointKeyframeAnimation extends KeyframeAnimation<Offset> {
PointKeyframeAnimation(List<Keyframe<Offset>> keyframes) : super(keyframes); PointKeyframeAnimation(super.keyframes);
@override @override
Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) { Offset getValue(Keyframe<Offset> keyframe, double keyframeProgress) {

View File

@ -3,14 +3,15 @@ import '../../model/content/shape_data.dart';
import '../../utils/misc.dart'; import '../../utils/misc.dart';
import '../../utils/path_factory.dart'; import '../../utils/path_factory.dart';
import '../../value/keyframe.dart'; import '../../value/keyframe.dart';
import '../content/shape_modifier_content.dart';
import 'base_keyframe_animation.dart'; import 'base_keyframe_animation.dart';
class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> { class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
final ShapeData _tempShapeData = ShapeData.empty(); final ShapeData _tempShapeData = ShapeData.empty();
final Path _tempPath = PathFactory.create(); final Path _tempPath = PathFactory.create();
List<ShapeModifierContent>? _shapeModifiers;
ShapeKeyframeAnimation(List<Keyframe<ShapeData>> keyframes) ShapeKeyframeAnimation(super.keyframes);
: super(keyframes);
@override @override
Path getValue(Keyframe<ShapeData> keyframe, double keyframeProgress) { Path getValue(Keyframe<ShapeData> keyframe, double keyframeProgress) {
@ -19,7 +20,18 @@ class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
_tempShapeData.interpolateBetween( _tempShapeData.interpolateBetween(
startShapeData, endShapeData, keyframeProgress); 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; return _tempPath;
} }
void setShapeModifiers(List<ShapeModifierContent>? shapeModifiers) {
_shapeModifiers = shapeModifiers;
}
} }

View File

@ -1,18 +1,67 @@
import '../../model/document_data.dart'; import '../../model/document_data.dart';
import '../../value/keyframe.dart'; import '../../value/keyframe.dart';
import '../../value/lottie_frame_info.dart';
import '../../value/lottie_value_callback.dart';
import 'keyframe_animation.dart'; import 'keyframe_animation.dart';
class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> { class TextKeyframeAnimation extends KeyframeAnimation<DocumentData> {
TextKeyframeAnimation(List<Keyframe<DocumentData>> keyframes) TextKeyframeAnimation(super.keyframes);
: super(keyframes);
@override @override
DocumentData getValue( DocumentData getValue(
Keyframe<DocumentData> keyframe, double keyframeProgress) { 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!; return keyframe.startValue!;
} else { } else {
return keyframe.endValue!; 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,10 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../lottie.dart'; import '../lottie.dart';
import 'composition.dart'; import 'composition.dart';
import 'frame_rate.dart';
import 'l.dart'; import 'l.dart';
import 'lottie_builder.dart'; import 'lottie_builder.dart';
import 'options.dart'; import 'providers/lottie_provider.dart';
import 'providers/load_image.dart';
/// A widget to display a loaded [LottieComposition]. /// A widget to display a loaded [LottieComposition].
/// The [controller] property allows to specify a custom AnimationController that /// The [controller] property allows to specify a custom AnimationController that
@ -15,8 +12,11 @@ import 'providers/load_image.dart';
/// automatically and the behavior could be adjusted with the properties [animate], /// automatically and the behavior could be adjusted with the properties [animate],
/// [repeat] and [reverse]. /// [repeat] and [reverse].
class Lottie extends StatefulWidget { class Lottie extends StatefulWidget {
/// The cache instance for recently loaded Lottie compositions.
static LottieCache get cache => sharedLottieCache;
const Lottie({ const Lottie({
Key? key, super.key,
required this.composition, required this.composition,
this.controller, this.controller,
this.width, this.width,
@ -30,11 +30,11 @@ class Lottie extends StatefulWidget {
this.delegates, this.delegates,
this.options, this.options,
bool? addRepaintBoundary, bool? addRepaintBoundary,
this.filterQuality,
}) : animate = animate ?? true, }) : animate = animate ?? true,
reverse = reverse ?? false, reverse = reverse ?? false,
repeat = repeat ?? true, repeat = repeat ?? true,
addRepaintBoundary = addRepaintBoundary ?? true, addRepaintBoundary = addRepaintBoundary ?? true;
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle]. /// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
static LottieBuilder asset( static LottieBuilder asset(
@ -55,9 +55,10 @@ class Lottie extends StatefulWidget {
double? width, double? width,
double? height, double? height,
BoxFit? fit, BoxFit? fit,
Alignment? alignment, AlignmentGeometry? alignment,
String? package, String? package,
bool? addRepaintBoundary, bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning, WarningCallback? onWarning,
}) => }) =>
LottieBuilder.asset( LottieBuilder.asset(
@ -81,6 +82,7 @@ class Lottie extends StatefulWidget {
alignment: alignment, alignment: alignment,
package: package, package: package,
addRepaintBoundary: addRepaintBoundary, addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning, onWarning: onWarning,
); );
@ -102,8 +104,9 @@ class Lottie extends StatefulWidget {
double? width, double? width,
double? height, double? height,
BoxFit? fit, BoxFit? fit,
Alignment? alignment, AlignmentGeometry? alignment,
bool? addRepaintBoundary, bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning, WarningCallback? onWarning,
}) => }) =>
LottieBuilder.file( LottieBuilder.file(
@ -125,6 +128,7 @@ class Lottie extends StatefulWidget {
fit: fit, fit: fit,
alignment: alignment, alignment: alignment,
addRepaintBoundary: addRepaintBoundary, addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning, onWarning: onWarning,
); );
@ -146,8 +150,9 @@ class Lottie extends StatefulWidget {
double? width, double? width,
double? height, double? height,
BoxFit? fit, BoxFit? fit,
Alignment? alignment, AlignmentGeometry? alignment,
bool? addRepaintBoundary, bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning, WarningCallback? onWarning,
}) => }) =>
LottieBuilder.memory( LottieBuilder.memory(
@ -169,6 +174,7 @@ class Lottie extends StatefulWidget {
fit: fit, fit: fit,
alignment: alignment, alignment: alignment,
addRepaintBoundary: addRepaintBoundary, addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning, onWarning: onWarning,
); );
@ -190,8 +196,9 @@ class Lottie extends StatefulWidget {
double? width, double? width,
double? height, double? height,
BoxFit? fit, BoxFit? fit,
Alignment? alignment, AlignmentGeometry? alignment,
bool? addRepaintBoundary, bool? addRepaintBoundary,
FilterQuality? filterQuality,
WarningCallback? onWarning, WarningCallback? onWarning,
}) => }) =>
LottieBuilder.network( LottieBuilder.network(
@ -213,6 +220,7 @@ class Lottie extends StatefulWidget {
fit: fit, fit: fit,
alignment: alignment, alignment: alignment,
addRepaintBoundary: addRepaintBoundary, addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning, onWarning: onWarning,
); );
@ -303,13 +311,19 @@ class Lottie extends StatefulWidget {
/// This property is `true` by default. /// This property is `true` by default.
final bool addRepaintBoundary; final bool addRepaintBoundary;
/// The quality of the image layer. See [FilterQuality]
/// [FilterQuality.high] is highest quality but slowest.
///
/// Defaults to [FilterQuality.low]
final FilterQuality? filterQuality;
static bool get traceEnabled => L.traceEnabled; static bool get traceEnabled => L.traceEnabled;
static set traceEnabled(bool enabled) { static set traceEnabled(bool enabled) {
L.traceEnabled = enabled; L.traceEnabled = enabled;
} }
@override @override
_LottieState createState() => _LottieState(); State<Lottie> createState() => _LottieState();
} }
class _LottieState extends State<Lottie> with TickerProviderStateMixin { class _LottieState extends State<Lottie> with TickerProviderStateMixin {
@ -370,6 +384,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
height: widget.height, height: widget.height,
fit: widget.fit, fit: widget.fit,
alignment: widget.alignment, alignment: widget.alignment,
filterQuality: widget.filterQuality,
); );
}, },
); );

View File

@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../lottie.dart';
import 'composition.dart'; import 'composition.dart';
import 'frame_rate.dart'; import 'frame_rate.dart';
import 'lottie.dart'; import 'lottie.dart';
import 'lottie_delegates.dart';
import 'options.dart';
import 'providers/asset_provider.dart'; import 'providers/asset_provider.dart';
import 'providers/file_provider.dart'; import 'providers/file_provider.dart';
import 'providers/load_image.dart'; import 'providers/load_image.dart';
@ -33,16 +32,16 @@ typedef LottieErrorWidgetBuilder = Widget Function(
/// Several constructors are provided for the various ways that a Lottie file /// Several constructors are provided for the various ways that a Lottie file
/// can be provided: /// can be provided:
/// ///
/// * [new Lottie], for obtaining a composition from a [LottieProvider]. /// * [Lottie], for obtaining a composition from a [LottieProvider].
/// * [new Lottie.asset], for obtaining a Lottie file from an [AssetBundle] /// * [Lottie.asset], for obtaining a Lottie file from an [AssetBundle]
/// using a key. /// using a key.
/// * [new Lottie.network], for obtaining a lottie file from a URL. /// * [Lottie.network], for obtaining a lottie file from a URL.
/// * [new Lottie.file], for obtaining a lottie file from a [File]. /// * [Lottie.file], for obtaining a lottie file from a [File].
/// * [new Lottie.memory], for obtaining a lottie file from a [Uint8List]. /// * [Lottie.memory], for obtaining a lottie file from a [Uint8List].
/// ///
class LottieBuilder extends StatefulWidget { class LottieBuilder extends StatefulWidget {
const LottieBuilder({ const LottieBuilder({
Key? key, super.key,
required this.lottie, required this.lottie,
this.controller, this.controller,
this.frameRate, this.frameRate,
@ -59,8 +58,9 @@ class LottieBuilder extends StatefulWidget {
this.fit, this.fit,
this.alignment, this.alignment,
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality,
this.onWarning, this.onWarning,
}) : super(key: key); });
/// Creates a widget that displays an [LottieComposition] obtained from the network. /// Creates a widget that displays an [LottieComposition] obtained from the network.
LottieBuilder.network( LottieBuilder.network(
@ -75,7 +75,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
this.width, this.width,
@ -83,10 +83,10 @@ class LottieBuilder extends StatefulWidget {
this.fit, this.fit,
this.alignment, this.alignment,
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = NetworkLottie(src, }) : lottie = NetworkLottie(src,
headers: headers, imageProviderFactory: imageProviderFactory), headers: headers, imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from a [File]. /// Creates a widget that displays an [LottieComposition] obtained from a [File].
/// ///
@ -109,7 +109,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
this.width, this.width,
@ -117,9 +117,9 @@ class LottieBuilder extends StatefulWidget {
this.fit, this.fit,
this.alignment, this.alignment,
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory), }) : lottie = FileLottie(file, imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle]. /// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
LottieBuilder.asset( LottieBuilder.asset(
@ -133,7 +133,7 @@ class LottieBuilder extends StatefulWidget {
this.options, this.options,
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
Key? key, super.key,
AssetBundle? bundle, AssetBundle? bundle,
this.frameBuilder, this.frameBuilder,
this.errorBuilder, this.errorBuilder,
@ -143,12 +143,12 @@ class LottieBuilder extends StatefulWidget {
this.alignment, this.alignment,
String? package, String? package,
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = AssetLottie(name, }) : lottie = AssetLottie(name,
bundle: bundle, bundle: bundle,
package: package, package: package,
imageProviderFactory: imageProviderFactory), imageProviderFactory: imageProviderFactory);
super(key: key);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List]. /// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
LottieBuilder.memory( LottieBuilder.memory(
@ -163,17 +163,16 @@ class LottieBuilder extends StatefulWidget {
LottieImageProviderFactory? imageProviderFactory, LottieImageProviderFactory? imageProviderFactory,
this.onLoaded, this.onLoaded,
this.errorBuilder, this.errorBuilder,
Key? key, super.key,
this.frameBuilder, this.frameBuilder,
this.width, this.width,
this.height, this.height,
this.fit, this.fit,
this.alignment, this.alignment,
this.addRepaintBoundary, this.addRepaintBoundary,
this.filterQuality,
this.onWarning, this.onWarning,
}) : lottie = }) : lottie = MemoryLottie(bytes, imageProviderFactory: imageProviderFactory);
MemoryLottie(bytes, imageProviderFactory: imageProviderFactory),
super(key: key);
/// The lottie animation to load. /// The lottie animation to load.
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie] /// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
@ -361,6 +360,12 @@ class LottieBuilder extends StatefulWidget {
/// This property is `true` by default. /// This property is `true` by default.
final bool? addRepaintBoundary; final bool? addRepaintBoundary;
/// The quality of the image layer. See [FilterQuality]
/// [FilterQuality.high] is highest quality but slowest.
///
/// Defaults to [FilterQuality.low]
final FilterQuality? filterQuality;
/// A callback called when there is a warning during the loading or painting /// A callback called when there is a warning during the loading or painting
/// of the animation. /// of the animation.
final WarningCallback? onWarning; final WarningCallback? onWarning;
@ -398,7 +403,7 @@ class LottieBuilder extends StatefulWidget {
final ImageErrorWidgetBuilder? errorBuilder; final ImageErrorWidgetBuilder? errorBuilder;
@override @override
_LottieBuilderState createState() => _LottieBuilderState(); State<LottieBuilder> createState() => _LottieBuilderState();
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
@ -482,6 +487,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
fit: widget.fit, fit: widget.fit,
alignment: widget.alignment, alignment: widget.alignment,
addRepaintBoundary: widget.addRepaintBoundary, addRepaintBoundary: widget.addRepaintBoundary,
filterQuality: widget.filterQuality,
); );
if (widget.frameBuilder != null) { if (widget.frameBuilder != null) {

View File

@ -1,7 +1,8 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../lottie.dart'; import 'composition.dart';
import 'lottie_drawable.dart'; import 'lottie_drawable.dart';
import 'lottie_image_asset.dart';
import 'value_delegate.dart'; import 'value_delegate.dart';
// TODO(xha): recognize style Bold, Medium, Regular, SemiBold, etc... // TODO(xha): recognize style Bold, Medium, Regular, SemiBold, etc...
@ -81,5 +82,5 @@ class LottieDelegates {
values == other.values; values == other.values;
@override @override
int get hashCode => hashValues(text, textStyle, values); int get hashCode => Object.hash(text, textStyle, values);
} }

View File

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

View File

@ -129,8 +129,12 @@ abstract class LottieProperty {
static const dropShadow = DropShadow( static const dropShadow = DropShadow(
color: Color(0x00000000), direction: 0, distance: 0, radius: 0); 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 = static const ColorFilter colorFilter =
ColorFilter.mode(Color(0xFF000000), BlendMode.dst); ColorFilter.mode(Color(0xFF000000), BlendMode.dst);
static final List<Color> gradientColor = []; static final List<Color> gradientColor = [];
/// Replace the text for a text layer.
static const text = 'dynamic_text';
} }

View File

@ -1,11 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/color_keyframe_animation.dart'; import '../../animation/keyframe/color_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableColorValue extends BaseAnimatableValue<Color, Color> { class AnimatableColorValue extends BaseAnimatableValue<Color, Color> {
AnimatableColorValue.fromKeyframes(List<Keyframe<Color>> keyframes) AnimatableColorValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
ColorKeyframeAnimation createAnimation() { ColorKeyframeAnimation createAnimation() {

View File

@ -1,12 +1,10 @@
import '../../animation/keyframe/double_keyframe_animation.dart'; import '../../animation/keyframe/double_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableDoubleValue extends BaseAnimatableValue<double, double> { class AnimatableDoubleValue extends BaseAnimatableValue<double, double> {
AnimatableDoubleValue() : super.fromValue(0.0); AnimatableDoubleValue() : super.fromValue(0.0);
AnimatableDoubleValue.fromKeyframes(List<Keyframe<double>> keyframes) AnimatableDoubleValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
DoubleKeyframeAnimation createAnimation() { DoubleKeyframeAnimation createAnimation() {

View File

@ -1,13 +1,11 @@
import '../../animation/keyframe/gradient_color_keyframe_animation.dart'; import '../../animation/keyframe/gradient_color_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../content/gradient_color.dart'; import '../content/gradient_color.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableGradientColorValue class AnimatableGradientColorValue
extends BaseAnimatableValue<GradientColor, GradientColor> { extends BaseAnimatableValue<GradientColor, GradientColor> {
AnimatableGradientColorValue.fromKeyframes( AnimatableGradientColorValue.fromKeyframes(super.keyframes)
List<Keyframe<GradientColor>> keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
GradientColorKeyframeAnimation createAnimation() { GradientColorKeyframeAnimation createAnimation() {

View File

@ -1,13 +1,11 @@
import '../../animation/keyframe/base_keyframe_animation.dart'; import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/integer_keyframe_animation.dart'; import '../../animation/keyframe/integer_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableIntegerValue extends BaseAnimatableValue<int, int> { class AnimatableIntegerValue extends BaseAnimatableValue<int, int> {
AnimatableIntegerValue() : super.fromValue(100); AnimatableIntegerValue() : super.fromValue(100);
AnimatableIntegerValue.fromKeyframes(List<Keyframe<int>> keyframes) AnimatableIntegerValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
BaseKeyframeAnimation<int, int> createAnimation() { BaseKeyframeAnimation<int, int> createAnimation() {

View File

@ -1,11 +1,9 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/point_keyframe_animation.dart'; import '../../animation/keyframe/point_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatablePointValue extends BaseAnimatableValue<Offset, Offset> { class AnimatablePointValue extends BaseAnimatableValue<Offset, Offset> {
AnimatablePointValue.fromKeyframes(List<Keyframe<Offset>> keyframes) AnimatablePointValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
PointKeyframeAnimation createAnimation() { PointKeyframeAnimation createAnimation() {

View File

@ -1,16 +1,14 @@
import 'dart:ui'; import 'dart:ui';
import '../../animation/keyframe/base_keyframe_animation.dart'; import '../../animation/keyframe/base_keyframe_animation.dart';
import '../../animation/keyframe/point_keyframe_animation.dart'; import '../../animation/keyframe/point_keyframe_animation.dart';
import '../../value/keyframe.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> { class AnimatableScaleValue extends BaseAnimatableValue<Offset, Offset> {
AnimatableScaleValue.one() : this(const Offset(1, 1)); AnimatableScaleValue.one() : this(const Offset(1, 1));
AnimatableScaleValue(Offset value) : super.fromValue(value); AnimatableScaleValue(super.value) : super.fromValue();
AnimatableScaleValue.fromKeyframes(List<Keyframe<Offset>> keyframes) AnimatableScaleValue.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
BaseKeyframeAnimation<Offset, Offset> createAnimation() { BaseKeyframeAnimation<Offset, Offset> createAnimation() {

View File

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

View File

@ -1,12 +1,10 @@
import '../../animation/keyframe/text_keyframe_animation.dart'; import '../../animation/keyframe/text_keyframe_animation.dart';
import '../../value/keyframe.dart';
import '../document_data.dart'; import '../document_data.dart';
import 'base_animatable_value.dart'; import 'base_animatable_value.dart';
class AnimatableTextFrame class AnimatableTextFrame
extends BaseAnimatableValue<DocumentData, DocumentData> { extends BaseAnimatableValue<DocumentData, DocumentData> {
AnimatableTextFrame.fromKeyframes(List<Keyframe<DocumentData>> keyframes) AnimatableTextFrame.fromKeyframes(super.keyframes) : super.fromKeyframes();
: super.fromKeyframes(keyframes);
@override @override
TextKeyframeAnimation createAnimation() { TextKeyframeAnimation createAnimation() {

View File

@ -37,6 +37,7 @@ class PolystarShape implements ContentModel {
final AnimatableDoubleValue? innerRoundedness; final AnimatableDoubleValue? innerRoundedness;
final AnimatableDoubleValue outerRoundedness; final AnimatableDoubleValue outerRoundedness;
final bool hidden; final bool hidden;
final bool isReversed;
PolystarShape({ PolystarShape({
this.name, this.name,
@ -49,6 +50,7 @@ class PolystarShape implements ContentModel {
this.innerRoundedness, this.innerRoundedness,
required this.outerRoundedness, required this.outerRoundedness,
required this.hidden, required this.hidden,
required this.isReversed,
}); });
@override @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; return _initialPoint;
} }
void setClosed(bool closed) {
_closed = closed;
}
bool get isClosed { bool get isClosed {
return _closed; return _closed;
} }

View File

@ -1,7 +1,44 @@
import 'dart:ui'; 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 { class CubicCurveData {
Offset controlPoint1 = Offset.zero; Offset controlPoint1 = Offset.zero;
Offset controlPoint2 = Offset.zero; Offset controlPoint2 = Offset.zero;
Offset vertex = 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

@ -31,7 +31,7 @@ class DocumentData {
@override @override
int get hashCode { int get hashCode {
return hashValues( return Object.hash(
text, text,
fontName, fontName,
size, size,

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