Compare commits

...

27 Commits

Author SHA1 Message Date
78bbbc895f fix: wrap parameters.startFrame between brackets in assert message (#399)
Brackets were missing around parameters.startFrame so only parameters
was print in case the assert failed
2025-03-24 08:43:02 +01:00
5e71dabfa3 Bump version to 3.3.1 (#394) 2025-01-10 10:55:28 +01:00
f892a14a7e Upgrade package:archive ^4.0.0 (#392) 2024-12-16 22:08:00 +01:00
84cfa16673 Update changelog 2024-12-12 17:29:15 +01:00
61756b6613 Flutter 3.27 (#389) 2024-12-12 17:28:06 +01:00
7e4d1d3813 Add conditional imports to prevent importing dart:io on Web targets (#387) 2024-12-10 10:11:07 +01:00
56a69f56d0 Bump codecov/codecov-action from 4.5.0 to 5.1.1 (#388) 2024-12-09 09:02:18 +01:00
d0deffa2ee Apply blend mode at the layer level (#386) 2024-12-08 14:55:59 +01:00
a2e40ecd08 Bump version 2024-10-14 11:10:40 +02:00
5a725a064e Update Archive Dependency to Fix Path Traversal Vulnerability (#379)
This pull request updates the version of the Archive dependency to
eliminate a security vulnerability related to path traversal. The new
version of the library includes fixes that prevent the exploitation of
this vulnerability, ensuring that input paths are handled securely.
Please review the update and let me know if any further adjustments are
needed.

For more information about this vulnerability, you can refer:
- https://github.com/advisories/GHSA-9v85-q87q-g4vg
- https://osv.dev/vulnerability/GHSA-9v85-q87q-g4vg

Thank you for your consideration!

Co-authored-by: Juan <jaymerich93@gmail.com>
2024-10-09 17:04:59 +02:00
8302c4b7e6 Bump codecov/codecov-action from 4.4.0 to 4.5.0 (#362) 2024-06-17 09:04:30 +02:00
029cee4f0a Bump codecov/codecov-action from 4.3.1 to 4.4.0 (#352)
Bumps
[codecov/codecov-action](https://github.com/codecov/codecov-action) from
4.3.1 to 4.4.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.4.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: Clarify isPullRequestFromFork by <a
href="https://github.com/jsoref"><code>@​jsoref</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1411">codecov/codecov-action#1411</a></li>
<li>build(deps): bump actions/checkout from 4.1.4 to 4.1.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1423">codecov/codecov-action#1423</a></li>
<li>build(deps): bump github/codeql-action from 3.25.3 to 3.25.4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1421">codecov/codecov-action#1421</a></li>
<li>build(deps): bump ossf/scorecard-action from 2.3.1 to 2.3.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1420">codecov/codecov-action#1420</a></li>
<li>feat: remove GPG and run on spawn by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1426">codecov/codecov-action#1426</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
7.8.0 to 7.9.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1428">codecov/codecov-action#1428</a></li>
<li>chore(release): 4.4.0 by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1430">codecov/codecov-action#1430</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/codecov/codecov-action/compare/v4.3.1...v4.4.0">https://github.com/codecov/codecov-action/compare/v4.3.1...v4.4.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6d798873df"><code>6d79887</code></a>
chore(release): 4.4.0 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1430">#1430</a>)</li>
<li><a
href="37364fa731"><code>37364fa</code></a>
build(deps-dev): bump <code>@​typescript-eslint/parser</code> from 7.8.0
to 7.9.0 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1428">#1428</a>)</li>
<li><a
href="2791a5c4fe"><code>2791a5c</code></a>
fix: remove GPG and run on spawn (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1426">#1426</a>)</li>
<li><a
href="b71af43c1e"><code>b71af43</code></a>
build(deps): bump ossf/scorecard-action from 2.3.1 to 2.3.3 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1420">#1420</a>)</li>
<li><a
href="29f97fc54f"><code>29f97fc</code></a>
build(deps): bump github/codeql-action from 3.25.3 to 3.25.4 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1421">#1421</a>)</li>
<li><a
href="645d2a5926"><code>645d2a5</code></a>
build(deps): bump actions/checkout from 4.1.4 to 4.1.5 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1423">#1423</a>)</li>
<li><a
href="f691d469fb"><code>f691d46</code></a>
chore: Clarify isPullRequestFromFork (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1411">#1411</a>)</li>
<li>See full diff in <a
href="https://github.com/codecov/codecov-action/compare/v4.3.1...v4.4.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=4.3.1&new-version=4.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 14:43:31 +02:00
eeda2f4de4 Fix expected int (#351)
Fixes #348
2024-05-17 20:41:58 +02:00
ba039e9423 Allow missing end values for integer animations (#349) 2024-05-13 16:21:17 +02:00
3c58936d36 Fix rounding-off error on progress calculation (#346)
I fix a blinking issue when change lottie progress. The duration of
compositon has offset in LottieCompositionParser (parameters.endFrame =
reader.nextDouble() - 0.01), but progress calculate is based on the
original value(no-offset), the function roundProgress() will caculate a
error progress.

The issue is same as: airbnb/lottie-android#1372.

---------

Co-authored-by: Xavier Hainaux <xavier.hainaux@gmail.com>
2024-05-12 20:16:11 +02:00
d0fed17a70 Bump codecov/codecov-action from 4.3.0 to 4.3.1 (#347) 2024-05-06 07:23:21 +02:00
1c113d961a Bump codecov/codecov-action from 4.2.0 to 4.3.0 (#343) 2024-04-15 10:15:38 +02:00
17449658bb Bump codecov/codecov-action from 4.1.0 to 4.2.0 (#341) 2024-04-08 08:47:39 +02:00
e258c610f5 Bump codecov/codecov-action from 4.0.1 to 4.1.0 (#336)
Bumps
[codecov/codecov-action](https://github.com/codecov/codecov-action) from
4.0.1 to 4.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's
releases</a>.</em></p>
<blockquote>
<h2>v4.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: set safe directory by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1304">codecov/codecov-action#1304</a></li>
<li>build(deps): bump github/codeql-action from 3.24.3 to 3.24.5 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1306">codecov/codecov-action#1306</a></li>
<li>build(deps-dev): bump eslint from 8.56.0 to 8.57.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1305">codecov/codecov-action#1305</a></li>
<li>chore(release): v4.1.0 by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1307">codecov/codecov-action#1307</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/codecov/codecov-action/compare/v4.0.2...v4.1.0">https://github.com/codecov/codecov-action/compare/v4.0.2...v4.1.0</a></p>
<h2>v4.0.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1251">codecov/codecov-action#1251</a></li>
<li>build(deps-dev): bump <code>@​types/jest</code> from 29.5.11 to
29.5.12 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1257">codecov/codecov-action#1257</a></li>
<li>build(deps): bump github/codeql-action from 3.23.2 to 3.24.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1266">codecov/codecov-action#1266</a></li>
<li>Escape pipes in table of arguments by <a
href="https://github.com/jwodder"><code>@​jwodder</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1265">codecov/codecov-action#1265</a></li>
<li>Add link to docs on Dependabot secrets by <a
href="https://github.com/ianlewis"><code>@​ianlewis</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1260">codecov/codecov-action#1260</a></li>
<li>fix: working-directory input for all stages by <a
href="https://github.com/Bo98"><code>@​Bo98</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1272">codecov/codecov-action#1272</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
6.20.0 to 6.21.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1271">codecov/codecov-action#1271</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 6.20.0 to 6.21.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1269">codecov/codecov-action#1269</a></li>
<li>build(deps): bump github/codeql-action from 3.24.0 to 3.24.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1298">codecov/codecov-action#1298</a></li>
<li>Use updated syntax for GitHub Markdown notes by <a
href="https://github.com/jamacku"><code>@​jamacku</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1300">codecov/codecov-action#1300</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 6.21.0 to 7.0.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1290">codecov/codecov-action#1290</a></li>
<li>build(deps): bump actions/upload-artifact from 4.3.0 to 4.3.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1286">codecov/codecov-action#1286</a></li>
<li>chore(release): bump to 4.0.2 by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1302">codecov/codecov-action#1302</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jwodder"><code>@​jwodder</code></a> made
their first contribution in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1265">codecov/codecov-action#1265</a></li>
<li><a href="https://github.com/ianlewis"><code>@​ianlewis</code></a>
made their first contribution in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1260">codecov/codecov-action#1260</a></li>
<li><a href="https://github.com/Bo98"><code>@​Bo98</code></a> made their
first contribution in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1272">codecov/codecov-action#1272</a></li>
<li><a href="https://github.com/jamacku"><code>@​jamacku</code></a> made
their first contribution in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1300">codecov/codecov-action#1300</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/codecov/codecov-action/compare/v4.0.1...v4.0.2">https://github.com/codecov/codecov-action/compare/v4.0.1...v4.0.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="54bcd8715e"><code>54bcd87</code></a>
chore(release): v4.1.0 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1307">#1307</a>)</li>
<li><a
href="8ba77ef8d7"><code>8ba77ef</code></a>
build(deps-dev): bump eslint from 8.56.0 to 8.57.0 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1305">#1305</a>)</li>
<li><a
href="c60aa801e3"><code>c60aa80</code></a>
build(deps): bump github/codeql-action from 3.24.3 to 3.24.5 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1306">#1306</a>)</li>
<li><a
href="2fc4847d3f"><code>2fc4847</code></a>
fix: set safe directory (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1304">#1304</a>)</li>
<li><a
href="0cfda1dd0a"><code>0cfda1d</code></a>
chore(release): bump to 4.0.2 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1302">#1302</a>)</li>
<li><a
href="7d3a55eb5e"><code>7d3a55e</code></a>
build(deps): bump actions/upload-artifact from 4.3.0 to 4.3.1 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1286">#1286</a>)</li>
<li><a
href="fe84a0b3c0"><code>fe84a0b</code></a>
build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 6.21.0 to 7.0.0 (...</li>
<li><a
href="e12c9402c4"><code>e12c940</code></a>
Use updated syntax for GitHub Markdown notes (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1300">#1300</a>)</li>
<li><a
href="ef7f8a5d3c"><code>ef7f8a5</code></a>
build(deps): bump github/codeql-action from 3.24.0 to 3.24.3 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1298">#1298</a>)</li>
<li><a
href="b8a1d6a424"><code>b8a1d6a</code></a>
build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 6.20.0 to 6.21.0 ...</li>
<li>Additional commits viewable in <a
href="https://github.com/codecov/codecov-action/compare/v4.0.1...v4.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=4.0.1&new-version=4.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-12 18:22:51 +01:00
945285175a Set version 3.1.0 2024-02-21 15:19:24 +01:00
cfb29485b0 Use package:http (#334) 2024-02-21 15:11:36 +01:00
31ab666099 Add codecov (#333) 2024-02-21 10:03:45 +01:00
8881e357c6 Update changelog 2024-01-11 14:57:07 +01:00
1c665c7756 Small fixes (#331) 2024-01-11 13:41:39 +01:00
cad1806f2e Allow easing to go negative for bounce in/out (#330)
Don't clamp interpolated distance along path to 'progress' value between
0 and 1.

Bounce in / Bounce out easings can transform the `progress` to negative
values or values higher than 1. `getTangentForOffset` unfortunately
clamps the input to be between 0 and 1, so we need to handle the `t < 0`
and `t > 1` cases separately.
2024-01-11 12:14:40 +01:00
08e9678dc3 Background loading & RenderCache.drawingCommands (#326) 2023-12-22 18:21:28 +01:00
9f4a8d74d5 Fix wrong order of lerp when parsing of color stops (#324)
The `_getColorInBetweenColorStops` interpolated between color stops with
the order of stops inverted between the two it should interpolate with
(comparing with `_getColorInBetweenOpacityStops` confirms this as well).
This means that if there is more than two stops in the gradient, the
second color in the gradient never appears and the third one takes its
place, and the colors are on the wrong stop after that.
2023-12-18 14:40:24 +01:00
620 changed files with 18224 additions and 1385 deletions

View File

@ -8,15 +8,12 @@ on:
jobs:
analyze_and_test:
name: Flutter analyze
strategy:
matrix:
flutter: ['stable']
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: ${{ matrix.flutter }}
channel: 'stable'
- run: flutter doctor
- run: flutter --version
- run: flutter pub get

22
.github/workflows/coverage.yaml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Coverage
on:
pull_request:
push:
branches:
- master
jobs:
coverage:
name: Coverage
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter test --coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5.1.1
with:
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -1,37 +1,46 @@
## 3.0.0-alpha.3
- Reduce the max memory used when using `enableRenderCache` (now limited to 50MB)
- Allow to configure the memory with a global settings:
```dart
Lottie.renderCacheMaxMemory = 75000000;
```
## 3.3.1
- Update `package:archive` to `>=4.0.0` constraint
## 3.0.0-alpha.2
- Implement auto-orient
- Add support for layer blend mode
- Require Flutter 3.16
## 3.3.0
- Requires Flutter 3.27 and fix lints.
- Add conditional imports to prevent importing `dart:io` on Web targets
## 3.0.0-alpha.1
- Add `enableRenderCache` parameter.
## 3.2.0
- Apply Blend mode at layer level
## 3.1.3
- Update `package:archive` dependency constraints
## 3.1.2
- Fixes for some animations generated by lottiefiles.com
## 3.1.1
- Fix rounding-off error on progress calculation
- Allow missing end values for integer animations
## 3.1.0
- Use `package:http` for `Lottie.network`. This allows to drop dependency on `dart:html` and be compatible with `wasm`.
- Fix new lints
## 3.0.0
- Add `renderCache` parameter.
```dart
Lottie.asset('assets/complex_animation.json',
enableRenderCache: true,
renderCache: RenderCache.raster,
)
```
It allows to opt into a mode where the frames of the animation are rendered lazily in an offscreen cache.
Subsequent runs of the animation will be very cheap to render.
Opt-in to a special render mode where the frames of the animation are lazily rendered and kept in a cache.
Subsequent runs of the animation are cheaper to render.
This is useful is the animation is complex and can consume a lot of energy from the battery.
It's a trade-off to lower the CPU usage at the cost of an increased memory usage.
There are 2 kinds of caches:
The render cache is managed internally and will release the memory once the animation is disposed.
The cache is shared between all animations. If 2 `Lottie` widget are rendered at the same size, they will render only
once.
Any change in the configuration of the animation (delegates, frame rate etc...) will clear the cache.
Any change in the size will invalidate the cache. The cache use the final size visible on the screen (with all
transforms applied).
**RenderCache.raster**: keep the frame rasterized in the cache (as [dart:ui.Image]).
Subsequent runs of the animation are very cheap for both the CPU and GPU but it takes
a lot of memory.
**RenderCache.drawingCommands**: keep the frame as a list of graphical operations ([dart:ui.Picture]).
Subsequent runs of the animation are cheaper for the CPU but not for the GPU.
- Allow to load Telegram Stickers (.tgs)
@ -43,7 +52,7 @@ Lottie.asset(
```
- Expose a hook to customize how to decode zip archives. This is useful for dotlottie archives (.lottie) when we want
to specify a specific .json file inside the archive
to specify a specific .json file inside the archive
```dart
Lottie.asset(
@ -58,21 +67,46 @@ Future<LottieComposition?> customDecoder(List<int> bytes) {
}
```
- Add `backgroundLoading` parameter to `Lottie.asset|network|file|memory`.
If `backgroundLoading` is true, the animation will be loaded in a background isolate.
This is useful for large animations that can take a long time to parse and block the UI work.
- Remove the name property from `LottieComposition`
- `imageProviderFactory` is not used in .zip file by default anymore.
To restore the old behaviour, use:
To restore the old behaviour, use:
```dart
Future<LottieComposition?> decoder(List<int> bytes) {
return LottieComposition.decodeZip(bytes, imageProviderFactory: imageProviderFactory);
}
Lottie.asset('anim.json', imageProviderFactory: imageProviderFactory, decoder: decoder)
Lottie.asset('anim.json', decoder: decoder)
```
- Disable gradient cache optimization when `ValueDelegate.gradientColor` is used
- Use `DefaultAssetBundle.of` in `AssetLottie` before fallback to `rootBundle`
- Add `BuildContext` optional parameter in `LottieProvider.load`
- Fixed varying opacity stops across keyframes in the same gradient
- Fixed rounded corners for non-closed curves
- Implement auto-orient
- Add support for layer blend mode
- Require Flutter 3.16
## 3.0.0-alpha.4
*See the latest 3.0.0 release*
## 3.0.0-alpha.3
*See the latest 3.0.0 release*
## 3.0.0-alpha.2
*See the latest 3.0.0 release*
## 3.0.0-alpha.1
*See the latest 3.0.0 release*
## 2.7.0
- Support loading Fonts from a zip file

2
FUNDING.yml Normal file
View File

@ -0,0 +1,2 @@
github: xvrh
custom: https://buymeacoffee.com/xvrh

View File

@ -10,6 +10,8 @@ This repository is an unofficial conversion of the [Lottie-android](https://gith
It works on Android, iOS, macOS, linux, windows and web.
<a href="https://www.buymeacoffee.com/xvrh" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60" width="217"></a>
## Usage
### Simple animation
@ -306,10 +308,10 @@ Future<LottieComposition?> customDecoder(List<int> bytes) {
## Performance or excessive CPU/GPU usage
Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
Version `v3.0` introduced the `renderCache` parameter to help reduce an excessive energy consumption.
In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
are cheaper to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).

View File

@ -10,6 +10,8 @@ This repository is an unofficial conversion of the [Lottie-android](https://gith
It works on Android, iOS, macOS, linux, windows and web.
<a href="https://www.buymeacoffee.com/xvrh" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60" width="217"></a>
## Usage
### Simple animation
@ -107,10 +109,10 @@ import 'example/lib/examples/dotlottie.dart#example';
## Performance or excessive CPU/GPU usage
Version `v3.0` introduced the `enableRenderCache` parameter to help reduce an excessive energy consumption.
Version `v3.0` introduced the `renderCache` parameter to help reduce an excessive energy consumption.
In this mode, the frames of the animation are rendered lazily in an offscreen cache. Subsequent runs of the animation
are very cheap to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
are cheaper to render. It helps reduce the power usage of the application at the cost of an increased memory usage.
## Limitations
This port supports the same [feature set as Lottie Android](https://airbnb.io/lottie/#/supported-features).

View File

@ -18,7 +18,6 @@ linter:
avoid_js_rounded_ints: true
avoid_positional_boolean_parameters: true
avoid_redundant_argument_values: true
avoid_returning_null_for_future: true
avoid_setters_without_getters: true
avoid_type_to_string: true
avoid_unused_constructor_parameters: true

1
example/.gitignore vendored
View File

@ -31,7 +31,6 @@
/build/
# Web related
lib/generated_plugin_registrant.dart
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

View File

@ -0,0 +1 @@
{"v":"5.12.1","fr":25,"ip":0,"op":50,"w":1080,"h":2330,"nm":"Testcomp","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Top Shape With Multiply","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,1165,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":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[608,608],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.996078012504,0.996078012504,0.996078012504,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.108819677316,0.687484142827,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4,-261],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":825,"st":0,"ct":1,"bm":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape With Outline","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-45,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.25,"y":1},"o":{"x":0.75,"y":0},"t":0,"s":[524,1208,0],"to":[0,39.333,0],"ti":[0,0,0]},{"i":{"x":0.25,"y":1},"o":{"x":0.75,"y":0},"t":25,"s":[524,1444,0],"to":[0,0,0],"ti":[0,39.333,0]},{"t":49,"s":[524,1208,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-74,327,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":24,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.109803929048,0.686274509804,0.956862804936,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-74,327],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":825,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"BKG","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,1165,0],"ix":2,"l":2},"a":{"a":0,"k":[540,1165,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1080,"sh":2330,"sc":"#ffffff","ip":0,"op":825,"st":0,"bm":0}],"markers":[],"props":{}}

View File

@ -0,0 +1,898 @@
{
"v": "5.7.5",
"fr": 100,
"ip": 0,
"op": 400,
"w": 800,
"h": 1000,
"nm": "Comp 1",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 0,
"ty": 4,
"nm": "Bounce out curve",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
0.21176470588235294,
0.9176470588235294,
1
],
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 1,
"k": [
{
"t": 0,
"s": [
510,
100
],
"i": {
"x": [
0.2
],
"y": [
1
]
},
"o": {
"x": [
0.5
],
"y": [
-0.5
]
},
"ti": [
-268.19047619047615,
-294.42857142857247
],
"to": [
-268.19047619047615,
238.90476190476068
]
},
{
"t": 400,
"s": [
510,
900
],
"i": {
"x": [
1
],
"y": [
1
]
},
"o": {
"x": [
0
],
"y": [
0
]
}
}
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 401,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Bounce in curve",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0.3411764705882353,
0.21176470588235294
],
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 1,
"k": [
{
"t": 0,
"s": [
360,
100
],
"i": {
"x": [
0.5
],
"y": [
1.5
]
},
"o": {
"x": [
0.8
],
"y": [
0
]
},
"ti": [
-268.19047619047615,
-294.42857142857247
],
"to": [
-268.19047619047615,
238.90476190476068
]
},
{
"t": 400,
"s": [
360,
900
],
"i": {
"x": [
1
],
"y": [
1
]
},
"o": {
"x": [
0
],
"y": [
0
]
}
}
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 401,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "Bounce in",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0.7254901960784313,
0.5764705882352941
],
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 1,
"k": [
{
"t": 0,
"s": [
650,
100
],
"i": {
"x": [
0.5
],
"y": [
1.5
]
},
"o": {
"x": [
0.8
],
"y": [
0
]
}
},
{
"t": 400,
"s": [
650,
900
],
"i": {
"x": [
1
],
"y": [
1
]
},
"o": {
"x": [
0
],
"y": [
0
]
}
}
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 401,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 3,
"ty": 4,
"nm": "Bounce out",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0.8392156862745098,
0.25098039215686274
],
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 1,
"k": [
{
"t": 0,
"s": [
150,
100
],
"i": {
"x": [
0.2
],
"y": [
1
]
},
"o": {
"x": [
0.5
],
"y": [
-0.5
]
}
},
{
"t": 400,
"s": [
150,
900
],
"i": {
"x": [
1
],
"y": [
1
]
},
"o": {
"x": [
0
],
"y": [
0
]
}
}
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 401,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@ -0,0 +1,522 @@
{
"v": "4.8.0",
"meta": {
"g": "LottieFiles AE 3.5.2",
"a": "",
"k": "",
"d": "",
"tc": "#000000"
},
"fr": 24,
"ip": 0,
"op": 6912,
"w": 312,
"h": 312,
"nm": "Master_1-4a- GREEN",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 30,
"ty": 4,
"nm": "Background_Layers",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
158,
154,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
50,
50,
100
],
"ix": 6
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[
149.841,
0
],
[
0,
-149.841
],
[
-149.841,
0
],
[
0,
149.841
]
],
"o": [
[
-149.841,
0
],
[
0,
149.841
],
[
149.841,
0
],
[
0,
-149.841
]
],
"v": [
[
0,
-271.311
],
[
-271.311,
0
],
[
0,
271.311
],
[
271.311,
0
]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "st",
"c": {
"a": 0,
"k": [
1,
1,
1,
1
],
"ix": 3
},
"o": {
"a": 0,
"k": 100,
"ix": 4
},
"w": {
"a": 0,
"k": 0,
"ix": 5
},
"lc": 1,
"lj": 1,
"ml": 4,
"bm": 0,
"nm": "Stroke 1",
"mn": "ADBE Vector Graphic - Stroke",
"hd": false
},
{
"ty": "gf",
"o": {
"a": 0,
"k": 60,
"ix": 10
},
"r": 1,
"bm": 0,
"g": {
"p": 3,
"k": {
"a": 1,
"k": [
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 898.499,
"s": [
0.799,
0.988,
0.875,
0.435,
0.9,
0.994,
0.937,
0.718,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 1112.312,
"s": [
0.799,
0.988,
0.875,
0.435,
0.9,
0.994,
0.937,
0.718,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 1330.13,
"s": [
0.799,
0.828,
0.855,
0.554,
0.9,
0.914,
0.928,
0.777,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 1420.621,
"s": [
0.799,
0.761,
0.847,
0.604,
0.9,
0.88,
0.924,
0.802,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 4054.455,
"s": [
0.799,
0.761,
0.847,
0.604,
0.9,
0.88,
0.924,
0.802,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 4311.512,
"s": [
0.799,
0.675,
0.859,
0.894,
0.9,
0.837,
0.929,
0.947,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"i": {
"x": 0.833,
"y": 0.833
},
"o": {
"x": 0.167,
"y": 0.167
},
"t": 6439.239,
"s": [
0.799,
0.675,
0.859,
0.894,
0.9,
0.837,
0.929,
0.947,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
},
{
"t": 6672.2724609375,
"s": [
0.799,
0.71,
0.098,
0.392,
0.9,
0.855,
0.549,
0.696,
1,
1,
1,
1,
0.072,
1,
0.536,
0.5,
1,
0
]
}
],
"ix": 9
}
},
"s": {
"a": 0,
"k": [
0.798,
1.032
],
"ix": 5
},
"e": {
"a": 0,
"k": [
244.484,
0
],
"ix": 6
},
"t": 2,
"h": {
"a": 0,
"k": 0,
"ix": 7
},
"a": {
"a": 0,
"k": 0,
"ix": 8
},
"nm": "Gradient Fill 1",
"mn": "ADBE Vector Graphic - G-Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
-4.865,
2.221
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 1
},
"s": {
"a": 0,
"k": [
108.71,
108.71
],
"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": "Transformieren"
}
],
"nm": "Ellipse 1",
"np": 3,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": -8.80880880880881,
"op": 7462.66266266266,
"st": -8.80880880880881,
"bm": 0
}
],
"markers": []
}

View File

@ -0,0 +1,678 @@
{
"v": "5.7.5",
"fr": 100,
"ip": 0,
"op": 300,
"w": 100,
"h": 100,
"nm": "Comp 1",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 0,
"ty": 4,
"nm": "Linear gradient",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "sh",
"hd": false,
"d": 1,
"ks": {
"a": 0,
"k": {
"c": false,
"v": [
[
-17.99999846997801,
-9
],
[
17.99999846997801,
-9
],
[
17.99999846997801,
9
],
[
-17.99999846997801,
9
],
[
-17.99999846997801,
-9
]
],
"i": [
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
]
],
"o": [
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
],
[
0,
0
]
]
}
}
},
{
"ty": "gf",
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"g": {
"p": 5,
"k": {
"a": 0,
"k": [
0,
0,
1,
0,
0.6126387150748301,
0,
0,
1,
0.8114341216791559,
1,
0,
1,
0.9555392466605757,
1,
1,
0,
0.9977200010227434,
1,
1,
0,
0,
1,
0.6126387150748301,
1,
0.8114341216791559,
0,
0.9555392466605757,
0.3146550641021125,
0.9977200010227434,
0
],
"ix": 2
}
},
"s": {
"a": 0,
"k": [
-17.99999809265137,
0
],
"ix": 2
},
"e": {
"a": 0,
"k": [
9.028081893920898,
4.32133674621582E-7
],
"ix": 2
},
"t": 1,
"nm": "Gradient Fill 1",
"mn": "ADBE Vector Graphic - G-Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 0,
"k": [
49.99999618530273,
72.5
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
250,
250
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 301,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Radial gradient",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
36,
16
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "gf",
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"g": {
"p": 5,
"k": {
"a": 0,
"k": [
0,
0,
1,
0,
0.5371484055543164,
0,
0,
1,
0.8215441678174731,
1,
0,
1,
0.9571973765957152,
1,
1,
0,
1,
1,
1,
0,
0,
1,
0.5371484055543164,
1,
0.8215441678174731,
0,
0.9571973765957152,
0.3292524136178862,
1,
0
],
"ix": 2
}
},
"s": {
"a": 0,
"k": [
-18,
-7.999999523162842
],
"ix": 2
},
"e": {
"a": 0,
"k": [
9.53293228149414,
-7.999999523162842
],
"ix": 2
},
"t": 2,
"nm": "Gradient Fill 1",
"mn": "ADBE Vector Graphic - G-Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 0,
"k": [
50,
24.99999618530273
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
250,
250
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 301,
"st": 0,
"bm": 0
},
{
"ddd": 0,
"ind": 2,
"ty": 4,
"nm": "Background",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
100,
100
],
"ix": 2
}
},
"ao": 0,
"hd": false,
"shapes": [
{
"ty": "gr",
"hd": false,
"it": [
{
"ty": "rc",
"hd": false,
"d": 1,
"s": {
"a": 0,
"k": [
39.999999658311,
40.00000046690118
],
"ix": 2
},
"p": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
}
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
1,
0.25098039215686274,
0.25098039215686274
],
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tm",
"s": {
"a": 0,
"k": 0,
"ix": 2
},
"e": {
"a": 0,
"k": 100,
"ix": 2
},
"o": {
"a": 0,
"k": 0,
"ix": 2
},
"m": 1,
"hd": false
},
{
"ty": "tr",
"hd": false,
"p": {
"a": 0,
"k": [
50,
50
],
"ix": 2
},
"a": {
"a": 0,
"k": [
0,
0
],
"ix": 2
},
"s": {
"a": 0,
"k": [
250,
250
],
"ix": 2
},
"r": {
"a": 0,
"k": 0,
"ix": 2
},
"o": {
"a": 0,
"k": 100,
"ix": 2
},
"sk": {
"a": 0,
"k": 0,
"ix": 2
},
"sa": {
"a": 0,
"k": 0,
"ix": 2
}
}
]
}
],
"ip": 0,
"op": 301,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@ -0,0 +1,222 @@
{
"v": "5.9.1",
"fr": 25,
"ip": 0,
"op": 75,
"w": 1200,
"h": 1200,
"nm": "square",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 15,
"ty": 4,
"nm": "Fond Silhouettes",
"sr": 1,
"ks": {
"o": {
"a": 0,
"k": 100,
"ix": 11
},
"r": {
"a": 0,
"k": 0,
"ix": 10
},
"p": {
"a": 0,
"k": [
600,
600,
0
],
"ix": 2,
"l": 2
},
"a": {
"a": 0,
"k": [
600,
600,
0
],
"ix": 1,
"l": 2
},
"s": {
"a": 0,
"k": [
100,
100,
100
],
"ix": 6,
"l": 2
}
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"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": [
[
-600,
600
],
[
600,
600
],
[
600,
-600
],
[
-600,
-600
]
],
"c": true
},
"ix": 2
},
"nm": "Tracé 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [
0.561497886508,
0.699996708889,
0.56712066052,
1
],
"ix": 4
},
"o": {
"a": 0,
"k": 100,
"ix": 5
},
"r": 1,
"bm": 0,
"nm": "Fond 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 0,
"k": [
600,
600
],
"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": "Transformer "
}
],
"nm": "Groupe 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 76,
"st": 0,
"bm": 0
}
],
"markers": []
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '11.0'
platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -15,9 +15,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d
PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048
COCOAPODS: 1.14.2
COCOAPODS: 1.15.2

View File

@ -163,7 +163,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -351,7 +351,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -366,8 +366,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TC6K7794M3;
DEVELOPMENT_TEAM = PS45A9TPZ7;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -382,8 +384,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_BUNDLE_IDENTIFIER = com.example;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -437,7 +440,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -486,7 +489,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -502,8 +505,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TC6K7794M3;
DEVELOPMENT_TEAM = PS45A9TPZ7;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -518,8 +523,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_BUNDLE_IDENTIFIER = com.example;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -533,8 +539,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = TC6K7794M3;
DEVELOPMENT_TEAM = PS45A9TPZ7;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -549,8 +557,9 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.github.xvrh.lottie.example;
PRODUCT_BUNDLE_IDENTIFIER = com.example;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View File

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

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart' hide Image;
import 'package:flutter/material.dart' as material;
import 'package:lottie/lottie.dart';
/// This example shows how to cache the animation as a List<Image>.
/// This example shows how to cache the animation as a `List<Image>`.
/// After the initial cache of each frame, drawing the animation is almost free
/// in term of CPU usage.
/// The animation will run at a specific framerate (not FrameRate.max) and specific size

View File

@ -1,10 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
// ignore: implementation_imports
import 'package:lottie/src/render_cache.dart';
void main() {
globalRenderCache.enableDebugBackground = true;
runApp(const App());
}
@ -45,133 +43,77 @@ class App extends StatelessWidget {
}
}
class _Example extends StatefulWidget {
static String _text(a) => '';
@override
State<_Example> createState() => _ExampleState();
}
class _ExampleState extends State<_Example> {
int _animationCount = 1;
class _Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
ElevatedButton(
onPressed: () {
setState(() {
++_animationCount;
});
_Row(
builder: (cache) {
return Lottie.asset('assets/Mobilo/Z.json',
renderCache: cache, height: 100);
},
child: Text('Add animation $_animationCount'),
),
Row(
children: [
Expanded(
child: Stack(
children: [
for (var i = 0; i < _animationCount; i++)
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(60),
enableRenderCache: true,
fit: BoxFit.cover,
delegates: LottieDelegates(
text: _Example._text,
values: [
ValueDelegate.color(['*'], value: Color(i)),
],
),
),
),
],
),
),
Expanded(
child: Lottie.asset(
'assets/Mobilo/B.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.cover,
),
),
],
),
Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
for (var fit in [BoxFit.cover, BoxFit.fill, BoxFit.contain])
_Row(
builder: (cache) {
return Lottie.asset(
'assets/lottiefiles/bb8.json',
renderCache: cache,
fit: fit,
height: 60,
);
},
),
child: Lottie.asset('assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
enableRenderCache: true),
),
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 2,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
enableRenderCache: true,
frameRate: const FrameRate(10),
fit: BoxFit.fill,
),
),
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
),
child: Transform.scale(
scale: 0.5,
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.green, width: 2),
),
child: Lottie.asset(
'assets/Mobilo/A.json',
height: 200,
frameRate: const FrameRate(10),
enableRenderCache: true,
fit: BoxFit.fill,
),
),
),
),
),
],
_Row(
builder: (cache) {
return Lottie.asset(
'assets/lottiefiles/a_mountain.json',
renderCache: cache,
height: 40,
);
},
),
for (var align in [
Alignment.bottomCenter,
Alignment.center,
Alignment.topRight
])
_Row(
builder: (cache) {
return Lottie.asset('assets/lottiefiles/bomb.json',
renderCache: cache, height: 40, alignment: align);
},
),
],
);
}
}
class _Row extends StatelessWidget {
final Widget Function(RenderCache? cache) builder;
const _Row({required this.builder});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(border: Border.all(color: Colors.green)),
child: Row(
children: [
for (var cache in [
null,
RenderCache.raster,
RenderCache.drawingCommands
])
Expanded(child: builder(cache))
],
),
);
}
}
class RenderCacheDebugPanel extends StatefulWidget {
const RenderCacheDebugPanel({super.key});
@ -180,40 +122,34 @@ class RenderCacheDebugPanel extends StatefulWidget {
}
class _RenderCacheDebugPanelState extends State<RenderCacheDebugPanel> {
late Timer _refreshTimer;
@override
void initState() {
super.initState();
_refreshTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
setState(() {
// refresh
});
});
}
@override
Widget build(BuildContext context) {
return StreamBuilder<void>(
stream: globalRenderCache.onUpdate,
builder: (context, snapshot) {
return ListView(
children: [
Text('Images: ${globalRenderCache.imageCount}'),
Text(
'Memory: ${(globalRenderCache.totalMemory / 1000000).toStringAsFixed(1)}MB'),
const Divider(),
ElevatedButton(
onPressed: () {
globalRenderCache.clear();
},
child: const Text('Clear'),
),
const Divider(),
SwitchListTile(
title: const Text('Enable debug background'),
value: globalRenderCache.enableDebugBackground,
onChanged: (v) {
setState(() {
globalRenderCache.enableDebugBackground = v;
});
},
)
],
);
});
return ListView(
children: [
Text('Images: ${RenderCache.raster.store.imageCount}'),
Text(
'Memory: ${(RenderCache.raster.store.totalMemory / 1000000).toStringAsFixed(1)}MB'),
const Divider(),
],
);
}
@override
void dispose() {
_refreshTimer.cancel();
super.dispose();
}
}

View File

@ -42,7 +42,8 @@ class App extends StatelessWidget {
child: Lottie.asset(
assetName,
fit: BoxFit.contain,
enableRenderCache: true,
renderCache: RenderCache.drawingCommands,
backgroundLoading: false,
onWarning: (w) => _logger.info('$assetName - $w'),
frameBuilder: (context, child, composition) {
return AnimatedOpacity(
@ -77,7 +78,7 @@ class _Item extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(10)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: Colors.black.withValues(alpha: 0.1),
offset: const Offset(2, 2),
blurRadius: 5)
]),

View File

@ -18,6 +18,7 @@ class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
showPerformanceOverlay: true,
home: Scaffold(
appBar: AppBar(
title: const Text(''),
@ -136,6 +137,7 @@ class __LottieState extends State<_Lottie> with TickerProviderStateMixin {
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Lottie(
composition: widget.composition,
renderCache: RenderCache.raster,
controller: _controller,
width: widget.width,
height: widget.height,

View File

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

View File

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

View File

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

View File

@ -2,19 +2,7 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View File

@ -1,18 +1,5 @@
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.assets.pictures.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
<dict/>
</plist>

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
url: "https://pub.dev"
source: hosted
version: "3.4.9"
version: "4.0.2"
async:
dependency: transitive
description:
@ -45,26 +45,26 @@ packages:
dependency: "direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.2"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.6"
fake_async:
dependency: transitive
description:
@ -77,10 +77,10 @@ packages:
dependency: transitive
description:
name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.3"
flutter:
dependency: "direct main"
description: flutter
@ -90,134 +90,150 @@ packages:
dependency: "direct main"
description:
name: flutter_colorpicker
sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "1.1.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
golden_toolkit:
dependency: "direct dev"
description:
name: golden_toolkit
sha256: "8f74adab33154fe7b731395782797021f97d2edc52f7bfb85ff4f1b5c4a215f0"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
http:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
js:
version: "4.1.2"
leak_tracker:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
name: leak_tracker
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
url: "https://pub.dev"
source: hosted
version: "0.6.7"
version: "10.0.7"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "5.1.1"
logging:
dependency: "direct main"
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
lottie:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "3.0.0-alpha.3"
version: "3.3.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.15.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
path:
dependency: "direct main"
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
version: "1.9.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
@ -230,47 +246,63 @@ packages:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.3.0"
platform:
dependency: transitive
description:
name: platform
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.7"
pointycastle:
version: "2.1.8"
posix:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
name: posix
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
url: "https://pub.dev"
source: hosted
version: "3.7.3"
version: "6.0.1"
shelf:
dependency: "direct dev"
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_static:
dependency: "direct dev"
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_span:
dependency: transitive
description:
@ -283,10 +315,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.0"
stream_channel:
dependency: transitive
description:
@ -299,10 +331,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
term_glyph:
dependency: transitive
description:
@ -315,18 +347,18 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.3"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.4.0"
vector_math:
dependency: transitive
description:
@ -335,30 +367,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
url: "https://pub.dev"
source: hosted
version: "14.3.0"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "0.3.0"
win32:
dependency: transitive
description:
name: win32
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
url: "https://pub.dev"
source: hosted
version: "5.1.0"
version: "1.1.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
version: "1.1.0"
sdks:
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View File

@ -5,7 +5,7 @@ publish_to: none
version: 2.7.0+1
environment:
sdk: "^3.2.0"
sdk: "^3.6.0"
dependencies:
collection:
@ -23,7 +23,8 @@ dev_dependencies:
flutter_lints:
flutter_test:
sdk: flutter
golden_toolkit:
shelf:
shelf_static:
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -0,0 +1,9 @@
import 'dart:io';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_static/shelf_static.dart';
void main() async {
var server = await serve(
createStaticHandler('build/web'), InternetAddress.loopbackIPv4, 0);
print('Listen on http://${server.address.host}:${server.port}/index.html');
}

View File

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

View File

@ -14,7 +14,6 @@ import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/dash_path.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../utils/utils.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
@ -29,8 +28,8 @@ import 'trim_path_content.dart';
abstract class BaseStrokeContent
implements KeyPathElementContent, DrawingContent {
final Path _path = PathFactory.create();
final Path _trimPathPath = PathFactory.create();
final Path _path = Path();
final Path _trimPathPath = Path();
final LottieDrawable lottieDrawable;
final BaseLayer layer;
final List<_PathGroup> _pathGroups = <_PathGroup>[];
@ -135,8 +134,7 @@ abstract class BaseStrokeContent
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
L.beginSection('StrokeContent#draw');
if (parentMatrix.hasZeroScaleAxis) {
L.endSection('StrokeContent#draw');

View File

@ -8,7 +8,6 @@ import '../../model/key_path.dart';
import '../../model/key_path_element.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/transform_keyframe_animation.dart';
import 'content.dart';
@ -42,7 +41,7 @@ class ContentGroup implements DrawingContent, PathContent, KeyPathElement {
}
final Matrix4 _matrix = Matrix4.identity();
final Path _path = PathFactory.create();
final Path _path = Path();
@override
final String? name;
@ -144,8 +143,7 @@ class ContentGroup implements DrawingContent, PathContent, KeyPathElement {
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
if (_hidden) {
return;
}
@ -176,7 +174,7 @@ class ContentGroup implements DrawingContent, PathContent, KeyPathElement {
for (var i = _contents.length - 1; i >= 0; i--) {
Object content = _contents[i];
if (content is DrawingContent) {
content.draw(canvas, size, _matrix, parentAlpha: childAlpha);
content.draw(canvas, _matrix, parentAlpha: childAlpha);
}
}

View File

@ -3,7 +3,6 @@ import 'package:vector_math/vector_math_64.dart';
import 'content.dart';
abstract class DrawingContent extends Content {
void draw(Canvas canvas, Size canvasSize, Matrix4 parentMatrix,
{required int parentAlpha});
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha});
Rect getBounds(Matrix4 parentMatrix, {required bool applyParents});
}

View File

@ -7,7 +7,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@ -19,7 +18,7 @@ import 'trim_path_content.dart';
class EllipseContent implements PathContent, KeyPathElementContent {
static const _ellipseControlPointPercentage = 0.55228;
final Path _path = PathFactory.create();
final Path _path = Path();
@override
final String? name;

View File

@ -9,7 +9,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
@ -21,7 +20,7 @@ import 'key_path_element_content.dart';
import 'path_content.dart';
class FillContent implements DrawingContent, KeyPathElementContent {
final Path _path = PathFactory.create();
final Path _path = Path();
final BaseLayer layer;
@override
final String? name;
@ -79,17 +78,13 @@ class FillContent implements DrawingContent, KeyPathElementContent {
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
if (_hidden) {
return;
}
L.beginSection('FillContent#draw');
var paint = Paint()..color = _colorAnimation.value;
if (layer.blendMode case var blendMode?) {
paint.blendMode = blendMode;
}
var alpha =
((parentAlpha / 255.0 * _opacityAnimation.value / 100.0) * 255).round();
paint.setAlpha(alpha.clamp(0, 255));

View File

@ -11,7 +11,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/drop_shadow.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
@ -29,7 +28,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
final GradientFill _fill;
final _linearGradientCache = <int, Gradient>{};
final _radialGradientCache = <int, Gradient>{};
final _path = PathFactory.create();
final _path = Path();
final _paint = Paint();
final _paths = <PathContent>[];
final BaseKeyframeAnimation<GradientColor, GradientColor> _colorAnimation;
@ -97,8 +96,7 @@ class GradientFillContent implements DrawingContent, KeyPathElementContent {
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
if (_fill.hidden) {
return;
}

View File

@ -61,8 +61,7 @@ class GradientStrokeContent extends BaseStrokeContent {
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
if (_hidden) {
return;
}
@ -76,7 +75,7 @@ class GradientStrokeContent extends BaseStrokeContent {
paint.shader = gradient;
super.draw(canvas, size, parentMatrix, parentAlpha: parentAlpha);
super.draw(canvas, parentMatrix, parentAlpha: parentAlpha);
}
Gradient _getLinearGradient(Matrix4 parentMatrix) {

View File

@ -1,16 +1,15 @@
import 'dart:ui';
import '../../model/content/merge_paths.dart';
import '../../utils.dart';
import '../../utils/path_factory.dart';
import 'content.dart';
import 'content_group.dart';
import 'greedy_content.dart';
import 'path_content.dart';
class MergePathsContent implements PathContent, GreedyContent {
final Path _firstPath = PathFactory.create();
final Path _remainderPath = PathFactory.create();
final Path _path = PathFactory.create();
final Path _firstPath = Path();
final Path _remainderPath = Path();
final Path _path = Path();
final List<PathContent> _pathContents = <PathContent>[];
final MergePaths _mergePaths;

View File

@ -8,7 +8,6 @@ import '../../model/content/shape_trim_path.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@ -24,7 +23,7 @@ class PolystarContent implements PathContent, KeyPathElementContent {
/// work otherwise.
static const _polystarMagicNumber = .47829;
static const _polygonMagicNumber = .25;
final _path = PathFactory.create();
final _path = Path();
final LottieDrawable lottieDrawable;
final PolystarShape _polystarShape;

View File

@ -8,7 +8,6 @@ import '../../model/content/shape_trim_path.dart';
import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
@ -19,7 +18,7 @@ import 'rounded_corners_content.dart';
import 'trim_path_content.dart';
class RectangleContent implements KeyPathElementContent, PathContent {
final _path = PathFactory.create();
final _path = Path();
@override
final String? name;

View File

@ -7,7 +7,6 @@ import '../../model/key_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import '../keyframe/base_keyframe_animation.dart';
import '../keyframe/transform_keyframe_animation.dart';
@ -25,7 +24,7 @@ class RepeaterContent
GreedyContent,
KeyPathElementContent {
final Matrix4 _matrix = Matrix4.identity();
final _path = PathFactory.create();
final _path = Path();
final LottieDrawable lottieDrawable;
final BaseLayer layer;
@ -105,8 +104,7 @@ class RepeaterContent
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
var copies = _copies.value;
var offset = _offset.value;
var startOpacity = _transform.startOpacity!.value / 100.0;
@ -116,7 +114,7 @@ class RepeaterContent
_matrix.preConcat(_transform.getMatrixForRepeater(i + offset));
var newAlpha =
parentAlpha * lerpDouble(startOpacity, endOpacity, i / copies)!;
_contentGroup!.draw(canvas, size, _matrix, parentAlpha: newAlpha.round());
_contentGroup!.draw(canvas, _matrix, parentAlpha: newAlpha.round());
}
}

View File

@ -4,7 +4,6 @@ import '../../model/content/shape_path.dart';
import '../../model/content/shape_trim_path.dart';
import '../../model/layer/base_layer.dart';
import '../../utils.dart';
import '../../utils/path_factory.dart';
import '../keyframe/shape_keyframe_animation.dart';
import 'compound_trim_path_content.dart';
import 'content.dart';
@ -13,7 +12,7 @@ import 'shape_modifier_content.dart';
import 'trim_path_content.dart';
class ShapeContent implements PathContent {
final _path = PathFactory.create();
final _path = Path();
final ShapePath _shape;

View File

@ -34,16 +34,16 @@ class StrokeContent extends BaseStrokeContent {
}
@override
void draw(Canvas canvas, Size size, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
if (_hidden) {
return;
}
paint.color = _colorAnimation.value.withAlpha(paint.color.alpha);
paint.color =
_colorAnimation.value.withAlpha((paint.color.a * 255).toInt());
if (_colorFilterAnimation != null) {
paint.colorFilter = _colorFilterAnimation!.value;
}
super.draw(canvas, size, parentMatrix, parentAlpha: parentAlpha);
super.draw(canvas, parentMatrix, parentAlpha: parentAlpha);
}
@override

View File

@ -3,8 +3,8 @@ import '../../l.dart';
import '../../value/keyframe.dart';
import '../../value/lottie_value_callback.dart';
/// @param <K> Keyframe type
/// @param <A> Animation type
/// @param K Keyframe type
/// @param A Animation type
abstract class BaseKeyframeAnimation<K extends Object, A extends Object?> {
// This is not a Set because we don't want to create an iterator object on every setProgress.
final listeners = <void Function()>[];
@ -82,7 +82,7 @@ abstract class BaseKeyframeAnimation<K extends Object, A extends Object?> {
/// the current keyframe's interpolator.
double getInterpolatedCurrentKeyframeProgress() {
var keyframe = getCurrentKeyframe();
if (keyframe.isStatic) {
if (keyframe.isStatic || keyframe.interpolator == null) {
return 0.0;
}
return keyframe.interpolator!.transform(getLinearCurrentKeyframeProgress());

View File

@ -69,8 +69,8 @@ class DropShadowKeyframeAnimation {
if (callback != null) {
_color.setValueCallback(_createCallback(
callback, (c) => c?.color ?? const Color(0xff000000)));
_opacity.setValueCallback(
_createCallback(callback, (c) => c?.color.alpha.toDouble() ?? 255));
_opacity
.setValueCallback(_createCallback(callback, (c) => c?.color.a ?? 1));
_direction.setValueCallback(
_createCallback(callback, (c) => c?.direction ?? 0));
_distance

View File

@ -7,16 +7,18 @@ class IntegerKeyframeAnimation extends KeyframeAnimation<int> {
@override
int getValue(Keyframe<int> keyframe, double keyframeProgress) {
if (keyframe.startValue == null || keyframe.endValue == null) {
if (keyframe.startValue == null) {
throw Exception('Missing values for keyframe.');
}
var endValue = keyframe.endValue ?? keyframe.startValue;
if (valueCallback != null) {
var value = valueCallback!.getValueInternal(
keyframe.startFrame,
keyframe.endFrame,
keyframe.startValue,
keyframe.endValue,
endValue,
keyframeProgress,
getLinearCurrentKeyframeProgress(),
progress);
@ -25,7 +27,6 @@ class IntegerKeyframeAnimation extends KeyframeAnimation<int> {
}
}
return lerpDouble(keyframe.startValue, keyframe.endValue, keyframeProgress)!
.round();
return lerpDouble(keyframe.startValue, endValue, keyframeProgress)!.round();
}
}

View File

@ -16,21 +16,20 @@ class PathKeyframe extends Keyframe<Offset> {
xInterpolator: keyframe.xInterpolator,
yInterpolator: keyframe.yInterpolator,
startFrame: keyframe.startFrame,
endFrame: keyframe.endFrame) {
createPath();
}
endFrame: keyframe.endFrame);
void createPath() {
Path? _createPath() {
var equals =
endValue != null && startValue != null && startValue == endValue;
if (startValue != null && endValue != null && !equals) {
_path = Utils.createPath(startValue!, endValue!, _pointKeyFrame.pathCp1,
return Utils.createPath(startValue!, endValue!, _pointKeyFrame.pathCp1,
_pointKeyFrame.pathCp2);
}
return null;
}
/// This will be null if the startValue and endValue are the same. */
/// This will be null if the startValue and endValue are the same.
Path? getPath() {
return _path;
return _path ??= _createPath();
}
}

View File

@ -36,8 +36,22 @@ class PathKeyframeAnimation extends KeyframeAnimation<Offset> {
_pathMeasureKeyframe = pathKeyframe;
}
return _pathMeasure
.getTangentForOffset(keyframeProgress * _pathMeasure.length)!
.position;
var length = _pathMeasure.length;
// allow bounce easings to calculate positions outside the path
// by using the tangent at the extremities
if (keyframeProgress < 0) {
var tangent = _pathMeasure.getTangentForOffset(0)!;
return tangent.position + tangent.vector * (keyframeProgress * length);
} else if (keyframeProgress > 1) {
var tangent = _pathMeasure.getTangentForOffset(length)!;
return tangent.position +
tangent.vector * ((keyframeProgress - 1) * length);
} else {
return _pathMeasure
.getTangentForOffset(keyframeProgress * length)!
.position;
}
}
}

View File

@ -1,14 +1,13 @@
import 'dart:ui';
import '../../model/content/shape_data.dart';
import '../../utils/misc.dart';
import '../../utils/path_factory.dart';
import '../../value/keyframe.dart';
import '../content/shape_modifier_content.dart';
import 'base_keyframe_animation.dart';
class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
final ShapeData _tempShapeData = ShapeData.empty();
final Path _tempPath = PathFactory.create();
final Path _tempPath = Path();
List<ShapeModifierContent>? _shapeModifiers;
ShapeKeyframeAnimation(super.keyframes);
@ -16,7 +15,7 @@ class ShapeKeyframeAnimation extends BaseKeyframeAnimation<ShapeData, Path> {
@override
Path getValue(Keyframe<ShapeData> keyframe, double keyframeProgress) {
var startShapeData = keyframe.startValue!;
var endShapeData = keyframe.endValue!;
var endShapeData = keyframe.endValue ?? startShapeData;
_tempShapeData.interpolateBetween(
startShapeData, endShapeData, keyframeProgress);

View File

@ -1,6 +1,5 @@
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui';
import 'package:archive/archive.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
@ -46,7 +45,6 @@ class LottieComposition {
static Future<LottieComposition> fromByteData(
ByteData data, {
LottieImageProviderFactory? imageProviderFactory,
LottieDecoder? decoder,
}) {
return fromBytes(data.buffer.asUint8List(), decoder: decoder);
@ -79,7 +77,7 @@ class LottieComposition {
}
jsonFile ??= archive.files.firstWhere((e) => e.name.endsWith('.json'));
var composition = parseJsonBytes(jsonFile.content as Uint8List);
var composition = parseJsonBytes(jsonFile.content);
for (var image in composition.images.values) {
var imagePath = p.posix.join(image.dirName, image.fileName);
@ -96,8 +94,8 @@ class LottieComposition {
}
if (found != null) {
image.loadedImage ??= await loadImage(
composition, image, MemoryImage(found.content as Uint8List));
image.loadedImage ??=
await loadImage(composition, image, MemoryImage(found.content));
}
}
@ -105,8 +103,8 @@ class LottieComposition {
var fileName = p.basenameWithoutExtension(font.name).toLowerCase();
var existingFont = composition.fonts.values
.firstWhereOrNull((f) => f.family.toLowerCase() == fileName);
await loadFontFromList(font.content as Uint8List,
fontFamily: existingFont?.family);
composition._fontsToLoad
.add(FontToLoad(font.content, family: existingFont?.family));
}
return composition;
}
@ -138,6 +136,8 @@ class LottieComposition {
/// was only faster until you had ~4 masks after which it would actually become slower.
int _maskAndMatteCount = 0;
final _fontsToLoad = <FontToLoad>[];
WarningCallback? onWarning;
void addWarning(String warning) {
@ -211,9 +211,8 @@ class LottieComposition {
return _parameters.images;
}
double get durationFrames {
return endFrame - startFrame;
}
/// Number of frames in the animation
double get durationFrames => endFrame - startFrame;
/// Returns a "rounded" progress value according to the frameRate
double roundProgress(double progress, {required FrameRate frameRate}) {
@ -224,8 +223,10 @@ class LottieComposition {
fps = this.frameRate;
}
fps ??= frameRate.framesPerSecond;
assert(!fps.isNaN && fps.isFinite && !fps.isNegative);
var totalFrameCount = seconds * fps;
var noOffsetDurationFrames = durationFrames + 0.01;
var totalFrameCount = (noOffsetDurationFrames / this.frameRate) * fps;
var frameIndex = (totalFrameCount * progress).toInt();
var roundedProgress = frameIndex / totalFrameCount;
assert(roundedProgress >= 0 && roundedProgress <= 1,
@ -242,3 +243,19 @@ class LottieComposition {
return sb.toString();
}
}
class FontToLoad {
final Uint8List bytes;
final String? family;
FontToLoad(this.bytes, {this.family});
static List<FontToLoad>? getAndClear(LottieComposition composition) {
if (composition._fontsToLoad.isNotEmpty) {
var fonts = composition._fontsToLoad.toList();
composition._fontsToLoad.clear();
return fonts;
}
return null;
}
}

View File

@ -1,11 +1,11 @@
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import '../lottie.dart';
import 'composition.dart';
import 'l.dart';
import 'lottie_builder.dart';
import 'providers/lottie_provider.dart';
import 'render_cache.dart';
/// A widget to display a loaded [LottieComposition].
/// The [controller] property allows to specify a custom AnimationController that
@ -16,15 +16,6 @@ class Lottie extends StatefulWidget {
/// The cache instance for recently loaded Lottie compositions.
static LottieCache get cache => sharedLottieCache;
/// The maximum memory to use when using `enableRenderCache`.
/// When the limit is reached, new frames are not put in the cache until some
/// memory is released. When an animation disappear from the screen, its memory
/// is released immediately.
static int get renderCacheMaxMemory => globalRenderCache.maxMemory;
static set renderCacheMaxMemory(int value) {
globalRenderCache.maxMemory = value;
}
const Lottie({
super.key,
required this.composition,
@ -41,12 +32,11 @@ class Lottie extends StatefulWidget {
this.options,
bool? addRepaintBoundary,
this.filterQuality,
bool? enableRenderCache,
this.renderCache,
}) : animate = animate ?? true,
reverse = reverse ?? false,
repeat = repeat ?? true,
addRepaintBoundary = addRepaintBoundary ?? true,
enableRenderCache = enableRenderCache ?? false;
addRepaintBoundary = addRepaintBoundary ?? true;
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
static LottieBuilder asset(
@ -73,7 +63,8 @@ class Lottie extends StatefulWidget {
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
RenderCache? renderCache,
bool? backgroundLoading,
}) =>
LottieBuilder.asset(
name,
@ -99,12 +90,13 @@ class Lottie extends StatefulWidget {
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
static LottieBuilder file(
Object /*io.File|html.File*/ file, {
Object file, {
Animation<double>? controller,
FrameRate? frameRate,
bool? animate,
@ -125,7 +117,8 @@ class Lottie extends StatefulWidget {
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
RenderCache? renderCache,
bool? backgroundLoading,
}) =>
LottieBuilder.file(
file,
@ -149,7 +142,8 @@ class Lottie extends StatefulWidget {
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
@ -175,7 +169,8 @@ class Lottie extends StatefulWidget {
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
RenderCache? renderCache,
bool? backgroundLoading,
}) =>
LottieBuilder.memory(
bytes,
@ -199,12 +194,15 @@ class Lottie extends StatefulWidget {
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
);
/// Creates a widget that displays an [LottieComposition] obtained from the network.
static LottieBuilder network(
String url, {
http.Client? client,
Map<String, String>? headers,
Animation<double>? controller,
FrameRate? frameRate,
bool? animate,
@ -225,10 +223,13 @@ class Lottie extends StatefulWidget {
FilterQuality? filterQuality,
WarningCallback? onWarning,
LottieDecoder? decoder,
bool? enableRenderCache,
RenderCache? renderCache,
bool? backgroundLoading,
}) =>
LottieBuilder.network(
url,
client: client,
headers: headers,
controller: controller,
frameRate: frameRate,
animate: animate,
@ -249,7 +250,8 @@ class Lottie extends StatefulWidget {
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
enableRenderCache: enableRenderCache,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
);
/// The Lottie composition to animate.
@ -346,26 +348,38 @@ class Lottie extends StatefulWidget {
/// Defaults to [FilterQuality.low]
final FilterQuality? filterQuality;
/// Opt-in a special render mode where the frames of the animation are
/// lazily rendered in offscreen images.
/// Subsequent runs of the animation will be very cheap to render.
/// {@template lottie.renderCache}
/// Opt-in to a special render mode where the frames of the animation are
/// lazily rendered and kept in a cache.
/// Subsequent runs of the animation will be cheaper to render.
///
/// This is useful is the animation is complex and can consume lot of energy
/// from the battery.
/// This is will trade an excessive CPU usage for an increase memory usage.
/// This will trade an excessive CPU usage for an increase memory usage.
/// The main use-case is a short and small (size on the screen) animation that is
/// played repeatedly.
///
/// There are 2 kinds of caches:
/// - [RenderCache.raster]: keep the frame rasterized in the cache (as [dart:ui.Image]).
/// Subsequent runs of the animation are very cheap for both the CPU and GPU but it takes
/// a lot of memory (rendered_width * rendered_height * frame_rate * duration_of_the_animation).
/// This should only be used for very short and very small animations.
/// - [RenderCache.drawingCommands]: keep the frame as a list of graphical operations ([dart:ui.Picture]).
/// Subsequent runs of the animation are cheaper for the CPU but not for the GPU.
/// Memory usage is a lot lower than RenderCache.raster.
///
/// The render cache is managed internally and will release the memory once the
/// animation is disposed. The cache is shared between all animations. If 2 `Lottie`
/// widget are rendered at the same size, they will render only once.
///
/// animation disappear. The cache is shared between all animations.
/// Any change in the configuration of the animation (delegates, frame rate etc...)
/// will clear the cache.
/// Any change in the size will invalidate the cache. The cache use the final size
/// visible on the screen (with all transforms applied).
/// will clear the cache entry.
/// For RenderCache.raster, any change in the size will invalidate the cache entry. The cache
/// use the final size visible on the screen (with all transforms applied).
///
/// In order to not exceed the memory limit of a device, the cache is constrained
/// In order to not exceed the memory limit of a device, the raster cache is constrained
/// to maximum 50MB. After that, animations are not cached anymore.
final bool enableRenderCache;
/// {@endtemplate}
final RenderCache? renderCache;
static bool get traceEnabled => L.traceEnabled;
static set traceEnabled(bool enabled) {
@ -435,7 +449,7 @@ class _LottieState extends State<Lottie> with TickerProviderStateMixin {
fit: widget.fit,
alignment: widget.alignment,
filterQuality: widget.filterQuality,
enableRenderCache: widget.enableRenderCache,
renderCache: widget.renderCache,
);
},
);

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'composition.dart';
import 'frame_rate.dart';
import 'lottie.dart';
@ -12,6 +13,7 @@ import 'providers/load_image.dart';
import 'providers/lottie_provider.dart';
import 'providers/memory_provider.dart';
import 'providers/network_provider.dart';
import 'render_cache.dart';
typedef LottieFrameBuilder = Widget Function(
BuildContext context,
@ -60,12 +62,13 @@ class LottieBuilder extends StatefulWidget {
this.addRepaintBoundary,
this.filterQuality,
this.onWarning,
this.enableRenderCache,
this.renderCache,
});
/// Creates a widget that displays an [LottieComposition] obtained from the network.
LottieBuilder.network(
String src, {
http.Client? client,
Map<String, String>? headers,
this.controller,
this.frameRate,
@ -87,11 +90,14 @@ class LottieBuilder extends StatefulWidget {
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
this.renderCache,
bool? backgroundLoading,
}) : lottie = NetworkLottie(src,
client: client,
headers: headers,
imageProviderFactory: imageProviderFactory,
decoder: decoder);
decoder: decoder,
backgroundLoading: backgroundLoading);
/// Creates a widget that displays an [LottieComposition] obtained from a [File].
///
@ -104,7 +110,7 @@ class LottieBuilder extends StatefulWidget {
/// `android.permission.READ_EXTERNAL_STORAGE` permission.
///
LottieBuilder.file(
Object /*io.File|html.File*/ file, {
Object file, {
this.controller,
this.frameRate,
this.animate,
@ -125,9 +131,14 @@ class LottieBuilder extends StatefulWidget {
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = FileLottie(file,
imageProviderFactory: imageProviderFactory, decoder: decoder);
this.renderCache,
bool? backgroundLoading,
}) : lottie = FileLottie(
file,
imageProviderFactory: imageProviderFactory,
decoder: decoder,
backgroundLoading: backgroundLoading,
);
/// Creates a widget that displays an [LottieComposition] obtained from an [AssetBundle].
LottieBuilder.asset(
@ -154,12 +165,14 @@ class LottieBuilder extends StatefulWidget {
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
this.renderCache,
bool? backgroundLoading,
}) : lottie = AssetLottie(name,
bundle: bundle,
package: package,
imageProviderFactory: imageProviderFactory,
decoder: decoder);
decoder: decoder,
backgroundLoading: backgroundLoading);
/// Creates a widget that displays an [LottieComposition] obtained from a [Uint8List].
LottieBuilder.memory(
@ -184,9 +197,14 @@ class LottieBuilder extends StatefulWidget {
this.filterQuality,
this.onWarning,
LottieDecoder? decoder,
this.enableRenderCache,
}) : lottie = MemoryLottie(bytes,
imageProviderFactory: imageProviderFactory, decoder: decoder);
this.renderCache,
bool? backgroundLoading,
}) : lottie = MemoryLottie(
bytes,
imageProviderFactory: imageProviderFactory,
decoder: decoder,
backgroundLoading: backgroundLoading,
);
/// The lottie animation to load.
/// Example of providers: [AssetLottie], [NetworkLottie], [FileLottie], [MemoryLottie]
@ -417,26 +435,36 @@ class LottieBuilder extends StatefulWidget {
/// ```
final ImageErrorWidgetBuilder? errorBuilder;
/// Opt-in a special render mode where the frames of the animation are
/// lazily rendered in offscreen images.
/// Subsequent runs of the animation will be very cheap to render.
/// Opt-in to a special render mode where the frames of the animation are
/// lazily rendered and kept in a cache.
/// Subsequent runs of the animation will be cheaper to render.
///
/// This is useful is the animation is complex and can consume lot of energy
/// from the battery.
/// This is will trade an excessive CPU usage for an increase memory usage.
/// This will trade an excessive CPU usage for an increase memory usage.
/// The main use-case is a short and small (size on the screen) animation that is
/// played repeatedly.
///
/// There are 2 kinds of caches:
/// - [RenderCache.raster]: keep the frame rasterized in the cache (as [dart:ui.Image]).
/// Subsequent runs of the animation are very cheap for both the CPU and GPU but it takes
/// a lot of memory (rendered_width * rendered_height * frame_rate * duration_of_the_animation).
/// This should only be used for very short and very small animations.
/// - [RenderCache.drawingCommands]: keep the frame as a list of graphical operations ([dart:ui.Picture]).
/// Subsequent runs of the animation are cheaper for the CPU but not for the GPU.
/// Memory usage is a lot lower than RenderCache.raster.
///
/// The render cache is managed internally and will release the memory once the
/// animation is disposed. The cache is shared between all animations. If 2 `Lottie`
/// widget are rendered at the same size, they will render only once.
///
/// animation disappear. The cache is shared between all animations.
/// Any change in the configuration of the animation (delegates, frame rate etc...)
/// will clear the cache.
/// Any change in the size will invalidate the cache. The cache use the final size
/// visible on the screen (with all transforms applied).
/// will clear the cache entry.
/// For RenderCache.raster, any change in the size will invalidate the cache entry. The cache
/// use the final size visible on the screen (with all transforms applied).
///
/// In order to not exceed the memory limit of a device, the cache is constrained
/// In order to not exceed the memory limit of a device, the raster cache is constrained
/// to maximum 50MB. After that, animations are not cached anymore.
final bool? enableRenderCache;
final RenderCache? renderCache;
@override
State<LottieBuilder> createState() => _LottieBuilderState();
@ -533,7 +561,7 @@ class _LottieBuilderState extends State<LottieBuilder> {
alignment: widget.alignment,
addRepaintBoundary: widget.addRepaintBoundary,
filterQuality: widget.filterQuality,
enableRenderCache: widget.enableRenderCache,
renderCache: widget.renderCache,
);
if (widget.frameBuilder != null) {

View File

@ -50,6 +50,8 @@ class LottieDrawable {
_isDirty = true;
}
final _progressAliases = <double, double>{};
double get progress => _progress ?? 0.0;
double? _progress;
bool setProgress(double value) {
@ -58,8 +60,13 @@ class LottieDrawable {
composition.roundProgress(value, frameRate: frameRate);
if (roundedProgress != _progress) {
_isDirty = false;
var previousProgress = _progress;
_progress = roundedProgress;
_compositionLayer.setProgress(roundedProgress);
if (!_isDirty && frameRate != FrameRate.max && previousProgress != null) {
var alias = _progressAliases[previousProgress] ?? previousProgress;
_progressAliases[roundedProgress] = alias;
}
return _isDirty;
} else {
return false;
@ -76,7 +83,7 @@ class LottieDrawable {
}
}
List<Object?> _configHash() {
List<Object?> configHash() {
return [
enableMergePaths,
filterQuality,
@ -85,6 +92,8 @@ class LottieDrawable {
];
}
int delegatesHash() => _delegatesHash;
int _computeValueDelegateHash(LottieDelegates? delegates) {
if (delegates == null) return 0;
@ -175,7 +184,6 @@ class LottieDrawable {
return keyPaths;
}
static final _normalPaint = Paint();
void draw(
ui.Canvas canvas,
ui.Rect rect, {
@ -206,35 +214,26 @@ class LottieDrawable {
var cacheUsed = false;
if (renderCache != null) {
var rect = Rect.fromPoints(renderCache.localToGlobal(destinationPosition),
renderCache.localToGlobal(destinationRect.bottomRight));
var cacheImageSize = Size(
(rect.size.width * renderCache.devicePixelRatio).roundToDouble(),
(rect.size.height * renderCache.devicePixelRatio).roundToDouble());
var cacheKey = CacheKey(
composition: composition,
size: cacheImageSize,
config: _configHash(),
delegates: _delegatesHash);
var cache = renderCache.handle.withKey(cacheKey);
var cachedImage = cache.imageForProgress(progress, (cacheCanvas) {
_matrix.scale(cacheImageSize.width / sourceSize.width,
cacheImageSize.height / sourceSize.height);
_compositionLayer.draw(cacheCanvas, cacheImageSize, _matrix,
parentAlpha: 255);
});
if (cachedImage != null) {
cacheUsed = true;
canvas.drawImageRect(cachedImage, Offset.zero & cacheImageSize,
destinationRect, _normalPaint);
}
var progressForCache = _progressAliases[progress] ?? progress;
cacheUsed = renderCache.cache.draw(
this,
progressForCache,
canvas,
destinationPosition: destinationPosition,
destinationRect: destinationRect,
sourceSize: sourceSize,
sourceRect: sourceRect,
renderBox: renderCache.renderBox,
devicePixelRatio: renderCache.devicePixelRatio,
);
}
if (!cacheUsed) {
canvas.save();
canvas.translate(destinationRect.left, destinationRect.top);
_matrix.scale(destinationSize.width / sourceRect.width,
destinationSize.height / sourceRect.height);
_compositionLayer.draw(canvas, rect.size, _matrix, parentAlpha: 255);
_compositionLayer.draw(canvas, _matrix, parentAlpha: 255);
canvas.restore();
}
}
@ -247,12 +246,13 @@ class LottieFontStyle {
}
class RenderCacheContext {
final RenderCacheHandle handle;
final Offset Function(Offset) localToGlobal;
final AnimationCache cache;
final RenderBox renderBox;
final double devicePixelRatio;
RenderCacheContext(
{required this.handle,
required this.localToGlobal,
required this.devicePixelRatio});
RenderCacheContext({
required this.cache,
required this.renderBox,
required this.devicePixelRatio,
});
}

View File

@ -2,54 +2,54 @@ import 'dart:ui';
import 'value/drop_shadow.dart';
/// Property values are the same type as the generic type of their corresponding
/// {@link LottieValueCallback}. With this, we can use generics to maintain type safety
/// [LottieValueCallback]. With this, we can use generics to maintain type safety
/// of the callbacks.
///
/// Supported properties:
/// Transform:
/// {@link #TRANSFORM_ANCHOR_POINT}
/// {@link #TRANSFORM_POSITION}
/// {@link #TRANSFORM_OPACITY}
/// {@link #TRANSFORM_SCALE}
/// {@link #TRANSFORM_ROTATION}
/// {@link #TRANSFORM_SKEW}
/// {@link #TRANSFORM_SKEW_ANGLE}
/// {TRANSFORM_ANCHOR_POINT}
/// {TRANSFORM_POSITION}
/// {TRANSFORM_OPACITY}
/// {TRANSFORM_SCALE}
/// {TRANSFORM_ROTATION}
/// {TRANSFORM_SKEW}
/// {TRANSFORM_SKEW_ANGLE}
///
/// Fill:
/// {@link #COLOR} (non-gradient)
/// {@link #OPACITY}
/// {@link #COLOR_FILTER}
/// {#COLOR} (non-gradient)
/// {#OPACITY}
/// {#COLOR_FILTER}
///
/// Stroke:
/// {@link #COLOR} (non-gradient)
/// {@link #STROKE_WIDTH}
/// {@link #OPACITY}
/// {@link #COLOR_FILTER}
/// {#COLOR} (non-gradient)
/// {#STROKE_WIDTH}
/// {#OPACITY}
/// {#COLOR_FILTER}
///
/// Ellipse:
/// {@link #POSITION}
/// {@link #ELLIPSE_SIZE}
/// {#POSITION}
/// {#ELLIPSE_SIZE}
///
/// Polystar:
/// {@link #POLYSTAR_POINTS}
/// {@link #POLYSTAR_ROTATION}
/// {@link #POSITION}
/// {@link #POLYSTAR_INNER_RADIUS} (star)
/// {@link #POLYSTAR_OUTER_RADIUS}
/// {@link #POLYSTAR_INNER_ROUNDEDNESS} (star)
/// {@link #POLYSTAR_OUTER_ROUNDEDNESS}
/// {#POLYSTAR_POINTS}
/// {#POLYSTAR_ROTATION}
/// {#POSITION}
/// {#POLYSTAR_INNER_RADIUS} (star)
/// {#POLYSTAR_OUTER_RADIUS}
/// {#POLYSTAR_INNER_ROUNDEDNESS} (star)
/// {#POLYSTAR_OUTER_ROUNDEDNESS}
///
/// Repeater:
/// All transform properties
/// {@link #REPEATER_COPIES}
/// {@link #REPEATER_OFFSET}
/// {@link #TRANSFORM_ROTATION}
/// {@link #TRANSFORM_START_OPACITY}
/// {@link #TRANSFORM_END_OPACITY}
/// {#REPEATER_COPIES}
/// {#REPEATER_OFFSET}
/// {#TRANSFORM_ROTATION}
/// {#TRANSFORM_START_OPACITY}
/// {#TRANSFORM_END_OPACITY}
///
/// Layers:
/// All transform properties
/// {@link #TIME_REMAP} (composition layers only)
/// {#TIME_REMAP} (composition layers only)
abstract class LottieProperty {
/// ColorInt **/
static const Color color = Color(0x00000001);

View File

@ -1,16 +1,32 @@
import 'dart:ui';
import '../../utils.dart';
import '../../utils/collection.dart';
import '../../utils/gamma_evaluator.dart';
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
class GradientColor {
final List<double> positions;
final List<Color> colors;
GradientColor(this.positions, this.colors);
const GradientColor(this.positions, this.colors);
int get size => colors.length;
void lerp(GradientColor gc1, GradientColor gc2, double progress) {
// Fast return in case start and end is the same
// or if progress is at start/end or out of [0,1] bounds
if (gc1 == gc2) {
_copyFrom(gc1);
return;
} else if (progress <= 0) {
_copyFrom(gc1);
return;
} else if (progress >= 1) {
_copyFrom(gc2);
return;
}
if (gc1.colors.length != gc2.colors.length) {
throw Exception('Cannot interpolate between gradients. '
'Lengths vary (${gc1.colors.length} vs ${gc2.colors.length})');
@ -59,4 +75,30 @@ class GradientColor {
var fraction = (position - startPosition) / (endPosition - startPosition);
return GammaEvaluator.evaluate(fraction, startColor, endColor);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other is! GradientColor) {
return false;
}
return const ListEquality<double>().equals(positions, other.positions) &&
const ListEquality<Color>().equals(colors, other.colors);
}
@override
int get hashCode {
var result = Object.hashAll(positions);
result = 31 * result + Object.hashAll(colors);
return result;
}
void _copyFrom(GradientColor other) {
for (var i = 0; i < other.colors.length; i++) {
positions[i] = other.positions[i];
colors[i] = other.colors[i];
}
}
}

View File

@ -4,9 +4,10 @@ class Font {
final String style;
final double ascent;
Font(
{required this.family,
required this.name,
required this.style,
required this.ascent});
Font({
required this.family,
required this.name,
required this.style,
required this.ascent,
});
}

View File

@ -173,8 +173,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
}
@override
void draw(Canvas canvas, Size canvasSize, Matrix4 parentMatrix,
{required int parentAlpha}) {
void draw(Canvas canvas, Matrix4 parentMatrix, {required int parentAlpha}) {
L.beginSection(_drawTraceName);
if (!_visible || layerModel.isHidden) {
L.endSection(_drawTraceName);
@ -190,10 +189,11 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
L.endSection('Layer#parentMatrix');
var opacity = transform.opacity?.value ?? 100;
var alpha = ((parentAlpha / 255.0 * opacity / 100.0) * 255).toInt();
if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer()) {
var blendMode = this.blendMode;
if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer() && blendMode == null) {
_matrix.preConcat(transform.getMatrix());
L.beginSection('Layer#drawLayer');
drawLayer(canvas, canvasSize, _matrix, parentAlpha: alpha);
drawLayer(canvas, _matrix, parentAlpha: alpha);
L.endSection('Layer#drawLayer');
_recordRenderTime(L.endSection(_drawTraceName));
return;
@ -214,24 +214,19 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
_matrix.preConcat(transform.getMatrix());
bounds = _intersectBoundsWithMask(bounds, _matrix);
if (bounds
.intersect(Rect.fromLTWH(0, 0, canvasSize.width, canvasSize.height))
.isEmpty) {
bounds = Rect.zero;
}
L.endSection('Layer#computeBounds');
if (!bounds.isEmpty) {
L.beginSection('Layer#saveLayer');
_contentPaint.setAlpha(255);
_contentPaint.blendMode = blendMode ?? ui.BlendMode.srcOver;
canvas.saveLayer(bounds, _contentPaint);
L.endSection('Layer#saveLayer');
// Clear the off screen buffer. This is necessary for some phones.
_clearCanvas(canvas, bounds);
L.beginSection('Layer#drawLayer');
drawLayer(canvas, canvasSize, _matrix, parentAlpha: alpha);
drawLayer(canvas, _matrix, parentAlpha: alpha);
L.endSection('Layer#drawLayer');
if (hasMasksOnThisLayer()) {
@ -244,7 +239,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
canvas.saveLayer(bounds, _mattePaint);
L.endSection('Layer#saveLayer');
_clearCanvas(canvas, bounds);
_matteLayer!.draw(canvas, canvasSize, parentMatrix, parentAlpha: alpha);
_matteLayer!.draw(canvas, parentMatrix, parentAlpha: alpha);
L.beginSection('Layer#restoreLayer');
canvas.restore();
L.endSection('Layer#restoreLayer');
@ -339,7 +334,7 @@ abstract class BaseLayer implements DrawingContent, KeyPathElement {
return bounds;
}
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha});
void _applyMasks(Canvas canvas, Rect bounds, Matrix4 matrix) {

View File

@ -75,7 +75,7 @@ class CompositionLayer extends BaseLayer {
}
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
L.beginSection('CompositionLayer#draw');
var newClipRect = Rect.fromLTWH(0, 0, layerModel.preCompWidth.toDouble(),
@ -101,7 +101,7 @@ class CompositionLayer extends BaseLayer {
}
var layer = _layers[i];
layer.draw(canvas, size, parentMatrix, parentAlpha: childAlpha);
layer.draw(canvas, parentMatrix, parentAlpha: childAlpha);
}
canvas.restore();
L.endSection('CompositionLayer#draw');

View File

@ -14,7 +14,7 @@ class ImageLayer extends BaseLayer {
ImageLayer(super.lottieDrawable, super.layerModel);
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
var bitmap = getBitmap();
if (bitmap == null) {

View File

@ -6,7 +6,7 @@ class NullLayer extends BaseLayer {
NullLayer(super.lottieDrawable, super.layerModel);
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
// Do nothing.
}

View File

@ -26,9 +26,9 @@ class ShapeLayer extends BaseLayer {
}
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
_contentGroup.draw(canvas, size, parentMatrix, parentAlpha: parentAlpha);
_contentGroup.draw(canvas, parentMatrix, parentAlpha: parentAlpha);
}
@override

View File

@ -5,14 +5,13 @@ import '../../animation/keyframe/value_callback_keyframe_animation.dart';
import '../../lottie_drawable.dart';
import '../../lottie_property.dart';
import '../../utils.dart';
import '../../utils/path_factory.dart';
import '../../value/lottie_value_callback.dart';
import 'base_layer.dart';
import 'layer.dart';
class SolidLayer extends BaseLayer {
final Paint paint = Paint()..style = PaintingStyle.fill;
final Path path = PathFactory.create();
final Path path = Path();
BaseKeyframeAnimation<ColorFilter, ColorFilter?>? _colorFilterAnimation;
BaseKeyframeAnimation<Color, Color?>? _colorAnimation;
@ -22,23 +21,21 @@ class SolidLayer extends BaseLayer {
}
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
var backgroundAlpha = layerModel.solidColor.alpha;
var backgroundAlpha = layerModel.solidColor.a;
if (backgroundAlpha == 0) {
return;
}
paint.color = _colorAnimation?.value ?? layerModel.solidColor;
var opacity = transform.opacity?.value ?? 100;
var alpha = (parentAlpha /
255.0 *
(backgroundAlpha / 255.0 * opacity / 100.0) *
255.0)
.round();
var alpha =
(parentAlpha / 255.0 * (backgroundAlpha * opacity / 100.0) * 255.0)
.round();
paint.setAlpha(alpha);
if (_colorAnimation?.value case var color?) {
paint.color = color;
}
if (_colorFilterAnimation != null) {
paint.colorFilter = _colorFilterAnimation!.value;
}

View File

@ -90,7 +90,7 @@ class TextLayer extends BaseLayer {
}
@override
void drawLayer(Canvas canvas, Size size, Matrix4 parentMatrix,
void drawLayer(Canvas canvas, Matrix4 parentMatrix,
{required int parentAlpha}) {
var documentData = _textAnimation.value;
var font = _composition.fonts[documentData.fontName];
@ -120,7 +120,7 @@ class TextLayer extends BaseLayer {
} else {
fillPaintColor = documentData.color;
}
_fillPaint.color = fillPaintColor.withAlpha(_fillPaint.color.alpha);
_fillPaint.color = fillPaintColor.withValues(alpha: _fillPaint.color.a);
Color strokePaintColor;
if (_strokeColorCallbackAnimation != null) {
@ -130,7 +130,8 @@ class TextLayer extends BaseLayer {
} else {
strokePaintColor = documentData.strokeColor;
}
_strokePaint.color = strokePaintColor.withAlpha(_strokePaint.color.alpha);
_strokePaint.color =
strokePaintColor.withValues(alpha: _strokePaint.color.a);
var opacity = transform.opacity?.value ?? 100;
var alpha = opacity * 255 / 100 * parentAlpha ~/ 255;
@ -418,7 +419,7 @@ class TextLayer extends BaseLayer {
}
void _drawGlyph(Path path, Paint paint, Canvas canvas) {
if (paint.color.alpha == 0) {
if (paint.color.a == 0) {
return;
}
if (paint.style == PaintingStyle.stroke && paint.strokeWidth == 0) {
@ -440,7 +441,7 @@ class TextLayer extends BaseLayer {
void _drawCharacter(
String character, TextStyle textStyle, Paint paint, Canvas canvas) {
if (paint.color.alpha == 0) {
if (paint.color.a == 0) {
return;
}
if (paint.style == PaintingStyle.stroke && paint.strokeWidth == 0) {

View File

@ -76,7 +76,6 @@ class DropShadowEffectParser {
_radius = AnimatableValueParser.parseFloat(reader, composition);
default:
reader.skipValue();
break;
}
default:
reader.skipName();

View File

@ -162,7 +162,7 @@ class GradientColorParser {
continue;
}
if (i == colorStopPositions.length - 1 && position >= colorStopPosition) {
return colorStopColors[i].withOpacity(opacity);
return colorStopColors[i].withValues(alpha: opacity);
}
// We found the position in which position is between i - 1 and i.
var distanceBetweenColors =
@ -171,9 +171,9 @@ class GradientColorParser {
var percentage = distanceToLowerColor / distanceBetweenColors;
var upperColor = colorStopColors[i];
var lowerColor = colorStopColors[i - 1];
return GammaEvaluator.evaluate(
percentage, upperColor.withOpacity(1), lowerColor.withOpacity(1))
.withOpacity(opacity);
return GammaEvaluator.evaluate(percentage,
lowerColor.withValues(alpha: 1), upperColor.withValues(alpha: 1))
.withValues(alpha: opacity);
}
throw Exception('Unreachable code.');
}
@ -182,7 +182,7 @@ class GradientColorParser {
List<double> opacityStopPositions, List<double> opacityStopOpacities) {
if (opacityStopOpacities.length < 2 ||
position <= opacityStopPositions[0]) {
return color.withOpacity(opacityStopOpacities[0]);
return color.withValues(alpha: opacityStopOpacities[0]);
}
for (var i = 1; i < opacityStopPositions.length; i++) {
var opacityStopPosition = opacityStopPositions[i];
@ -202,7 +202,7 @@ class GradientColorParser {
opacity = lerpDouble(
opacityStopOpacities[i - 1], opacityStopOpacities[i], percentage)!;
}
return color.withOpacity(opacity);
return color.withValues(alpha: opacity);
}
throw Exception('Unreachable code.');
}

View File

@ -1,4 +1,3 @@
import '../animation/keyframe/path_keyframe.dart';
import '../composition.dart';
import '../value/keyframe.dart';
import 'keyframe_parser.dart';
@ -65,9 +64,6 @@ class KeyframesParser {
keyframe.endFrame = nextKeyframe.startFrame;
if (keyframe.endValue == null && nextKeyframe.startValue != null) {
keyframe.endValue = nextKeyframe.startValue;
if (keyframe is PathKeyframe) {
(keyframe as PathKeyframe).createPath();
}
}
}
var lastKeyframe = keyframes[size - 1];

View File

@ -228,9 +228,9 @@ class LayerParser {
case 15:
startFrame = reader.nextDouble();
case 16:
preCompWidth = reader.nextInt();
preCompWidth = reader.nextDouble().toInt();
case 17:
preCompHeight = reader.nextInt();
preCompHeight = reader.nextDouble().toInt();
case 18:
inFrame = reader.nextDouble();
case 19:

View File

@ -33,9 +33,9 @@ class LottieCompositionParser {
while (reader.hasNext()) {
switch (reader.selectName(_names)) {
case 0:
parameters.bounds.width = reader.nextInt();
parameters.bounds.width = reader.nextDouble().toInt();
case 1:
parameters.bounds.height = reader.nextInt();
parameters.bounds.height = reader.nextDouble().toInt();
case 2:
parameters.startFrame = reader.nextDouble();
case 3:
@ -70,7 +70,7 @@ class LottieCompositionParser {
}
}
assert(parameters.startFrame != parameters.endFrame,
'startFrame == endFrame ($parameters.startFrame)');
'startFrame == endFrame (${parameters.startFrame})');
assert(
parameters.frameRate > 0, 'invalid framerate: ${parameters.frameRate}');

View File

@ -66,7 +66,7 @@ class Buffer {
}
/// Reads and discards {@code byteCount} bytes from this source. Throws an
/// {@link java.io.EOFException} if the source is exhausted before the
/// [Exception] if the source is exhausted before the
/// requested bytes can be skipped.
void skip(int byteCount) {
_start += byteCount;

View File

@ -3,6 +3,8 @@ import 'buffer.dart';
import 'json_scope.dart';
import 'json_utf8_reader.dart';
// ignore_for_file: unintended_html_in_doc_comment
/// Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
/// encoded value as a stream of tokens. This stream includes both literal
/// values (strings, numbers, booleans, and nulls) as well as the begin and

View File

@ -857,7 +857,7 @@ class JsonUtf8Reader extends JsonReader {
/// Returns the next character in the stream that is neither whitespace nor a
/// part of a comment. When this returns, the returned character is always at
/// {@code buffer.getByte(0)}.
/// {buffer.getByte(0)}.
int _nextNonWhitespace(bool throwOnEof) {
// This code uses ugly local variables 'p' and 'l' representing the 'pos'
// and 'limit' fields respectively. Using locals rather than fields saves

View File

@ -1,10 +1,12 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
import '../composition.dart';
import '../lottie_image_asset.dart';
import 'load_fonts.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
@ -16,6 +18,7 @@ class AssetLottie extends LottieProvider {
this.package,
super.imageProviderFactory,
super.decoder,
super.backgroundLoading,
});
final String assetName;
@ -37,13 +40,21 @@ class AssetLottie extends LottieProvider {
var data = await chosenBundle.load(keyName);
var composition = await LottieComposition.fromByteData(data,
imageProviderFactory: imageProviderFactory, decoder: decoder);
LottieComposition composition;
if (backgroundLoading) {
composition =
await compute(parseJsonBytes, (data.buffer.asUint8List(), decoder));
} else {
composition =
await LottieComposition.fromByteData(data, decoder: decoder);
}
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(composition, image);
}
await ensureLoadedFonts(composition);
return composition;
});
}
@ -63,7 +74,7 @@ class AssetLottie extends LottieProvider {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AssetLottie &&
other.keyName == keyName &&

View File

@ -1,54 +1 @@
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import '../composition.dart';
import '../lottie_image_asset.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as io;
@immutable
class FileLottie extends LottieProvider {
FileLottie(
this.file, {
super.imageProviderFactory,
super.decoder,
});
final Object /*io.File|html.File*/ file;
@override
Future<LottieComposition> load({BuildContext? context}) {
return sharedLottieCache.putIfAbsent(this, () async {
var bytes = await io.loadFile(file);
var composition =
await LottieComposition.fromBytes(bytes, decoder: decoder);
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(composition, image);
}
return composition;
});
}
Future<ui.Image?> _loadImage(
LottieComposition composition, LottieImageAsset lottieImage) {
var imageProvider = getImageProvider(lottieImage);
imageProvider ??= io.loadImageForFile(file, lottieImage);
return loadImage(composition, lottieImage, imageProvider);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) return false;
return other is FileLottie && other.file == file;
}
@override
int get hashCode => file.hashCode;
@override
String toString() => '$runtimeType(file: ${io.filePath(file)})';
}
export 'file_provider_no_io.dart' if (dart.library.io) 'file_provider_io.dart';

View File

@ -0,0 +1,77 @@
import 'dart:io' as io;
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
import '../composition.dart';
import '../lottie_image_asset.dart';
import 'load_fonts.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
@immutable
class FileLottie extends LottieProvider {
FileLottie(
Object file, {
super.imageProviderFactory,
super.decoder,
super.backgroundLoading,
}) : file = file as io.File,
assert(
!kIsWeb,
'Lottie.file is not supported on Flutter Web. '
'Consider using either Lottie.asset or Lottie.network instead.',
);
final io.File file;
@override
Future<LottieComposition> load({BuildContext? context}) {
return sharedLottieCache.putIfAbsent(this, () async {
LottieComposition composition;
var args = (file, decoder);
if (backgroundLoading) {
composition = await compute(_loadFileAndParse, args);
} else {
composition = await _loadFileAndParse(args);
}
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(composition, image);
}
await ensureLoadedFonts(composition);
return composition;
});
}
Future<ui.Image?> _loadImage(
LottieComposition composition, LottieImageAsset lottieImage) {
var imageProvider = getImageProvider(lottieImage);
var imagePath = p.url
.join(p.dirname(file.path), lottieImage.dirName, lottieImage.fileName);
imageProvider ??= FileImage(io.File(imagePath));
return loadImage(composition, lottieImage, imageProvider);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FileLottie && file.path == other.file.path;
}
@override
int get hashCode => file.hashCode;
@override
String toString() => '$runtimeType(file: ${file.path})';
}
Future<LottieComposition> _loadFileAndParse(
(io.File, LottieDecoder?) args) async {
var bytes = await args.$1.readAsBytes();
return await LottieComposition.fromBytes(bytes, decoder: args.$2);
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import '../composition.dart';
import 'lottie_provider.dart';
@immutable
class FileLottie extends LottieProvider {
FileLottie(
this.file, {
super.imageProviderFactory,
super.decoder,
super.backgroundLoading,
}) : assert(
!kIsWeb,
'Lottie.file is not supported on Flutter Web. '
'Consider using either Lottie.asset or Lottie.network instead.',
);
final Object file;
@override
Future<LottieComposition> load({BuildContext? context}) {
throw UnimplementedError(
'FileLottie provider is not supported on Web platform');
}
}

View File

@ -0,0 +1,11 @@
import 'dart:ui';
import '../composition.dart';
Future<void> ensureLoadedFonts(LottieComposition composition) async {
var fonts = FontToLoad.getAndClear(composition);
if (fonts != null) {
for (var font in fonts) {
await loadFontFromList(font.bytes, fontFamily: font.family);
}
}
}

View File

@ -4,12 +4,18 @@ import '../../lottie.dart';
import 'load_image.dart';
abstract class LottieProvider {
LottieProvider({this.imageProviderFactory, this.decoder});
LottieProvider({
this.imageProviderFactory,
this.decoder,
bool? backgroundLoading,
}) : backgroundLoading = backgroundLoading ?? false;
final LottieImageProviderFactory? imageProviderFactory;
final LottieDecoder? decoder;
final bool backgroundLoading;
ImageProvider? getImageProvider(LottieImageAsset lottieImage) {
var imageProvider = fromDataUri(lottieImage.fileName);
if (imageProvider == null && imageProviderFactory != null) {
@ -21,6 +27,11 @@ abstract class LottieProvider {
Future<LottieComposition> load({BuildContext? context});
}
Future<LottieComposition> parseJsonBytes(
(Uint8List, LottieDecoder?) args) async {
return LottieComposition.fromBytes(args.$1, decoder: args.$2);
}
class LottieCache {
final Map<Object, Future<LottieComposition>> _pending =
<Object, Future<LottieComposition>>{};

View File

@ -4,24 +4,37 @@ import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
import '../composition.dart';
import '../lottie_image_asset.dart';
import 'load_fonts.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
@immutable
class MemoryLottie extends LottieProvider {
MemoryLottie(this.bytes, {super.imageProviderFactory, super.decoder});
MemoryLottie(
this.bytes, {
super.imageProviderFactory,
super.decoder,
super.backgroundLoading,
});
final Uint8List bytes;
@override
Future<LottieComposition> load({BuildContext? context}) {
return sharedLottieCache.putIfAbsent(this, () async {
var composition =
await LottieComposition.fromBytes(bytes, decoder: decoder);
LottieComposition composition;
if (backgroundLoading) {
composition = await compute(parseJsonBytes, (bytes, decoder));
} else {
composition =
await LottieComposition.fromBytes(bytes, decoder: decoder);
}
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(composition, image);
}
await ensureLoadedFonts(composition);
return composition;
});
}
@ -37,7 +50,7 @@ class MemoryLottie extends LottieProvider {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
//TODO(xha): compare bytes content

View File

@ -1,18 +1,27 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import '../composition.dart';
import '../lottie_image_asset.dart';
import 'load_fonts.dart';
import 'load_image.dart';
import 'lottie_provider.dart';
import 'provider_io.dart' if (dart.library.html) 'provider_web.dart' as network;
@immutable
class NetworkLottie extends LottieProvider {
NetworkLottie(this.url,
{this.headers, super.imageProviderFactory, super.decoder});
NetworkLottie(
this.url, {
this.client,
this.headers,
super.imageProviderFactory,
super.decoder,
super.backgroundLoading,
});
final http.Client? client;
final String url;
final Map<String, String>? headers;
@ -20,16 +29,31 @@ class NetworkLottie extends LottieProvider {
Future<LottieComposition> load({BuildContext? context}) {
return sharedLottieCache.putIfAbsent(this, () async {
var resolved = Uri.base.resolve(url);
var bytes = await network.loadHttp(resolved, headers: headers);
var composition =
await LottieComposition.fromBytes(bytes, decoder: decoder);
var client = this.client ?? http.Client();
try {
var bytes = await client.readBytes(resolved, headers: headers);
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(resolved, composition, image);
LottieComposition composition;
if (backgroundLoading) {
composition = await compute(parseJsonBytes, (bytes, decoder));
} else {
composition =
await LottieComposition.fromBytes(bytes, decoder: decoder);
}
for (var image in composition.images.values) {
image.loadedImage ??= await _loadImage(resolved, composition, image);
}
await ensureLoadedFonts(composition);
return composition;
} finally {
if (this.client == null) {
client.close();
}
}
return composition;
});
}
@ -47,7 +71,7 @@ class NetworkLottie extends LottieProvider {
}
@override
bool operator ==(dynamic other) {
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is NetworkLottie &&
other.url == url &&

View File

@ -1,41 +0,0 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart' as p;
import '../lottie_image_asset.dart';
final HttpClient _sharedHttpClient = HttpClient()..autoUncompress = false;
Future<Uint8List> loadHttp(Uri uri, {Map<String, String>? headers}) async {
var request = await _sharedHttpClient.getUrl(uri);
headers?.forEach((String name, String value) {
request.headers.add(name, value);
});
final response = await request.close();
if (response.statusCode != HttpStatus.ok) {
throw Exception('Http error. Status code: ${response.statusCode} for $uri');
}
final bytes = await consolidateHttpClientResponseBytes(response);
if (bytes.lengthInBytes == 0) {
throw Exception('NetworkImage is an empty file: $uri');
}
return bytes;
}
Future<Uint8List> loadFile(Object file) {
return (file as File).readAsBytes();
}
String filePath(Object file) {
return (file as File).path;
}
ImageProvider loadImageForFile(Object file, LottieImageAsset lottieImage) {
var fileIo = file as File;
var imagePath = p.url
.join(p.dirname(fileIo.path), lottieImage.dirName, lottieImage.fileName);
return FileImage(File(imagePath));
}

View File

@ -1,37 +0,0 @@
import 'dart:html';
import 'dart:typed_data';
import 'package:flutter/rendering.dart';
import '../lottie_image_asset.dart';
// ignore_for_file: avoid_web_libraries_in_flutter
Future<Uint8List> loadHttp(Uri uri, {Map<String, String>? headers}) async {
var request = await HttpRequest.request(uri.toString(),
requestHeaders: headers, responseType: 'blob');
return _loadBlob(request.response as Blob);
}
Future<Uint8List> loadFile(Object file) {
return _loadBlob(file as File);
}
Future<Uint8List> _loadBlob(Blob file) async {
var reader = FileReader();
reader.readAsArrayBuffer(file);
await reader.onLoadEnd.first;
if (reader.readyState != FileReader.DONE) {
throw Exception('Error while reading blob');
}
return reader.result! as Uint8List;
}
String filePath(Object file) {
return (file as File).relativePath ?? '';
}
ImageProvider loadImageForFile(Object file, LottieImageAsset lottieImage) {
throw UnimplementedError();
}

View File

@ -5,6 +5,7 @@ import 'frame_rate.dart';
import 'lottie_delegates.dart';
import 'lottie_drawable.dart';
import 'options.dart';
import 'render_cache.dart';
import 'render_lottie.dart';
/// A widget that displays a [LottieDrawable] directly.
@ -24,10 +25,9 @@ class RawLottie extends LeafRenderObjectWidget {
this.fit,
AlignmentGeometry? alignment,
this.filterQuality,
bool? enableRenderCache,
this.renderCache,
}) : progress = progress ?? 0.0,
alignment = alignment ?? Alignment.center,
enableRenderCache = enableRenderCache ?? false;
alignment = alignment ?? Alignment.center;
/// The Lottie composition to display.
final LottieComposition? composition;
@ -80,26 +80,8 @@ class RawLottie extends LeafRenderObjectWidget {
/// relative to text direction.
final AlignmentGeometry alignment;
/// Opt-in a special render mode where the frames of the animation are
/// lazily rendered in offscreen images.
/// Subsequent runs of the animation will be very cheap to render.
///
/// This is useful is the animation is complex and can consume lot of energy
/// from the battery.
/// This is will trade an excessive CPU usage for an increase memory usage.
///
/// The render cache is managed internally and will release the memory once the
/// animation is disposed. The cache is shared between all animations. If 2 `Lottie`
/// widget are rendered at the same size, they will render only once.
///
/// Any change in the configuration of the animation (delegates, frame rate etc...)
/// will clear the cache.
/// Any change in the size will invalidate the cache. The cache use the final size
/// visible on the screen (with all transforms applied).
///
/// In order to not exceed the memory limit of a device, the cache is constrained
/// to maximum 50MB. After that, animations are not cached anymore.
final bool enableRenderCache;
/// {@macro lottie.renderCache}
final RenderCache? renderCache;
final FilterQuality? filterQuality;
@ -117,7 +99,7 @@ class RawLottie extends LeafRenderObjectWidget {
fit: fit,
alignment: alignment,
filterQuality: filterQuality,
enableRenderCache: enableRenderCache,
renderCache: renderCache,
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
);
}
@ -138,7 +120,7 @@ class RawLottie extends LeafRenderObjectWidget {
..height = height
..alignment = alignment
..fit = fit
..enableRenderCache = enableRenderCache
..renderCache = renderCache
..devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
}

View File

@ -1,200 +1,34 @@
import 'dart:async';
import 'dart:ui';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show Colors;
import 'package:flutter/material.dart' show RenderBox;
import '../lottie.dart';
import 'utils.dart';
import 'render_cache/store_drawing.dart';
import 'render_cache/store_raster.dart';
final globalRenderCache = RenderCache();
abstract class RenderCache {
/// The frames stored in the cache are fully rasterized. This is the most efficient
/// to render but will use the most memory.
/// This should only be used for very short and very small animations (final size on the screen).
static const raster = RenderCacheRaster(maxMemory: 50000000);
class RenderCache {
final _onUpdateController = StreamController<void>.broadcast();
final entries = <CacheKey, RenderCacheEntry>{};
final handles = <Object, RenderCacheHandle>{};
/// The frames are stored as [dart:ui.Picture] in the cache.
/// It will will spare the CPU work for each frame. The GPU work will be the same as without cache.
static const drawingCommands = RenderCacheDrawing();
/// The maximum memory this cache will use (default 50MB)
/// It should refuse to create new images if the size is exceeded.
static const int defaultMaxMemory = 50000000;
AnimationCache acquire(Object user);
int maxMemory = defaultMaxMemory;
bool enableDebugBackground = false;
Stream<void> get onUpdate => _onUpdateController.stream;
int get handleCount => handles.length;
int get entryCount => entries.length;
int get imageCount =>
entries.values.map((e) => e.images.length).fold<int>(0, (a, b) => a + b);
int get totalMemory => entries.values
.expand((e) => e.images.values)
.fold(0, (a, b) => a + b.width * b.height);
bool canUseMemory(int newMemory) {
return totalMemory + newMemory <= maxMemory;
}
void clear() {
for (var entry in entries.values) {
entry._clear();
}
_notifyUpdate();
}
void _clearUnused() {
for (var entry in entries.entries.toList()) {
var key = entry.key;
var cache = entry.value;
if (cache.handles.isEmpty) {
cache.dispose();
var found = entries.remove(key);
assert(found == cache);
}
}
}
void _notifyUpdate() {
_onUpdateController.add(null);
}
RenderCacheHandle acquire(Object user) {
return handles[user] ??= RenderCacheHandle(this);
}
void release(Object user) {
var handle = handles.remove(user);
if (handle?._currentEntry case var currentEntry?) {
currentEntry.handles.remove(handle);
_clearUnused();
_notifyUpdate();
}
}
void dispose() {
_onUpdateController.close();
}
void release(Object user);
}
class RenderCacheHandle {
final RenderCache _cache;
RenderCacheEntry? _currentEntry;
RenderCacheHandle(this._cache);
RenderCacheEntry withKey(CacheKey key) {
if (_currentEntry case var currentEntry? when currentEntry.key != key) {
_currentEntry = null;
currentEntry.handles.remove(this);
_cache._clearUnused();
}
var entry = _cache.entries[key] ??= RenderCacheEntry(_cache, key);
entry.handles.add(this);
_currentEntry = entry;
_cache._notifyUpdate();
return entry;
}
}
var _debugId = 0;
class RenderCacheEntry {
final RenderCache _cache;
final CacheKey key;
final handles = <RenderCacheHandle>{};
final images = <double, Image>{};
bool _isDisposed = false;
final _id = _debugId++;
RenderCacheEntry(this._cache, this.key);
ui.Image? imageForProgress(double progress, void Function(Canvas) draw) {
assert(!_isDisposed);
var existing = images[progress];
if (existing != null) {
return existing;
}
var size = key.size;
var newImageSize = size.width.round() * size.height.round();
if (!_cache.canUseMemory(newImageSize)) {
return null;
}
var recorder = PictureRecorder();
var canvas = Canvas(recorder);
if (_cache.enableDebugBackground) {
_drawDebugBackground(canvas, size);
}
draw(canvas);
var picture = recorder.endRecording();
var image = picture.toImageSync(size.width.round(), size.height.round());
images[progress] = image;
picture.dispose();
_cache._notifyUpdate();
return image;
}
void _drawDebugBackground(Canvas canvas, Size size) {
var debugColors = [
Colors.yellow,
Colors.orange,
Colors.red,
Colors.blue,
Colors.green,
Colors.amber
];
var color = debugColors[_id % debugColors.length];
canvas.drawRect(
Offset.zero & size, Paint()..color = color.withOpacity(0.2));
}
void _clear() {
for (var image in images.values) {
image.dispose();
}
images.clear();
}
void dispose() {
_isDisposed = true;
_clear();
}
}
@immutable
class CacheKey {
final LottieComposition composition;
final Size size;
final List<Object?> config;
final int delegates;
CacheKey({
required this.composition,
required this.size,
required this.config,
required this.delegates,
}) : assert(size.width == size.width.toInt() &&
size.height == size.height.toInt());
@override
int get hashCode =>
Object.hash(composition, size, Object.hashAll(config), delegates);
@override
bool operator ==(other) =>
other is CacheKey &&
other.composition == composition &&
other.size == size &&
const ListEquality<Object?>().equals(other.config, config) &&
other.delegates == delegates;
@override
String toString() =>
'CacheKey(${composition.hashCode}, $size, $config, $delegates)';
abstract class AnimationCache {
bool draw(
LottieDrawable drawable,
double progress,
Canvas canvas, {
required Offset destinationPosition,
required Rect destinationRect,
required Rect sourceRect,
required Size sourceSize,
required RenderBox renderBox,
required double devicePixelRatio,
});
}

View File

@ -0,0 +1,36 @@
import 'dart:ui';
import 'package:flutter/foundation.dart';
import '../composition.dart';
import '../utils.dart';
@immutable
class CacheKey {
final LottieComposition composition;
final Size size;
final List<Object?> config;
final int delegates;
CacheKey({
required this.composition,
required this.size,
required this.config,
required this.delegates,
}) : assert(size.width == size.width.toInt() &&
size.height == size.height.toInt());
@override
int get hashCode =>
Object.hash(composition, size, Object.hashAll(config), delegates);
@override
bool operator ==(other) =>
other is CacheKey &&
other.composition == composition &&
other.size == size &&
const ListEquality<Object?>().equals(other.config, config) &&
other.delegates == delegates;
@override
String toString() =>
'CacheKey(${composition.hashCode}, $size, $config, $delegates)';
}

View File

@ -0,0 +1,63 @@
abstract class Store<TEntry extends CacheEntry<TCacheKey>, TCacheKey> {
final entries = <TCacheKey, TEntry>{};
final handles = <Object, Handle<TEntry, TCacheKey>>{};
bool shouldRemove(TEntry entry) => entry.handles.isEmpty;
TEntry createEntry(TCacheKey key);
void _clearUnused() {
for (var entry in entries.entries.toList()) {
var key = entry.key;
var cache = entry.value;
if (shouldRemove(cache)) {
cache.dispose();
var found = entries.remove(key);
assert(found == cache);
}
}
}
Handle<TEntry, TCacheKey> acquire(Object user) {
var handle = handles[user] ??= Handle<TEntry, TCacheKey>(this);
return handle;
}
void release(Object user) {
var handle = handles.remove(user);
if (handle?._currentEntry case var currentEntry?) {
var removed = currentEntry.handles.remove(handle);
assert(removed);
_clearUnused();
}
}
}
class Handle<TEntry extends CacheEntry<TCacheKey>, TCacheKey> {
final Store<TEntry, TCacheKey> _cache;
TEntry? _currentEntry;
Handle(this._cache);
TEntry withKey(TCacheKey key) {
if (_currentEntry case var currentEntry? when currentEntry.key != key) {
_currentEntry = null;
currentEntry.handles.remove(this);
_cache._clearUnused();
}
var entry = _cache.entries[key] ??= _cache.createEntry(key);
entry.handles.add(this);
_currentEntry = entry;
return entry;
}
}
abstract base class CacheEntry<TCacheKey> {
final TCacheKey key;
final handles = <Handle>{};
CacheEntry(this.key);
void dispose();
}

View File

@ -0,0 +1,131 @@
import 'dart:ui';
import 'package:flutter/rendering.dart';
import '../lottie_drawable.dart';
import '../render_cache.dart';
import 'key.dart';
import 'store.dart';
final _stores = Expando<DrawingStore>();
class RenderCacheDrawing implements RenderCache {
const RenderCacheDrawing();
@override
AnimationCache acquire(Object user) {
var handle = store.acquire(user);
return DrawingAnimationCache(handle);
}
@override
void release(Object user) {
store.release(user);
}
DrawingStore get store => _stores[this] ??= DrawingStore();
}
class DrawingAnimationCache extends AnimationCache {
final Handle<DrawingEntry, CacheKey> handle;
DrawingAnimationCache(this.handle);
@override
bool draw(
LottieDrawable drawable,
double progress,
Canvas canvas, {
required Offset destinationPosition,
required Rect destinationRect,
required Rect sourceRect,
required Size sourceSize,
required RenderBox renderBox,
required double devicePixelRatio,
}) {
var key = CacheKey(
composition: drawable.composition,
size: Size.zero,
config: drawable.configHash(),
delegates: drawable.delegatesHash());
var entry = handle.withKey(key);
return entry.draw(
drawable,
progress,
canvas,
destinationPosition: destinationPosition,
destinationRect: destinationRect,
sourceRect: sourceRect,
sourceSize: sourceSize,
);
}
}
class DrawingStore extends Store<DrawingEntry, CacheKey> {
@override
DrawingEntry createEntry(CacheKey key) {
return DrawingEntry(this, key);
}
}
base class DrawingEntry extends CacheEntry<CacheKey> {
final DrawingStore store;
final pictures = <double, Picture>{};
DrawingEntry(this.store, super.key);
Picture _record(void Function(Canvas) draw) {
var recorder = PictureRecorder();
var canvas = Canvas(recorder);
draw(canvas);
return recorder.endRecording();
}
Picture? _pictureForProgress(double progress, void Function(Canvas) draw) {
var existing = pictures[progress];
if (existing != null) {
return existing;
}
var picture = _record(draw);
pictures[progress] = picture;
return picture;
}
final _matrix = Matrix4.identity();
bool draw(
LottieDrawable drawable,
double progress,
Canvas canvas, {
required Offset destinationPosition,
required Rect destinationRect,
required Rect sourceRect,
required Size sourceSize,
}) {
var cachedImage = _pictureForProgress(progress, (cacheCanvas) {
drawable.compositionLayer.draw(cacheCanvas, _matrix, parentAlpha: 255);
});
if (cachedImage != null) {
var destinationSize = destinationRect.size;
canvas.save();
canvas.translate(destinationRect.left, destinationRect.top);
canvas.scale(destinationSize.width / sourceRect.width,
destinationSize.height / sourceRect.height);
canvas.drawPicture(cachedImage);
canvas.restore();
return true;
}
return false;
}
@override
void dispose() {
for (var picture in pictures.values) {
picture.dispose();
}
pictures.clear();
}
}

View File

@ -0,0 +1,171 @@
import 'dart:ui';
import 'package:flutter/rendering.dart';
import '../lottie_drawable.dart';
import '../render_cache.dart';
import 'key.dart';
import 'store.dart';
final _stores = Expando<RasterStore>();
class RenderCacheRaster implements RenderCache {
final int maxMemory;
const RenderCacheRaster({required this.maxMemory});
@override
AnimationCache acquire(Object user) {
var handle = store.acquire(user);
return RasterAnimationCache(handle);
}
@override
void release(Object user) {
store.release(user);
}
RasterStore get store => _stores[this] ??= RasterStore(maxMemory);
}
class RasterAnimationCache extends AnimationCache {
final Handle<RasterEntry, CacheKey> handle;
RasterAnimationCache(this.handle);
@override
bool draw(
LottieDrawable drawable,
double progress,
Canvas canvas, {
required Offset destinationPosition,
required Rect destinationRect,
required Rect sourceRect,
required Size sourceSize,
required RenderBox renderBox,
required double devicePixelRatio,
}) {
var rect = Rect.fromPoints(renderBox.localToGlobal(destinationPosition),
renderBox.localToGlobal(destinationRect.bottomRight));
var cacheImageSize = Size(
(rect.size.width * devicePixelRatio).roundToDouble(),
(rect.size.height * devicePixelRatio).roundToDouble());
var key = CacheKey(
composition: drawable.composition,
size: cacheImageSize,
config: drawable.configHash(),
delegates: drawable.delegatesHash());
var entry = handle.withKey(key);
return entry.draw(
drawable,
progress,
canvas,
destinationPosition: destinationPosition,
destinationRect: destinationRect,
sourceSize: sourceSize,
sourceRect: sourceRect,
);
}
}
class RasterStore extends Store<RasterEntry, CacheKey> {
final int maxMemory;
RasterStore(this.maxMemory);
int get totalMemory => entries.values.fold(0, (a, b) => a + b.currentMemory);
int get imageCount => entries.values.expand((e) => e.images.values).length;
void clear() {
for (var entry in entries.values) {
entry.clear();
}
}
@override
RasterEntry createEntry(CacheKey key) {
return RasterEntry(this, key);
}
bool canUseMemory(int newMemory) {
return totalMemory + newMemory <= maxMemory;
}
}
base class RasterEntry extends CacheEntry<CacheKey> {
final RasterStore store;
final images = <double, Image>{};
int currentMemory = 0;
RasterEntry(this.store, super.key);
Picture _record(void Function(Canvas) draw) {
var recorder = PictureRecorder();
var canvas = Canvas(recorder);
draw(canvas);
return recorder.endRecording();
}
Image? imageForProgress(double progress, void Function(Canvas) draw) {
var existing = images[progress];
if (existing != null) {
return existing;
}
var size = key.size;
var newImageSize = size.width.round() * size.height.round();
if (!store.canUseMemory(newImageSize)) {
return null;
}
var picture = _record(draw);
var image = picture.toImageSync(size.width.round(), size.height.round());
picture.dispose();
images[progress] = image;
currentMemory += size.width.round() * size.height.round();
return image;
}
final _normalPaint = Paint();
final _matrix = Matrix4.identity();
bool draw(
LottieDrawable drawable,
double progress,
Canvas canvas, {
required Offset destinationPosition,
required Rect destinationRect,
required Size sourceSize,
required Rect sourceRect,
}) {
var cacheImageSize = key.size;
var cachedImage = imageForProgress(progress, (cacheCanvas) {
_matrix.setIdentity();
_matrix.scale(cacheImageSize.width / sourceSize.width,
cacheImageSize.height / sourceSize.height);
drawable.compositionLayer.draw(cacheCanvas, _matrix, parentAlpha: 255);
});
if (cachedImage != null) {
canvas.drawImageRect(cachedImage, Offset.zero & cacheImageSize,
destinationRect, _normalPaint);
return true;
}
return false;
}
void clear() {
for (var image in images.values) {
image.dispose();
}
images.clear();
currentMemory = 0;
}
@override
void dispose() {
clear();
}
}

View File

@ -22,13 +22,13 @@ class RenderLottie extends RenderBox {
BoxFit? fit,
AlignmentGeometry alignment = Alignment.center,
FilterQuality? filterQuality,
bool enableRenderCache = false,
RenderCache? renderCache,
required double devicePixelRatio,
}) : assert(progress >= 0.0 && progress <= 1.0),
assert(
!enableRenderCache || frameRate != FrameRate.max,
'FrameRate.max cannot be used with enableRenderCache. '
'You should use a specific frame rate. e.g. FrameRate(60)'),
renderCache == null || frameRate != FrameRate.max,
'FrameRate.max cannot be used with a RenderCache '
'Use a specific frame rate. e.g. FrameRate(60)'),
_drawable = composition != null
? (LottieDrawable(composition, frameRate: frameRate)
..setProgress(progress)
@ -42,7 +42,7 @@ class RenderLottie extends RenderBox {
_height = height,
_fit = fit,
_alignment = alignment,
_enableRenderCache = enableRenderCache,
_renderCache = renderCache,
_devicePixelRatio = devicePixelRatio;
/// The lottie composition to display.
@ -161,13 +161,14 @@ class RenderLottie extends RenderBox {
markNeedsPaint();
}
bool get enableRenderCache => _enableRenderCache;
bool _enableRenderCache;
set enableRenderCache(bool value) {
if (value == _enableRenderCache) {
RenderCache? get renderCache => _renderCache;
RenderCache? _renderCache;
set renderCache(RenderCache? value) {
if (value == _renderCache) {
return;
}
_enableRenderCache = value;
_renderCache?.release(this);
_renderCache = value;
markNeedsPaint();
}
@ -256,14 +257,12 @@ class RenderLottie extends RenderBox {
if (_drawable == null) return;
RenderCacheContext? cacheContext;
if (enableRenderCache) {
if (_renderCache case var renderCache?) {
cacheContext = RenderCacheContext(
handle: globalRenderCache.acquire(this),
cache: renderCache.acquire(this),
devicePixelRatio: _devicePixelRatio,
localToGlobal: localToGlobal,
renderBox: this,
);
} else {
globalRenderCache.release(this);
}
_drawable!.draw(
@ -290,7 +289,7 @@ class RenderLottie extends RenderBox {
@override
void dispose() {
globalRenderCache.release(this);
_renderCache?.release(this);
super.dispose();
}
}

View File

@ -1,6 +1,5 @@
import 'dart:math';
import 'dart:ui';
import 'path_factory.dart';
Path dashPath(
Path source, {
@ -10,7 +9,7 @@ Path dashPath(
assert(intervals.length >= 2);
phase ??= 0;
var dest = PathFactory.create();
var dest = Path();
for (final metric in source.computeMetrics()) {
for (var dash in _dashes(metric.length, intervals, phase)) {
dest.addPath(metric.extractPath(dash.left, dash.right), Offset.zero);

View File

@ -26,18 +26,25 @@ class GammaEvaluator {
}
static Color evaluate(double fraction, Color startColor, Color endColor) {
// Fast return in case start and end is the same
// or if fraction is at start/end or out of [0,1] bounds
if (startColor == endColor) {
return startColor;
} else if (fraction <= 0) {
return startColor;
} else if (fraction >= 1) {
return endColor;
}
var startA = startColor.alpha / 255.0;
var startR = startColor.red / 255.0;
var startG = startColor.green / 255.0;
var startB = startColor.blue / 255.0;
var endA = endColor.alpha / 255.0;
var endR = endColor.red / 255.0;
var endG = endColor.green / 255.0;
var endB = endColor.blue / 255.0;
var startA = startColor.a;
var startR = startColor.r;
var startG = startColor.g;
var startB = startColor.b;
var endA = endColor.a;
var endR = endColor.r;
var endG = endColor.g;
var endB = endColor.b;
// convert from sRGB to linear
startR = _eocfSRgb(startR);

View File

@ -96,7 +96,7 @@ class MiscUtils {
/// it to the accumulator list.
///
/// Any {@link KeyPathElementContent} should call through to this as its implementation of
/// {@link KeyPathElementContent#resolveKeyPath(KeyPath, int, List, KeyPath)}.
/// {KeyPathElementContent#resolveKeyPath(KeyPath, int, List, KeyPath)}.
static void resolveKeyPath(
KeyPath keyPath,
int depth,

View File

@ -1,7 +0,0 @@
import 'dart:ui';
class PathFactory {
static Path create() {
return Path();
}
}

View File

@ -1,17 +1,33 @@
import 'dart:ui';
import 'package:flutter/animation.dart';
import 'path_factory.dart';
// ignore: must_be_immutable
class PathInterpolator extends Curve {
/// Governs the accuracy of the approximation of the {@link Path}.
static const double _precision = 0.002;
final List<double> _mX;
final List<double> _mY;
late final List<double> _mX;
late final List<double> _mY;
bool _isInitialized = false;
const PathInterpolator._(this._mX, this._mY);
final double controlX1, controlY1, controlX2, controlY2;
PathInterpolator.cubic(
this.controlX1, this.controlY1, this.controlX2, this.controlY2);
void _ensureInitialized() {
if (_isInitialized) {
return;
}
_initialize();
_isInitialized = true;
}
void _initialize() {
final path = Path();
path.moveTo(0.0, 0.0);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0, 1.0);
factory PathInterpolator(Path path) {
final pathMeasure = path.computeMetrics().toList().first;
final pathLength = pathMeasure.length;
@ -27,18 +43,13 @@ class PathInterpolator extends Curve {
mX[i] = tangent.position.dx;
mY[i] = tangent.position.dy;
}
return PathInterpolator._(mX, mY);
}
factory PathInterpolator.cubic(
double controlX1, double controlY1, double controlX2, double controlY2) {
return PathInterpolator(
_createCubic(controlX1, controlY1, controlX2, controlY2));
_mX = mX;
_mY = mY;
}
@override
double transform(double t) {
_ensureInitialized();
if (t <= 0.0) {
return 0.0;
} else if (t >= 1.0) {
@ -70,12 +81,4 @@ class PathInterpolator extends Curve {
return startY + (fraction * (endY - startY));
}
static Path _createCubic(
double controlX1, double controlY1, double controlX2, double controlY2) {
final path = PathFactory.create();
path.moveTo(0.0, 0.0);
path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1.0, 1.0);
return path;
}
}

View File

@ -4,12 +4,11 @@ import '../animation/content/trim_path_content.dart';
import '../l.dart';
import '../utils.dart';
import 'misc.dart';
import 'path_factory.dart';
class Utils {
static Path createPath(
Offset startPoint, Offset endPoint, Offset? cp1, Offset? cp2) {
var path = PathFactory.create();
var path = Path();
path.moveTo(startPoint.dx, startPoint.dy);
if (cp1 != null &&

View File

@ -331,7 +331,7 @@ class ResolvedValueDelegate<T> {
/// to multiple contents. In that case, the callbacks's value will apply to all of them.
/// <p>
/// Internally, this will check if the {@link KeyPath} has already been resolved with
/// {@link #resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
/// {#resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
void addValueCallback(LottieDrawable drawable) {
var invalidate = false;
if (valueDelegate.keyPath.isEmpty) {

View File

@ -5,34 +5,39 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
sha256: "88399e291da5f7e889359681a8f64b18c5123e03576b01f32a6a276611e511c3"
url: "https://pub.dev"
source: hosted
version: "64.0.0"
version: "78.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: "direct dev"
description:
name: analyzer
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
sha256: "62899ef43d0b962b056ed2ebac6b47ec76ffd003d5f7c4e4dc870afe63188e33"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
version: "7.1.0"
archive:
dependency: "direct main"
description:
name: archive
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
url: "https://pub.dev"
source: hosted
version: "3.4.9"
version: "4.0.2"
args:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.6.0"
async:
dependency: transitive
description:
@ -69,34 +74,34 @@ packages:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.1"
version: "3.1.2"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.6"
dart_style:
dependency: "direct dev"
description:
name: dart_style
sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
version: "3.0.1"
fake_async:
dependency: transitive
description:
@ -105,14 +110,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.0"
version: "7.0.1"
flutter:
dependency: "direct main"
description: flutter
@ -122,10 +135,10 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -139,83 +152,123 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
js:
dependency: transitive
http:
dependency: "direct main"
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "0.6.7"
version: "1.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
url: "https://pub.dev"
source: hosted
version: "10.0.7"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "5.1.1"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.15.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
path:
dependency: "direct main"
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
pointycastle:
version: "1.9.0"
posix:
dependency: transitive
description:
name: pointycastle
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
name: posix
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
url: "https://pub.dev"
source: hosted
version: "3.7.3"
version: "6.0.1"
pub_semver:
dependency: transitive
dependency: "direct dev"
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
version: "0.0.0"
source_span:
dependency: transitive
description:
@ -228,10 +281,10 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.12.0"
stream_channel:
dependency: transitive
description:
@ -244,10 +297,10 @@ packages:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.0"
term_glyph:
dependency: transitive
description:
@ -260,18 +313,18 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.3"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.4.0"
vector_math:
dependency: "direct main"
description:
@ -280,30 +333,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
url: "https://pub.dev"
source: hosted
version: "14.3.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "1.1.0"
yaml:
dependency: "direct dev"
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "3.1.3"
sdks:
dart: ">=3.2.0 <4.0.0"
flutter: ">=3.16.0"
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

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