├── .gitignore ├── .idea ├── libraries │ ├── Dart_SDK.xml │ └── Flutter_for_Android.xml ├── modules.xml ├── runConfigurations │ └── example_lib_main_dart.xml └── workspace.xml ├── .metadata ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh.md ├── analysis_options.yaml ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── opensource │ │ │ │ │ └── svgaplayer_flutter_example │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── angel.svga │ └── pin_jump.svga ├── ios │ ├── Flutter │ │ ├── .last_build_id │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ └── index.html ├── lib ├── dynamic_entity.dart ├── painter.dart ├── parser.dart ├── player.dart ├── proto │ ├── svga.pb.dart │ ├── svga.pbenum.dart │ ├── svga.pbjson.dart │ └── svga.pbserver.dart ├── simple_player.dart └── svgaplayer_flutter.dart ├── package.json ├── pubspec.lock ├── pubspec.yaml ├── svgaplayer_flutter.iml └── test └── svgaplayer_flutter_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations/example_lib_main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart", 11 | "program": "example/lib/main.dart" 12 | }, 13 | { 14 | "name": "Flutter profile", 15 | "request": "launch", 16 | "type": "dart", 17 | "program": "example/lib/main.dart", 18 | "args": ["--profile"] 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### 2.2.0 (2022-11-24) 2 | 3 | * Update path-drawing version. 4 | 5 | #### 2.1.2 (2021-06-26) 6 | 7 | * Fix KEEP path parser issue. 8 | 9 | #### 2.1.1 (2021-06-15) 10 | 11 | * Fix path parse error. 12 | 13 | #### 2.1.0 (2021-06-11) 14 | 15 | * Add Flutter Web support. 16 | * Add dispose method to MovieEntity. 17 | * Add IgnorePointer to CustomPaint. 18 | * Fix path parse error. 19 | 20 | #### 2.0.0 (2021-05-29) 21 | 22 | * Release 2.0.0. 23 | 24 | #### 2.0.0-nullsafety.0 (2021-03-31) 25 | 26 | * Add null safety support. 27 | * Upgrade all dependencies. 28 | 29 | #### 1.2.0 (2021-03-02) 30 | 31 | * Remove native plugin. 32 | * Improve decoding images speed. 33 | 34 | #### 1.1.0 (2020-03-19) 35 | 36 | * 修复 bitmap 锯齿问题和 dynamicItem frame 问题 37 | * fix: Keep type shape error parsed. 38 | 39 | #### 1.0.1 (2019-11-27) 40 | 41 | * Fix an issue about setText. 42 | 43 | #### 1.0.0 (2019-11-19) 44 | 45 | * Use protobuf `^1.0.0` 46 | 47 | #### 0.1.1 (2019-09-12) 48 | 49 | ##### Chores 50 | 51 | * Lock protobuf version < 0.13.16, because 0.13.16 leads build fail. 52 | 53 | #### 0.1.0 (2019-07-26) 54 | 55 | ##### Feat 56 | 57 | * Add file option to SVGASimpleImage. 58 | 59 | #### 0.0.4 (2019-07-01) 60 | 61 | ##### Bug Fixes 62 | 63 | * Should not draw as groups for bitmap or shape, should draw as a sprite one by one. 64 | 65 | #### 0.0.3 (2019-07-01) 66 | 67 | ##### Chores 68 | 69 | * upgrade to 0.0.3 (3c40d8bf) 70 | * 0.0.2 released. (7a54b493) 71 | 72 | ##### Bug Fixes 73 | 74 | * https://github.com/yyued/SVGAPlayer-Flutter/issues/2 (3e8db072) 75 | 76 | #### 0.0.1 (2019-04-01) 77 | 78 | ##### New Features 79 | 80 | * Add zh readme. (ed4ceaf8) 81 | * Add assets to SVGASimpleImage. (34ffe14d) 82 | * Add decodeFromAssets method to parser. (e2746e62) 83 | * Add dynamicText dynamicHidden and dynamicDrawer. (0a0f7201) 84 | * Add SVGASimpleImage. (f6add286) 85 | * Add static url method to create Widget. (516d356b) 86 | * Add callbacks. (4fce439d) 87 | * Add path cache. fix: Miss shape transform. (f8104fb7) 88 | * Add shape render. (0c75cc32) 89 | * Add Parser, Player and Bitmap render. (99a1be72) 90 | 91 | ##### Other Changes 92 | 93 | * Update samples and doc. (a5d78c51) 94 | 95 | ##### Refactors 96 | 97 | * use AnimationController base animation. (ac210165) 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This repo developed by PonyCui, and PonyCui has the full permission to fix LICENSE. 2 | 3 | The "Anti 996" was added at 2019.07.24, if you use the commit before(include) a5bc735067196caa422257ebbf4218a3c5989b3b, you may not obey "Anti 996" License. 4 | 5 | Copyright (c) <2019> 6 | 7 | "Anti 996" License Version 1.0 (Draft) 8 | 9 | Permission is hereby granted to any individual or legal entity 10 | obtaining a copy of this licensed work (including the source code, 11 | documentation and/or related items, hereinafter collectively referred 12 | to as the "licensed work"), free of charge, to deal with the licensed 13 | work for any purpose, including without limitation, the rights to use, 14 | reproduce, modify, prepare derivative works of, distribute, publish 15 | and sublicense the licensed work, subject to the following conditions: 16 | 17 | 1. The individual or the legal entity must conspicuously display, 18 | without modification, this License and the notice on each redistributed 19 | or derivative copy of the Licensed Work. 20 | 21 | 2. The individual or the legal entity must strictly comply with all 22 | applicable laws, regulations, rules and standards of the jurisdiction 23 | relating to labor and employment where the individual is physically 24 | located or where the individual was born or naturalized; or where the 25 | legal entity is registered or is operating (whichever is stricter). In 26 | case that the jurisdiction has no such laws, regulations, rules and 27 | standards or its laws, regulations, rules and standards are 28 | unenforceable, the individual or the legal entity are required to 29 | comply with Core International Labor Standards. 30 | 31 | 3. The individual or the legal entity shall not induce, suggest or force 32 | its employee(s), whether full-time or part-time, or its independent 33 | contractor(s), in any methods, to agree in oral or written form, to 34 | directly or indirectly restrict, weaken or relinquish his or her 35 | rights or remedies under such laws, regulations, rules and standards 36 | relating to labor and employment as mentioned above, no matter whether 37 | such written or oral agreements are enforceable under the laws of the 38 | said jurisdiction, nor shall such individual or the legal entity 39 | limit, in any methods, the rights of its employee(s) or independent 40 | contractor(s) from reporting or complaining to the copyright holder or 41 | relevant authorities monitoring the compliance of the license about 42 | its violation(s) of the said license. 43 | 44 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 47 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 48 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 49 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 50 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archived 2 | 本仓库已经停止维护,你仍然继续阅读源码及创建分叉,但本仓库不会继续更新,也不会回答任何 issue。 3 | 4 | This repo has stopped maintenance, you can still continue to read the source code and create forks, but this repo will not continue to be updated, nor will it answer any issues. 5 | 6 | # SVGAPlayer-Flutter 7 | 8 | [简体中文](./README.zh.md) 9 | 10 | ## 支持本项目 11 | 12 | 如果 SVGA-Flutter 为您提供了便利与帮助,诚恳建议您通过以下方式支持作者、贡献者持续为该项目发电。 13 | 14 | 1. 轻点 GitHub Star,让更多人看到该项目。 15 | 2. 通过赞赏码(页面底部可见)的方式鼓励作者继续维护代码。 16 | 17 | 关注作者另外一个开源项目,[MPFlutter](https://mpflutter.com/),使用 Flutter 开发微信小程序。 18 | 19 | ## Introduce 20 | 21 | SVGAPlayer is a light-weight animation renderer. You use [tools](https://svga.io/designer.html) to export `svga` file from `Adobe Animate CC` or `Adobe After Effects`, and then use SVGAPlayer to render animation on mobile application. 22 | 23 | `SVGAPlayer-Flutter` render animation natively via Flutter CustomPainter, brings you a high-performance, low-cost animation experience. 24 | 25 | If wonder more information, go to this [website](https://svga.io/). 26 | 27 | * SVGAPlayer-Flutter supports 2.0 format only. 28 | 29 | ## Usage 30 | 31 | Here introduce `SVGAPlayer-Flutter` usage. Wonder exporting usage? Click [here](https://svga.io/designer.html). 32 | 33 | ### Add dependency 34 | 35 | ``` 36 | dependencies: 37 | svgaplayer_flutter: ^2.0.0 #latest version 38 | ``` 39 | 40 | ### Locate files 41 | 42 | SVGAPlayer could load svga file from Flutter local `assets` directory or remote server. Add file to pubspec.yaml by yourself. 43 | 44 | ### Super simple to use 45 | 46 | The simplest way to render an animation is to use `SVGASimpleImage` component. 47 | 48 | ```dart 49 | class MyWidget extends Widget { 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return Container( 54 | child: SVGASimpleImage( 55 | resUrl: "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"), 56 | ); 57 | } 58 | 59 | } 60 | ``` 61 | 62 | Animation will run repeatedly. If you wondering a stronger animation controls, use code. 63 | 64 | ### Code to use 65 | 66 | To control an animation rendering, you need to create a `SVGAAnimationController` instance just like Flutter regular animation. Assign to `SVGAImage`, load and decode resource using `SVGAParser`, and then do things as you want with `SVGAAnimationController`. 67 | 68 | ```dart 69 | import 'package:flutter/material.dart'; 70 | import 'package:svgaplayer_flutter/svgaplayer_flutter.dart'; 71 | 72 | void main() => runApp(MyApp()); 73 | 74 | class MyApp extends StatefulWidget { 75 | @override 76 | _MyAppState createState() => _MyAppState(); 77 | } 78 | 79 | class _MyAppState extends State with SingleTickerProviderStateMixin { 80 | SVGAAnimationController animationController; 81 | 82 | @override 83 | void initState() { 84 | this.animationController = SVGAAnimationController(vsync: this); 85 | this.loadAnimation(); 86 | super.initState(); 87 | } 88 | 89 | @override 90 | void dispose() { 91 | this.animationController.dispose(); 92 | super.dispose(); 93 | } 94 | 95 | void loadAnimation() async { 96 | final videoItem = await SVGAParser.shared.decodeFromURL( 97 | "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"); 98 | this.animationController.videoItem = videoItem; 99 | this 100 | .animationController 101 | .repeat() // Try to use .forward() .reverse() 102 | .whenComplete(() => this.animationController.videoItem = null); 103 | } 104 | 105 | @override 106 | Widget build(BuildContext context) { 107 | return Container( 108 | child: SVGAImage(this.animationController), 109 | ); 110 | } 111 | } 112 | ``` 113 | 114 | ### Reuse MovieEntity 115 | 116 | The `MovieEntity` will `dispose` after `AnimationController` dispose or `AnimationController::videoItem` reset. 117 | 118 | After dispose, the `MovieEntity` can not reused. 119 | 120 | If you eager to reuse the `MovieEntity`, assign `MovieEntity::autorelease` to false. 121 | 122 | ```dart 123 | final videoItem = await SVGAParser.shared.decodeFromURL( 124 | "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"); 125 | videoItem.autorelease = false; 126 | ``` 127 | 128 | You need to call `MovieEntity::dispose()` method when no need to use. 129 | 130 | ### Cache 131 | 132 | We will not manage any caches, you need to use `dio` or other libraries to handle resource caches. 133 | 134 | Use `SVGAParser.decodeFromBytes` method to decode caching data. 135 | 136 | ## Features 137 | 138 | Here are many feature samples. 139 | 140 | * [Replace an element with Bitmap.](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Image) 141 | * [Add text above an element.](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Text) 142 | * [Hides an element dynamicaly.](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Hidden) 143 | * [Use a custom drawer for element.](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Drawer) 144 | 145 | ## License 146 | 147 | [![996.ICU](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 148 | 149 | [Anti 996](./LICENSE) 150 | 151 | 152 | ## 感谢或联系作者 153 | 154 | ![](https://cdn.jsdelivr.net/gh/PonyCui/ponycui.github.io@master/contact.png) 155 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 | # SVGAPlayer-Flutter 2 | 3 | ## 介绍 4 | 5 | `SVGAPlayer` 是一个轻量的动画渲染库。你可以使用[工具](https://svga.io/designer.html)从 `Adobe Animate CC` 或者 `Adobe After Effects` 中导出动画文件,然后使用 `SVGAPlayer` 在移动设备上渲染并播放。 6 | 7 | `SVGAPlayer-Flutter` 通过 Flutter CustomPainter 原生渲染动画,为您带来高性能,低成本的动画体验。 8 | 9 | 如果你想要了解更多细节,请访问[官方网站](https://svga.io/)。 10 | 11 | * SVGAPlayer-Flutter 只支持 2.0 版本格式. 12 | 13 | ## 用法 14 | 15 | 我们在这里介绍 `SVGAPlayer-Flutter` 的用法。想要知道如何导出动画,点击[这里](https://svga.io/designer.html)。 16 | 17 | ### 添加依赖 18 | 19 | ``` 20 | dependencies: 21 | svgaplayer_flutter: ^2.0.0 #latest version 22 | ``` 23 | 24 | ### 放置 svga 文件 25 | 26 | SVGAPlayer 可以从本地 `assets` 目录,或者远端服务器上加载动画文件。 27 | 28 | ### 简易用法 29 | 30 | 使用 `SVGASimpleImage` 组件进行动画渲染是最简单的。 31 | 32 | ```dart 33 | class MyWidget extends Widget { 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Container( 38 | child: SVGASimpleImage( 39 | resUrl: "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"), 40 | ); 41 | } 42 | 43 | } 44 | ``` 45 | 46 | 动画将会循环播放,如果你希望更直接地控制动画,可以使用代码方式。 47 | 48 | ### 使用代码 49 | 50 | 为了控制动画的渲染操作,你需要创建一个 `SVGAAnimationController` 实例,这和普通的 Flutter 动画并没有什么区别。将这个实例赋予 `SVGAImage`,使用 `SVGAParser` 加载并解码资源,然后使用 Controller 播放动画。 51 | 52 | ```dart 53 | import 'package:flutter/material.dart'; 54 | import 'package:svgaplayer_flutter/svgaplayer_flutter.dart'; 55 | 56 | void main() => runApp(MyApp()); 57 | 58 | class MyApp extends StatefulWidget { 59 | @override 60 | _MyAppState createState() => _MyAppState(); 61 | } 62 | 63 | class _MyAppState extends State with SingleTickerProviderStateMixin { 64 | SVGAAnimationController animationController; 65 | 66 | @override 67 | void initState() { 68 | this.animationController = SVGAAnimationController(vsync: this); 69 | this.loadAnimation(); 70 | super.initState(); 71 | } 72 | 73 | @override 74 | void dispose() { 75 | this.animationController.dispose(); 76 | super.dispose(); 77 | } 78 | 79 | void loadAnimation() async { 80 | final videoItem = await SVGAParser.shared.decodeFromURL( 81 | "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"); 82 | this.animationController.videoItem = videoItem; 83 | this 84 | .animationController 85 | .repeat() // Try to use .forward() .reverse() 86 | .whenComplete(() => this.animationController.videoItem = null); 87 | } 88 | 89 | @override 90 | Widget build(BuildContext context) { 91 | return Container( 92 | child: SVGAImage(this.animationController), 93 | ); 94 | } 95 | } 96 | ``` 97 | 98 | ### 复用 MovieEntity 99 | 100 | `MovieEntity` 对象会在 `AnimationController` dispose 调用后,或 `AnimationController::videoItem` 赋值后,执行 `dispose` 操作。 101 | 102 | 在 dispose 以后 `MovieEntity` 不可复用。 103 | 104 | 如果你需要复用 `MovieEntity` 对象,赋值 `MovieEntity::autorelease` 为 `false` 即可。 105 | 106 | ```dart 107 | final videoItem = await SVGAParser.shared.decodeFromURL( 108 | "https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true"); 109 | videoItem.autorelease = false; 110 | ``` 111 | 112 | 当不再需要使用资源时,你需要自行调用 `MovieEntity::dispose()` 方法。 113 | 114 | ### 缓存 115 | 116 | 动画库不会管理任何缓存,你需要使用 `dio` 等网络库自行处理。 117 | 118 | 使用 `SVGAParser.decodeFromBytes` 方法解码数据。 119 | 120 | ## 功能示例 121 | 122 | * [使用位图替换指定元素。](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Image) 123 | * [在指定元素上绘制文本。](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Text) 124 | * [隐藏指定元素。](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Hidden) 125 | * [在指定元素上自由绘制。](https://github.com/yyued/SVGAPlayer-Flutter/wiki/Dynamic-Drawer) 126 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 71 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # svgaplayer_flutter_example 2 | 3 | Demonstrates how to use the svgaplayer_flutter plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 31 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.opensource.svgaplayer_flutter_example" 37 | minSdkVersion 16 38 | targetSdkVersion 31 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 12 | 20 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/opensource/svgaplayer_flutter_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.opensource.svgaplayer_flutter_example; 2 | 3 | import android.os.Bundle; 4 | 5 | import io.flutter.embedding.android.FlutterActivity; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | } 9 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:7.0.4' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/assets/angel.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/assets/angel.svga -------------------------------------------------------------------------------- /example/assets/pin_jump.svga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/assets/pin_jump.svga -------------------------------------------------------------------------------- /example/ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 4d08af9c26259ca52b9e745a40cb7390 -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'Flutter' 7 | s.version = '1.0.0' 8 | s.summary = 'High-performance, high-fidelity mobile apps.' 9 | s.description = <<-DESC 10 | Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. 11 | DESC 12 | s.homepage = 'https://flutter.io' 13 | s.license = { :type => 'MIT' } 14 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 15 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 16 | s.ios.deployment_target = '8.0' 17 | s.vendored_frameworks = 'Flutter.framework' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/saiakirahui/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/saiakirahui/Desktop/SVGAPlayer-Flutter/example" 5 | export "FLUTTER_TARGET=/Users/saiakirahui/Desktop/SVGAPlayer-Flutter/example/lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1" 10 | export "DART_DEFINES=flutter.inspector.structuredErrors%3Dtrue" 11 | export "DART_OBFUSCATION=false" 12 | export "TRACK_WIDGET_CREATION=true" 13 | export "TREE_SHAKE_ICONS=false" 14 | export "PACKAGE_CONFIG=/Users/saiakirahui/Desktop/SVGAPlayer-Flutter/example/.dart_tool/package_config.json" 15 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 2 | 3 | COCOAPODS: 1.9.1 4 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 1F157454912ACB0D29C33DA1 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C8BFA4B17CCA488F6519D4 /* libPods-Runner.a */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 14 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 15 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 16 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 17 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 18 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = ""; 26 | dstSubfolderSpec = 10; 27 | files = ( 28 | ); 29 | name = "Embed Frameworks"; 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXCopyFilesBuildPhase section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 39 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 40 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 41 | 805BF4801CDCB4C9520F8A1A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 42 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 43 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 44 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | D0C8BFA4B17CCA488F6519D4 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E95B10158C67CBE172DE6F06 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | F49FA3532CD7399F1325CA36 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 1F157454912ACB0D29C33DA1 /* libPods-Runner.a in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 1DFC42C9B7529C93C9E92E7D /* Pods */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | F49FA3532CD7399F1325CA36 /* Pods-Runner.debug.xcconfig */, 71 | 805BF4801CDCB4C9520F8A1A /* Pods-Runner.release.xcconfig */, 72 | E95B10158C67CBE172DE6F06 /* Pods-Runner.profile.xcconfig */, 73 | ); 74 | name = Pods; 75 | sourceTree = ""; 76 | }; 77 | 6292D635F241CF4B91368F3C /* Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | D0C8BFA4B17CCA488F6519D4 /* libPods-Runner.a */, 81 | ); 82 | name = Frameworks; 83 | sourceTree = ""; 84 | }; 85 | 9740EEB11CF90186004384FC /* Flutter */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 89 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 90 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 91 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 92 | ); 93 | name = Flutter; 94 | sourceTree = ""; 95 | }; 96 | 97C146E51CF9000F007C117D = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9740EEB11CF90186004384FC /* Flutter */, 100 | 97C146F01CF9000F007C117D /* Runner */, 101 | 97C146EF1CF9000F007C117D /* Products */, 102 | 1DFC42C9B7529C93C9E92E7D /* Pods */, 103 | 6292D635F241CF4B91368F3C /* Frameworks */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 97C146EF1CF9000F007C117D /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 97C146EE1CF9000F007C117D /* Runner.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 97C146F01CF9000F007C117D /* Runner */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 119 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 120 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 121 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 122 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 123 | 97C147021CF9000F007C117D /* Info.plist */, 124 | 97C146F11CF9000F007C117D /* Supporting Files */, 125 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 126 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 127 | ); 128 | path = Runner; 129 | sourceTree = ""; 130 | }; 131 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 97C146F21CF9000F007C117D /* main.m */, 135 | ); 136 | name = "Supporting Files"; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 97C146ED1CF9000F007C117D /* Runner */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 145 | buildPhases = ( 146 | F648A867F04CC1FB7002D25D /* [CP] Check Pods Manifest.lock */, 147 | 9740EEB61CF901F6004384FC /* Run Script */, 148 | 97C146EA1CF9000F007C117D /* Sources */, 149 | 97C146EB1CF9000F007C117D /* Frameworks */, 150 | 97C146EC1CF9000F007C117D /* Resources */, 151 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 152 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 0910; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | DevelopmentTeam = 544P5CH38C; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = English; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 204 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 205 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "Thin Binary"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 225 | }; 226 | 9740EEB61CF901F6004384FC /* Run Script */ = { 227 | isa = PBXShellScriptBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | ); 231 | inputPaths = ( 232 | ); 233 | name = "Run Script"; 234 | outputPaths = ( 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | shellPath = /bin/sh; 238 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 239 | }; 240 | F648A867F04CC1FB7002D25D /* [CP] Check Pods Manifest.lock */ = { 241 | isa = PBXShellScriptBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | inputFileListPaths = ( 246 | ); 247 | inputPaths = ( 248 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 249 | "${PODS_ROOT}/Manifest.lock", 250 | ); 251 | name = "[CP] Check Pods Manifest.lock"; 252 | outputFileListPaths = ( 253 | ); 254 | outputPaths = ( 255 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | shellPath = /bin/sh; 259 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 260 | showEnvVarsInLog = 0; 261 | }; 262 | /* End PBXShellScriptBuildPhase section */ 263 | 264 | /* Begin PBXSourcesBuildPhase section */ 265 | 97C146EA1CF9000F007C117D /* Sources */ = { 266 | isa = PBXSourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 270 | 97C146F31CF9000F007C117D /* main.m in Sources */, 271 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | /* End PBXSourcesBuildPhase section */ 276 | 277 | /* Begin PBXVariantGroup section */ 278 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 279 | isa = PBXVariantGroup; 280 | children = ( 281 | 97C146FB1CF9000F007C117D /* Base */, 282 | ); 283 | name = Main.storyboard; 284 | sourceTree = ""; 285 | }; 286 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | 97C147001CF9000F007C117D /* Base */, 290 | ); 291 | name = LaunchScreen.storyboard; 292 | sourceTree = ""; 293 | }; 294 | /* End PBXVariantGroup section */ 295 | 296 | /* Begin XCBuildConfiguration section */ 297 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ALWAYS_SEARCH_USER_PATHS = NO; 301 | CLANG_ANALYZER_NONNULL = YES; 302 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 303 | CLANG_CXX_LIBRARY = "libc++"; 304 | CLANG_ENABLE_MODULES = YES; 305 | CLANG_ENABLE_OBJC_ARC = YES; 306 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 307 | CLANG_WARN_BOOL_CONVERSION = YES; 308 | CLANG_WARN_COMMA = YES; 309 | CLANG_WARN_CONSTANT_CONVERSION = YES; 310 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 311 | CLANG_WARN_EMPTY_BODY = YES; 312 | CLANG_WARN_ENUM_CONVERSION = YES; 313 | CLANG_WARN_INFINITE_RECURSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 316 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 318 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 319 | CLANG_WARN_STRICT_PROTOTYPES = YES; 320 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = iphoneos; 339 | TARGETED_DEVICE_FAMILY = "1,2"; 340 | VALIDATE_PRODUCT = YES; 341 | }; 342 | name = Profile; 343 | }; 344 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 345 | isa = XCBuildConfiguration; 346 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 347 | buildSettings = { 348 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 349 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 350 | DEVELOPMENT_TEAM = 544P5CH38C; 351 | ENABLE_BITCODE = NO; 352 | FRAMEWORK_SEARCH_PATHS = ( 353 | "$(inherited)", 354 | "$(PROJECT_DIR)/Flutter", 355 | ); 356 | INFOPLIST_FILE = Runner/Info.plist; 357 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 358 | LIBRARY_SEARCH_PATHS = ( 359 | "$(inherited)", 360 | "$(PROJECT_DIR)/Flutter", 361 | ); 362 | PRODUCT_BUNDLE_IDENTIFIER = com.opensource.svgaplayerFlutterExample; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | VERSIONING_SYSTEM = "apple-generic"; 365 | }; 366 | name = Profile; 367 | }; 368 | 97C147031CF9000F007C117D /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | ALWAYS_SEARCH_USER_PATHS = NO; 372 | CLANG_ANALYZER_NONNULL = YES; 373 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 374 | CLANG_CXX_LIBRARY = "libc++"; 375 | CLANG_ENABLE_MODULES = YES; 376 | CLANG_ENABLE_OBJC_ARC = YES; 377 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 378 | CLANG_WARN_BOOL_CONVERSION = YES; 379 | CLANG_WARN_COMMA = YES; 380 | CLANG_WARN_CONSTANT_CONVERSION = YES; 381 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 382 | CLANG_WARN_EMPTY_BODY = YES; 383 | CLANG_WARN_ENUM_CONVERSION = YES; 384 | CLANG_WARN_INFINITE_RECURSION = YES; 385 | CLANG_WARN_INT_CONVERSION = YES; 386 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 387 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 389 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 390 | CLANG_WARN_STRICT_PROTOTYPES = YES; 391 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 395 | COPY_PHASE_STRIP = NO; 396 | DEBUG_INFORMATION_FORMAT = dwarf; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | ENABLE_TESTABILITY = YES; 399 | GCC_C_LANGUAGE_STANDARD = gnu99; 400 | GCC_DYNAMIC_NO_PIC = NO; 401 | GCC_NO_COMMON_BLOCKS = YES; 402 | GCC_OPTIMIZATION_LEVEL = 0; 403 | GCC_PREPROCESSOR_DEFINITIONS = ( 404 | "DEBUG=1", 405 | "$(inherited)", 406 | ); 407 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 409 | GCC_WARN_UNDECLARED_SELECTOR = YES; 410 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 411 | GCC_WARN_UNUSED_FUNCTION = YES; 412 | GCC_WARN_UNUSED_VARIABLE = YES; 413 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 414 | MTL_ENABLE_DEBUG_INFO = YES; 415 | ONLY_ACTIVE_ARCH = YES; 416 | SDKROOT = iphoneos; 417 | TARGETED_DEVICE_FAMILY = "1,2"; 418 | }; 419 | name = Debug; 420 | }; 421 | 97C147041CF9000F007C117D /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ALWAYS_SEARCH_USER_PATHS = NO; 425 | CLANG_ANALYZER_NONNULL = YES; 426 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 427 | CLANG_CXX_LIBRARY = "libc++"; 428 | CLANG_ENABLE_MODULES = YES; 429 | CLANG_ENABLE_OBJC_ARC = YES; 430 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 431 | CLANG_WARN_BOOL_CONVERSION = YES; 432 | CLANG_WARN_COMMA = YES; 433 | CLANG_WARN_CONSTANT_CONVERSION = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_EMPTY_BODY = YES; 436 | CLANG_WARN_ENUM_CONVERSION = YES; 437 | CLANG_WARN_INFINITE_RECURSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 440 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 442 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 443 | CLANG_WARN_STRICT_PROTOTYPES = YES; 444 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 445 | CLANG_WARN_UNREACHABLE_CODE = YES; 446 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 447 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 448 | COPY_PHASE_STRIP = NO; 449 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 450 | ENABLE_NS_ASSERTIONS = NO; 451 | ENABLE_STRICT_OBJC_MSGSEND = YES; 452 | GCC_C_LANGUAGE_STANDARD = gnu99; 453 | GCC_NO_COMMON_BLOCKS = YES; 454 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 455 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 456 | GCC_WARN_UNDECLARED_SELECTOR = YES; 457 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 458 | GCC_WARN_UNUSED_FUNCTION = YES; 459 | GCC_WARN_UNUSED_VARIABLE = YES; 460 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 461 | MTL_ENABLE_DEBUG_INFO = NO; 462 | SDKROOT = iphoneos; 463 | TARGETED_DEVICE_FAMILY = "1,2"; 464 | VALIDATE_PRODUCT = YES; 465 | }; 466 | name = Release; 467 | }; 468 | 97C147061CF9000F007C117D /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 471 | buildSettings = { 472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 473 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 474 | DEVELOPMENT_TEAM = 544P5CH38C; 475 | ENABLE_BITCODE = NO; 476 | FRAMEWORK_SEARCH_PATHS = ( 477 | "$(inherited)", 478 | "$(PROJECT_DIR)/Flutter", 479 | ); 480 | INFOPLIST_FILE = Runner/Info.plist; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 482 | LIBRARY_SEARCH_PATHS = ( 483 | "$(inherited)", 484 | "$(PROJECT_DIR)/Flutter", 485 | ); 486 | PRODUCT_BUNDLE_IDENTIFIER = com.opensource.svgaplayerFlutterExample; 487 | PRODUCT_NAME = "$(TARGET_NAME)"; 488 | VERSIONING_SYSTEM = "apple-generic"; 489 | }; 490 | name = Debug; 491 | }; 492 | 97C147071CF9000F007C117D /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 495 | buildSettings = { 496 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 497 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 498 | DEVELOPMENT_TEAM = 544P5CH38C; 499 | ENABLE_BITCODE = NO; 500 | FRAMEWORK_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "$(PROJECT_DIR)/Flutter", 503 | ); 504 | INFOPLIST_FILE = Runner/Info.plist; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 506 | LIBRARY_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "$(PROJECT_DIR)/Flutter", 509 | ); 510 | PRODUCT_BUNDLE_IDENTIFIER = com.opensource.svgaplayerFlutterExample; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | VERSIONING_SYSTEM = "apple-generic"; 513 | }; 514 | name = Release; 515 | }; 516 | /* End XCBuildConfiguration section */ 517 | 518 | /* Begin XCConfigurationList section */ 519 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | 97C147031CF9000F007C117D /* Debug */, 523 | 97C147041CF9000F007C117D /* Release */, 524 | 249021D3217E4FDB00AE95B9 /* Profile */, 525 | ); 526 | defaultConfigurationIsVisible = 0; 527 | defaultConfigurationName = Release; 528 | }; 529 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | 97C147061CF9000F007C117D /* Debug */, 533 | 97C147071CF9000F007C117D /* Release */, 534 | 249021D4217E4FDB00AE95B9 /* Profile */, 535 | ); 536 | defaultConfigurationIsVisible = 0; 537 | defaultConfigurationName = Release; 538 | }; 539 | /* End XCConfigurationList section */ 540 | }; 541 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 542 | } 543 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svga/SVGAPlayer-Flutter/de7de7036797b47c0df431fef67e4d8051b162af/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | svgaplayer_flutter_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:svgaplayer_flutter/svgaplayer_flutter.dart'; 5 | 6 | void main() => runApp(ExampleApp()); 7 | 8 | class ExampleApp extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return MaterialApp(theme: ThemeData.dark(), home: HomeScreen()); 12 | } 13 | } 14 | 15 | class HomeScreen extends StatelessWidget { 16 | final samples = const [ 17 | "assets/angel.svga", 18 | "assets/pin_jump.svga", 19 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/EmptyState.svga", 20 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/HamburgerArrow.svga", 21 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/PinJump.svga", 22 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/TwitterHeart.svga", 23 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/Walkthrough.svga", 24 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/kingset.svga", 25 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/halloween.svga", 26 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/heartbeat.svga", 27 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/matteBitmap.svga", 28 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/matteBitmap_1.x.svga", 29 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/matteRect.svga", 30 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/mutiMatte.svga", 31 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/posche.svga", 32 | "https://cdn.jsdelivr.net/gh/svga/SVGA-Samples@master/rose.svga", 33 | ].map((e) => [e.split('/').last, e]).toList(growable: false); 34 | 35 | // callback for register dynamicItem 36 | final dynamicSamples = { 37 | "kingset.svga": (entity) => entity.dynamicItem 38 | ..setText( 39 | TextPainter( 40 | text: TextSpan( 41 | text: "Hello, World!", 42 | style: TextStyle( 43 | fontSize: 28, 44 | color: Colors.white, 45 | fontWeight: FontWeight.bold, 46 | ))), 47 | "banner") 48 | // ..setImageWithUrl( 49 | // "https://github.com/PonyCui/resources/blob/master/svga_replace_avatar.png?raw=true", 50 | // "99") 51 | // ..setDynamicDrawer((canvas, frameIndex) { 52 | // canvas.drawRect(Rect.fromLTWH(0, 0, 88, 88), 53 | // Paint()..color = Colors.red); // draw by yourself. 54 | // }, "banner"), 55 | }; 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | return Scaffold( 60 | appBar: AppBar(title: Text('SVGA Flutter Samples')), 61 | body: ListView.separated( 62 | itemCount: samples.length, 63 | separatorBuilder: (_, __) => Divider(), 64 | itemBuilder: (context, index) { 65 | return ListTile( 66 | title: Text(samples[index].first), 67 | subtitle: Text(samples[index].last), 68 | onTap: () => _goToSample(context, samples[index])); 69 | }), 70 | ); 71 | } 72 | 73 | void _goToSample(context, List sample) { 74 | Navigator.of(context).push(MaterialPageRoute(builder: (context) { 75 | return SVGASampleScreen( 76 | name: sample.first, 77 | image: sample.last, 78 | dynamicCallback: dynamicSamples[sample.first]); 79 | })); 80 | } 81 | } 82 | 83 | class SVGASampleScreen extends StatefulWidget { 84 | final String? name; 85 | final String image; 86 | final void Function(MovieEntity entity)? dynamicCallback; 87 | const SVGASampleScreen( 88 | {Key? key, required this.image, this.name, this.dynamicCallback}) 89 | : super(key: key); 90 | 91 | @override 92 | _SVGASampleScreenState createState() => _SVGASampleScreenState(); 93 | } 94 | 95 | class _SVGASampleScreenState extends State 96 | with SingleTickerProviderStateMixin { 97 | SVGAAnimationController? animationController; 98 | bool isLoading = true; 99 | Color backgroundColor = Colors.transparent; 100 | bool allowOverflow = true; 101 | // Canvaskit need FilterQuality.high 102 | FilterQuality filterQuality = kIsWeb ? FilterQuality.high : FilterQuality.low; 103 | BoxFit fit = BoxFit.contain; 104 | late double containerWidth; 105 | late double containerHeight; 106 | bool hideOptions = false; 107 | @override 108 | void initState() { 109 | super.initState(); 110 | this.animationController = SVGAAnimationController(vsync: this); 111 | this._loadAnimation(); 112 | } 113 | 114 | @override 115 | void didChangeDependencies() { 116 | super.didChangeDependencies(); 117 | containerWidth = math.min(350, MediaQuery.of(context).size.width); 118 | containerHeight = math.min(350, MediaQuery.of(context).size.height); 119 | } 120 | 121 | @override 122 | void dispose() { 123 | this.animationController?.dispose(); 124 | this.animationController = null; 125 | super.dispose(); 126 | } 127 | 128 | void _loadAnimation() async { 129 | // FIXME: may throw error on loading 130 | final videoItem = await _loadVideoItem(widget.image); 131 | if (widget.dynamicCallback != null) { 132 | widget.dynamicCallback!(videoItem); 133 | } 134 | if (mounted) 135 | setState(() { 136 | this.isLoading = false; 137 | this.animationController?.videoItem = videoItem; 138 | _playAnimation(); 139 | }); 140 | } 141 | 142 | void _playAnimation() { 143 | if (animationController?.isCompleted == true) { 144 | animationController?.reset(); 145 | } 146 | animationController?.repeat(); // or animationController.forward(); 147 | } 148 | 149 | @override 150 | Widget build(BuildContext context) { 151 | return Scaffold( 152 | appBar: AppBar(title: Text(widget.name ?? "")), 153 | body: Stack( 154 | children: [ 155 | Container( 156 | padding: const EdgeInsets.all(8.0), 157 | child: Text("Url: ${widget.image}", 158 | style: Theme.of(context).textTheme.subtitle2)), 159 | if (isLoading) LinearProgressIndicator(), 160 | Center( 161 | child: ColoredBox( 162 | color: backgroundColor, 163 | child: SVGAImage( 164 | this.animationController!, 165 | fit: fit, 166 | clearsAfterStop: false, 167 | allowDrawingOverflow: allowOverflow, 168 | filterQuality: filterQuality, 169 | preferredSize: Size(containerWidth, containerHeight), 170 | ), 171 | ), 172 | ), 173 | Positioned(bottom: 10, child: _buildOptions(context)), 174 | ], 175 | ), 176 | floatingActionButton: isLoading || animationController!.videoItem == null 177 | ? null 178 | : FloatingActionButton.extended( 179 | label: Text(animationController!.isAnimating ? "Pause" : "Play"), 180 | icon: Icon(animationController!.isAnimating 181 | ? Icons.pause 182 | : Icons.play_arrow), 183 | onPressed: () { 184 | if (animationController?.isAnimating == true) { 185 | animationController?.stop(); 186 | } else { 187 | _playAnimation(); 188 | } 189 | setState(() {}); 190 | }), 191 | ); 192 | } 193 | 194 | Widget _buildOptions(BuildContext context) { 195 | return Container( 196 | width: 240, 197 | color: Colors.black12, 198 | padding: EdgeInsets.all(8.0), 199 | child: SliderTheme( 200 | data: SliderTheme.of(context).copyWith( 201 | showValueIndicator: ShowValueIndicator.always, 202 | trackHeight: 2, 203 | overlayShape: const RoundSliderOverlayShape(overlayRadius: 10), 204 | thumbShape: const RoundSliderThumbShape( 205 | enabledThumbRadius: 6, pressedElevation: 4), 206 | ), 207 | child: Column( 208 | crossAxisAlignment: CrossAxisAlignment.start, 209 | children: [ 210 | TextButton.icon( 211 | onPressed: () { 212 | setState(() { 213 | hideOptions = !hideOptions; 214 | }); 215 | }, 216 | icon: hideOptions 217 | ? Icon(Icons.arrow_drop_up) 218 | : Icon(Icons.arrow_drop_down), 219 | label: Text(hideOptions ? 'Show options' : 'Hide options')), 220 | AnimatedBuilder( 221 | animation: animationController!, 222 | builder: (context, child) { 223 | return Text( 224 | 'Current frame: ${animationController!.currentFrame + 1}/${animationController!.frames}'); 225 | }), 226 | if (!hideOptions) ...[ 227 | AnimatedBuilder( 228 | animation: animationController!, 229 | builder: (context, child) { 230 | return Slider( 231 | min: 0, 232 | max: animationController!.frames.toDouble(), 233 | value: animationController!.currentFrame.toDouble(), 234 | label: '${animationController!.currentFrame}', 235 | onChanged: (v) { 236 | if (animationController?.isAnimating == true) { 237 | animationController?.stop(); 238 | } 239 | animationController?.value = 240 | v / animationController!.frames; 241 | }, 242 | ); 243 | }), 244 | Row( 245 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 246 | children: [ 247 | Text('Image filter quality'), 248 | DropdownButton( 249 | value: filterQuality, 250 | onChanged: (FilterQuality? newValue) { 251 | setState(() { 252 | filterQuality = newValue!; 253 | }); 254 | }, 255 | items: FilterQuality.values.map((FilterQuality value) { 256 | return DropdownMenuItem( 257 | value: value, 258 | child: Text(value.toString().split('.').last), 259 | ); 260 | }).toList(), 261 | ) 262 | ], 263 | ), 264 | const SizedBox(height: 8), 265 | Row( 266 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 267 | children: [ 268 | Text('Allow drawing overflow'), 269 | const SizedBox(width: 8), 270 | Switch( 271 | value: allowOverflow, 272 | onChanged: (v) { 273 | setState(() { 274 | allowOverflow = v; 275 | }); 276 | }, 277 | ) 278 | ], 279 | ), 280 | Text('Container options:'), 281 | Row( 282 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 283 | children: [ 284 | Text(' width:'), 285 | Slider( 286 | min: 100, 287 | max: MediaQuery.of(context).size.width.roundToDouble(), 288 | value: containerWidth, 289 | label: '$containerWidth', 290 | onChanged: (v) { 291 | setState(() { 292 | containerWidth = v.truncateToDouble(); 293 | }); 294 | }, 295 | ), 296 | ], 297 | ), 298 | Row( 299 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 300 | children: [ 301 | Text(' height:'), 302 | Slider( 303 | min: 100, 304 | max: MediaQuery.of(context).size.height.roundToDouble(), 305 | label: '$containerHeight', 306 | value: containerHeight, 307 | onChanged: (v) { 308 | setState(() { 309 | containerHeight = v.truncateToDouble(); 310 | }); 311 | }, 312 | ), 313 | ], 314 | ), 315 | Row( 316 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 317 | children: [ 318 | Text(' box fit: '), 319 | const SizedBox(width: 8), 320 | DropdownButton( 321 | value: fit, 322 | onChanged: (BoxFit? newValue) { 323 | setState(() { 324 | fit = newValue!; 325 | }); 326 | }, 327 | items: BoxFit.values.map((BoxFit value) { 328 | return DropdownMenuItem( 329 | value: value, 330 | child: Text(value.toString().split('.').last), 331 | ); 332 | }).toList(), 333 | ) 334 | ], 335 | ), 336 | Row( 337 | mainAxisAlignment: MainAxisAlignment.spaceAround, 338 | children: const [ 339 | Colors.transparent, 340 | Colors.red, 341 | Colors.green, 342 | Colors.blue, 343 | Colors.yellow, 344 | Colors.black, 345 | ] 346 | .map( 347 | (e) => GestureDetector( 348 | onTap: () { 349 | setState(() { 350 | backgroundColor = e; 351 | }); 352 | }, 353 | child: Container( 354 | width: 20, 355 | height: 20, 356 | decoration: ShapeDecoration( 357 | color: e, 358 | shape: CircleBorder( 359 | side: backgroundColor == e 360 | ? const BorderSide( 361 | color: Colors.white, 362 | width: 3, 363 | ) 364 | : const BorderSide(color: Colors.grey), 365 | ), 366 | ), 367 | ), 368 | ), 369 | ) 370 | .toList(), 371 | ), 372 | ], 373 | ], 374 | ), 375 | ), 376 | ); 377 | } 378 | } 379 | 380 | Future _loadVideoItem(String image) { 381 | Future Function(String) decoder; 382 | if (image.startsWith(RegExp(r'https?://'))) { 383 | decoder = SVGAParser.shared.decodeFromURL; 384 | } else { 385 | decoder = SVGAParser.shared.decodeFromAssets; 386 | } 387 | return decoder(image); 388 | } 389 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "3.1.2" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "2.9.0" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.2.1" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.1.1" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.16.0" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "3.0.1" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "0.1.2" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.flutter-io.cn" 72 | source: hosted 73 | version: "1.3.1" 74 | fixnum: 75 | dependency: transitive 76 | description: 77 | name: fixnum 78 | url: "https://pub.flutter-io.cn" 79 | source: hosted 80 | version: "1.0.0" 81 | flutter: 82 | dependency: "direct main" 83 | description: flutter 84 | source: sdk 85 | version: "0.0.0" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | http: 92 | dependency: transitive 93 | description: 94 | name: http 95 | url: "https://pub.flutter-io.cn" 96 | source: hosted 97 | version: "0.13.3" 98 | http_parser: 99 | dependency: transitive 100 | description: 101 | name: http_parser 102 | url: "https://pub.flutter-io.cn" 103 | source: hosted 104 | version: "4.0.0" 105 | matcher: 106 | dependency: transitive 107 | description: 108 | name: matcher 109 | url: "https://pub.flutter-io.cn" 110 | source: hosted 111 | version: "0.12.12" 112 | material_color_utilities: 113 | dependency: transitive 114 | description: 115 | name: material_color_utilities 116 | url: "https://pub.flutter-io.cn" 117 | source: hosted 118 | version: "0.1.5" 119 | meta: 120 | dependency: transitive 121 | description: 122 | name: meta 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "1.8.0" 126 | path: 127 | dependency: transitive 128 | description: 129 | name: path 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "1.8.2" 133 | path_drawing: 134 | dependency: transitive 135 | description: 136 | name: path_drawing 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "1.0.1" 140 | path_parsing: 141 | dependency: transitive 142 | description: 143 | name: path_parsing 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "1.0.1" 147 | pedantic: 148 | dependency: transitive 149 | description: 150 | name: pedantic 151 | url: "https://pub.flutter-io.cn" 152 | source: hosted 153 | version: "1.11.0" 154 | protobuf: 155 | dependency: transitive 156 | description: 157 | name: protobuf 158 | url: "https://pub.flutter-io.cn" 159 | source: hosted 160 | version: "2.0.0" 161 | sky_engine: 162 | dependency: transitive 163 | description: flutter 164 | source: sdk 165 | version: "0.0.99" 166 | source_span: 167 | dependency: transitive 168 | description: 169 | name: source_span 170 | url: "https://pub.flutter-io.cn" 171 | source: hosted 172 | version: "1.9.0" 173 | stack_trace: 174 | dependency: transitive 175 | description: 176 | name: stack_trace 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "1.10.0" 180 | stream_channel: 181 | dependency: transitive 182 | description: 183 | name: stream_channel 184 | url: "https://pub.flutter-io.cn" 185 | source: hosted 186 | version: "2.1.0" 187 | string_scanner: 188 | dependency: transitive 189 | description: 190 | name: string_scanner 191 | url: "https://pub.flutter-io.cn" 192 | source: hosted 193 | version: "1.1.1" 194 | svgaplayer_flutter: 195 | dependency: "direct dev" 196 | description: 197 | path: ".." 198 | relative: true 199 | source: path 200 | version: "2.1.2" 201 | term_glyph: 202 | dependency: transitive 203 | description: 204 | name: term_glyph 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "1.2.1" 208 | test_api: 209 | dependency: transitive 210 | description: 211 | name: test_api 212 | url: "https://pub.flutter-io.cn" 213 | source: hosted 214 | version: "0.4.12" 215 | typed_data: 216 | dependency: transitive 217 | description: 218 | name: typed_data 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "1.3.0" 222 | vector_math: 223 | dependency: transitive 224 | description: 225 | name: vector_math 226 | url: "https://pub.flutter-io.cn" 227 | source: hosted 228 | version: "2.1.2" 229 | sdks: 230 | dart: ">=2.17.0-0 <3.0.0" 231 | flutter: ">=1.24.0-7.0" 232 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: svgaplayer_flutter_example 2 | description: Demonstrates how to use the svgaplayer_flutter plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.12.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^0.1.2 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | svgaplayer_flutter: 21 | path: ../ 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://www.dartlang.org/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | 34 | # To add assets to your application, add an assets section, like this: 35 | assets: 36 | - assets/pin_jump.svga 37 | - assets/angel.svga 38 | 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.io/assets-and-images/#resolution-aware. 41 | 42 | # For details regarding adding assets from package dependencies, see 43 | # https://flutter.io/assets-and-images/#from-packages 44 | 45 | # To add custom fonts to your application, add a fonts section here, 46 | # in this "flutter" section. Each entry in this list should have a 47 | # "family" key with the font family name, and a "fonts" key with a 48 | # list giving the asset and other descriptors for the font. For 49 | # example: 50 | # fonts: 51 | # - family: Schyler 52 | # fonts: 53 | # - asset: fonts/Schyler-Regular.ttf 54 | # - asset: fonts/Schyler-Italic.ttf 55 | # style: italic 56 | # - family: Trajan Pro 57 | # fonts: 58 | # - asset: fonts/TrajanPro.ttf 59 | # - asset: fonts/TrajanPro_Bold.ttf 60 | # weight: 700 61 | # 62 | # For details regarding fonts from package dependencies, 63 | # see https://flutter.io/custom-fonts/#from-packages 64 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | void main() {} 9 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | example 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/dynamic_entity.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui show Image; 2 | import 'package:http/http.dart'; 3 | import 'package:flutter/painting.dart'; 4 | 5 | typedef SVGACustomDrawer = Function(Canvas canvas, int frameIndex); 6 | 7 | class SVGADynamicEntity { 8 | final Map dynamicHidden = {}; 9 | final Map dynamicImages = {}; 10 | final Map dynamicText = {}; 11 | final Map dynamicDrawer = {}; 12 | 13 | void setHidden(bool value, String forKey) { 14 | this.dynamicHidden[forKey] = value; 15 | } 16 | 17 | void setImage(ui.Image image, String forKey) { 18 | this.dynamicImages[forKey] = image; 19 | } 20 | 21 | Future setImageWithUrl(String url, String forKey) async { 22 | this.dynamicImages[forKey] = 23 | await decodeImageFromList((await get(Uri.parse(url))).bodyBytes); 24 | } 25 | 26 | void setText(TextPainter textPainter, String forKey) { 27 | if (textPainter.textDirection == null) { 28 | textPainter.textDirection = TextDirection.ltr; 29 | textPainter.layout(); 30 | } 31 | this.dynamicText[forKey] = textPainter; 32 | } 33 | 34 | void setDynamicDrawer(SVGACustomDrawer drawer, String forKey) { 35 | this.dynamicDrawer[forKey] = drawer; 36 | } 37 | 38 | void reset() { 39 | this.dynamicHidden.clear(); 40 | this.dynamicImages.clear(); 41 | this.dynamicText.clear(); 42 | this.dynamicDrawer.clear(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/painter.dart: -------------------------------------------------------------------------------- 1 | part of svgaplayer_flutter_player; 2 | 3 | class _SVGAPainter extends CustomPainter { 4 | final BoxFit fit; 5 | final SVGAAnimationController controller; 6 | int get currentFrame => controller.currentFrame; 7 | MovieEntity get videoItem => controller.videoItem!; 8 | final FilterQuality filterQuality; 9 | 10 | /// Guaranteed to draw within the canvas bounds 11 | final bool clipRect; 12 | _SVGAPainter( 13 | this.controller, { 14 | this.fit = BoxFit.contain, 15 | this.filterQuality = FilterQuality.low, 16 | this.clipRect = true, 17 | }) : assert( 18 | controller.videoItem != null, 'Invalid SVGAAnimationController!'), 19 | super(repaint: controller); 20 | 21 | @override 22 | void paint(Canvas canvas, Size size) { 23 | if (controller._canvasNeedsClear) { 24 | // mark cleared 25 | controller._canvasNeedsClear = false; 26 | return; 27 | } 28 | if (size.isEmpty || controller.videoItem == null) return; 29 | final params = videoItem.params; 30 | final Size viewBoxSize = Size(params.viewBoxWidth, params.viewBoxHeight); 31 | if (viewBoxSize.isEmpty) return; 32 | canvas.save(); 33 | try { 34 | final canvasRect = Offset.zero & size; 35 | if (clipRect) canvas.clipRect(canvasRect); 36 | scaleCanvasToViewBox(canvas, canvasRect, Offset.zero & viewBoxSize); 37 | drawSprites(canvas, size); 38 | } finally { 39 | canvas.restore(); 40 | } 41 | } 42 | 43 | void scaleCanvasToViewBox(Canvas canvas, Rect canvasRect, Rect viewBoxRect) { 44 | final fittedSizes = applyBoxFit(fit, viewBoxRect.size, canvasRect.size); 45 | 46 | // scale viewbox size (source) to canvas size (destination) 47 | var sx = fittedSizes.destination.width / fittedSizes.source.width; 48 | var sy = fittedSizes.destination.height / fittedSizes.source.height; 49 | final Size scaledHalfViewBoxSize = 50 | Size(viewBoxRect.size.width * sx, viewBoxRect.size.height * sy) / 2.0; 51 | final Size halfCanvasSize = canvasRect.size / 2.0; 52 | // center align 53 | final Offset shift = Offset( 54 | halfCanvasSize.width - scaledHalfViewBoxSize.width, 55 | halfCanvasSize.height - scaledHalfViewBoxSize.height, 56 | ); 57 | if (shift != Offset.zero) canvas.translate(shift.dx, shift.dy); 58 | if (sx != 1.0 && sy != 1.0) canvas.scale(sx, sy); 59 | } 60 | 61 | void drawSprites(Canvas canvas, Size size) { 62 | for (final sprite in videoItem.sprites) { 63 | final imageKey = sprite.imageKey; 64 | // var matteKey = sprite.matteKey; 65 | if (imageKey.isEmpty || 66 | videoItem.dynamicItem.dynamicHidden[imageKey] == true) { 67 | continue; 68 | } 69 | final frameItem = sprite.frames[currentFrame]; 70 | final needTransform = frameItem.hasTransform(); 71 | final needClip = frameItem.hasClipPath(); 72 | if (needTransform) { 73 | canvas.save(); 74 | canvas.transform(Float64List.fromList([ 75 | frameItem.transform.a, 76 | frameItem.transform.b, 77 | 0.0, 78 | 0.0, 79 | frameItem.transform.c, 80 | frameItem.transform.d, 81 | 0.0, 82 | 0.0, 83 | 0.0, 84 | 0.0, 85 | 1.0, 86 | 0.0, 87 | frameItem.transform.tx, 88 | frameItem.transform.ty, 89 | 0.0, 90 | 1.0 91 | ])); 92 | } 93 | if (needClip) { 94 | canvas.save(); 95 | canvas.clipPath(buildDPath(frameItem.clipPath)); 96 | } 97 | final frameRect = 98 | Rect.fromLTRB(0, 0, frameItem.layout.width, frameItem.layout.height); 99 | final frameAlpha = 100 | frameItem.hasAlpha() ? (frameItem.alpha * 255).toInt() : 255; 101 | drawBitmap(canvas, imageKey, frameRect, frameAlpha); 102 | drawShape(canvas, frameItem.shapes, frameAlpha); 103 | // draw dynamic 104 | final dynamicDrawer = videoItem.dynamicItem.dynamicDrawer[imageKey]; 105 | if (dynamicDrawer != null) { 106 | dynamicDrawer(canvas, currentFrame); 107 | } 108 | if (needClip) { 109 | canvas.restore(); 110 | } 111 | if (needTransform) { 112 | canvas.restore(); 113 | } 114 | } 115 | } 116 | 117 | void drawBitmap(Canvas canvas, String imageKey, Rect frameRect, int alpha) { 118 | final bitmap = videoItem.dynamicItem.dynamicImages[imageKey] ?? 119 | videoItem.bitmapCache[imageKey]; 120 | if (bitmap == null) return; 121 | 122 | final bitmapPaint = Paint(); 123 | bitmapPaint.filterQuality = filterQuality; 124 | //解决bitmap锯齿问题 125 | bitmapPaint.isAntiAlias = true; 126 | bitmapPaint.color = Color.fromARGB(alpha, 0, 0, 0); 127 | 128 | Rect srcRect = 129 | Rect.fromLTRB(0, 0, bitmap.width.toDouble(), bitmap.height.toDouble()); 130 | Rect dstRect = frameRect; 131 | canvas.drawImageRect(bitmap, srcRect, dstRect, bitmapPaint); 132 | drawTextOnBitmap(canvas, imageKey, frameRect, alpha); 133 | } 134 | 135 | void drawShape(Canvas canvas, List shapes, int frameAlpha) { 136 | if (shapes.isEmpty) return; 137 | for (var shape in shapes) { 138 | final path = buildPath(shape); 139 | if (shape.hasTransform()) { 140 | canvas.save(); 141 | canvas.transform(Float64List.fromList([ 142 | shape.transform.a, 143 | shape.transform.b, 144 | 0.0, 145 | 0.0, 146 | shape.transform.c, 147 | shape.transform.d, 148 | 0.0, 149 | 0.0, 150 | 0.0, 151 | 0.0, 152 | 1.0, 153 | 0.0, 154 | shape.transform.tx, 155 | shape.transform.ty, 156 | 0.0, 157 | 1.0 158 | ])); 159 | } 160 | 161 | final fill = shape.styles.fill; 162 | if (fill.isInitialized()) { 163 | final paint = Paint(); 164 | paint.isAntiAlias = true; 165 | paint.style = PaintingStyle.fill; 166 | paint.color = Color.fromARGB( 167 | (fill.a * frameAlpha).toInt(), 168 | (fill.r * 255).toInt(), 169 | (fill.g * 255).toInt(), 170 | (fill.b * 255).toInt(), 171 | ); 172 | canvas.drawPath(path, paint); 173 | } 174 | final strokeWidth = shape.styles.strokeWidth; 175 | if (strokeWidth > 0) { 176 | final paint = Paint(); 177 | paint.style = PaintingStyle.stroke; 178 | if (shape.styles.stroke.isInitialized()) { 179 | paint.color = Color.fromARGB( 180 | (shape.styles.stroke.a * frameAlpha).toInt(), 181 | (shape.styles.stroke.r * 255).toInt(), 182 | (shape.styles.stroke.g * 255).toInt(), 183 | (shape.styles.stroke.b * 255).toInt(), 184 | ); 185 | } 186 | paint.strokeWidth = strokeWidth; 187 | final lineCap = shape.styles.lineCap; 188 | switch (lineCap) { 189 | case ShapeEntity_ShapeStyle_LineCap.LineCap_BUTT: 190 | paint.strokeCap = StrokeCap.butt; 191 | break; 192 | case ShapeEntity_ShapeStyle_LineCap.LineCap_ROUND: 193 | paint.strokeCap = StrokeCap.round; 194 | break; 195 | case ShapeEntity_ShapeStyle_LineCap.LineCap_SQUARE: 196 | paint.strokeCap = StrokeCap.square; 197 | break; 198 | default: 199 | } 200 | final lineJoin = shape.styles.lineJoin; 201 | switch (lineJoin) { 202 | case ShapeEntity_ShapeStyle_LineJoin.LineJoin_MITER: 203 | paint.strokeJoin = StrokeJoin.miter; 204 | break; 205 | case ShapeEntity_ShapeStyle_LineJoin.LineJoin_ROUND: 206 | paint.strokeJoin = StrokeJoin.round; 207 | break; 208 | case ShapeEntity_ShapeStyle_LineJoin.LineJoin_BEVEL: 209 | paint.strokeJoin = StrokeJoin.bevel; 210 | break; 211 | default: 212 | } 213 | paint.strokeMiterLimit = shape.styles.miterLimit; 214 | List lineDash = [ 215 | shape.styles.lineDashI, 216 | shape.styles.lineDashII, 217 | shape.styles.lineDashIII 218 | ]; 219 | if (lineDash[0] > 0 || lineDash[1] > 0) { 220 | canvas.drawPath( 221 | dashPath( 222 | path, 223 | dashArray: CircularIntervalList([ 224 | lineDash[0] < 1.0 ? 1.0 : lineDash[0], 225 | lineDash[1] < 0.1 ? 0.1 : lineDash[1], 226 | ]), 227 | dashOffset: DashOffset.absolute(lineDash[2]), 228 | ), 229 | paint); 230 | } else { 231 | canvas.drawPath(path, paint); 232 | } 233 | } 234 | if (shape.hasTransform()) { 235 | canvas.restore(); 236 | } 237 | } 238 | } 239 | 240 | static const _validMethods = 'MLHVCSQRZmlhvcsqrz'; 241 | 242 | Path buildPath(ShapeEntity shape) { 243 | final path = Path(); 244 | if (shape.type == ShapeEntity_ShapeType.SHAPE) { 245 | final args = shape.shape; 246 | final argD = args.d; 247 | return buildDPath(argD, path: path); 248 | } else if (shape.type == ShapeEntity_ShapeType.ELLIPSE) { 249 | final args = shape.ellipse; 250 | final xv = args.x; 251 | final yv = args.y; 252 | final rxv = args.radiusX; 253 | final ryv = args.radiusY; 254 | final rect = Rect.fromLTWH(xv - rxv, yv - ryv, rxv * 2, ryv * 2); 255 | if (!rect.isEmpty) path.addOval(rect); 256 | } else if (shape.type == ShapeEntity_ShapeType.RECT) { 257 | final args = shape.rect; 258 | final xv = args.x; 259 | final yv = args.y; 260 | final wv = args.width; 261 | final hv = args.height; 262 | final crv = args.cornerRadius; 263 | final rrect = RRect.fromRectAndRadius( 264 | Rect.fromLTWH(xv, yv, wv, hv), Radius.circular(crv)); 265 | if (!rrect.isEmpty) path.addRRect(rrect); 266 | } 267 | return path; 268 | } 269 | 270 | Path buildDPath(String argD, {Path? path}) { 271 | if (videoItem.pathCache[argD] != null) { 272 | return videoItem.pathCache[argD]!; 273 | } 274 | path ??= Path(); 275 | final d = argD.replaceAllMapped(RegExp('([a-df-zA-Z])'), (match) { 276 | return "|||${match.group(1)} "; 277 | }).replaceAll(RegExp(","), " "); 278 | var currentPointX = 0.0; 279 | var currentPointY = 0.0; 280 | double? currentPointX1; 281 | double? currentPointY1; 282 | double? currentPointX2; 283 | double? currentPointY2; 284 | d.split("|||").forEach((segment) { 285 | if (segment.isEmpty) { 286 | return; 287 | } 288 | final firstLetter = segment.substring(0, 1); 289 | if (_validMethods.contains(firstLetter)) { 290 | final args = segment.substring(1).trim().split(" "); 291 | if (firstLetter == "M") { 292 | currentPointX = double.parse(args[0]); 293 | currentPointY = double.parse(args[1]); 294 | path!.moveTo(currentPointX, currentPointY); 295 | } else if (firstLetter == "m") { 296 | currentPointX += double.parse(args[0]); 297 | currentPointY += double.parse(args[1]); 298 | path!.moveTo(currentPointX, currentPointY); 299 | } else if (firstLetter == "L") { 300 | currentPointX = double.parse(args[0]); 301 | currentPointY = double.parse(args[1]); 302 | path!.lineTo(currentPointX, currentPointY); 303 | } else if (firstLetter == "l") { 304 | currentPointX += double.parse(args[0]); 305 | currentPointY += double.parse(args[1]); 306 | path!.lineTo(currentPointX, currentPointY); 307 | } else if (firstLetter == "H") { 308 | currentPointX = double.parse(args[0]); 309 | path!.lineTo(currentPointX, currentPointY); 310 | } else if (firstLetter == "h") { 311 | currentPointX += double.parse(args[0]); 312 | path!.lineTo(currentPointX, currentPointY); 313 | } else if (firstLetter == "V") { 314 | currentPointY = double.parse(args[0]); 315 | path!.lineTo(currentPointX, currentPointY); 316 | } else if (firstLetter == "v") { 317 | currentPointY += double.parse(args[0]); 318 | path!.lineTo(currentPointX, currentPointY); 319 | } else if (firstLetter == "C") { 320 | currentPointX1 = double.parse(args[0]); 321 | currentPointY1 = double.parse(args[1]); 322 | currentPointX2 = double.parse(args[2]); 323 | currentPointY2 = double.parse(args[3]); 324 | currentPointX = double.parse(args[4]); 325 | currentPointY = double.parse(args[5]); 326 | path!.cubicTo( 327 | currentPointX1!, 328 | currentPointY1!, 329 | currentPointX2!, 330 | currentPointY2!, 331 | currentPointX, 332 | currentPointY, 333 | ); 334 | } else if (firstLetter == "c") { 335 | currentPointX1 = currentPointX + double.parse(args[0]); 336 | currentPointY1 = currentPointY + double.parse(args[1]); 337 | currentPointX2 = currentPointX + double.parse(args[2]); 338 | currentPointY2 = currentPointY + double.parse(args[3]); 339 | currentPointX += double.parse(args[4]); 340 | currentPointY += double.parse(args[5]); 341 | path!.cubicTo( 342 | currentPointX1!, 343 | currentPointY1!, 344 | currentPointX2!, 345 | currentPointY2!, 346 | currentPointX, 347 | currentPointY, 348 | ); 349 | } else if (firstLetter == "S") { 350 | if (currentPointX1 != null && 351 | currentPointY1 != null && 352 | currentPointX2 != null && 353 | currentPointY2 != null) { 354 | currentPointX1 = currentPointX - currentPointX2! + currentPointX; 355 | currentPointY1 = currentPointY - currentPointY2! + currentPointY; 356 | currentPointX2 = double.parse(args[0]); 357 | currentPointY2 = double.parse(args[1]); 358 | currentPointX = double.parse(args[2]); 359 | currentPointY = double.parse(args[3]); 360 | path!.cubicTo( 361 | currentPointX1!, 362 | currentPointY1!, 363 | currentPointX2!, 364 | currentPointY2!, 365 | currentPointX, 366 | currentPointY, 367 | ); 368 | } else { 369 | currentPointX1 = double.parse(args[0]); 370 | currentPointY1 = double.parse(args[1]); 371 | currentPointX = double.parse(args[2]); 372 | currentPointY = double.parse(args[3]); 373 | path!.quadraticBezierTo( 374 | currentPointX1!, currentPointY1!, currentPointX, currentPointY); 375 | } 376 | } else if (firstLetter == "s") { 377 | if (currentPointX1 != null && 378 | currentPointY1 != null && 379 | currentPointX2 != null && 380 | currentPointY2 != null) { 381 | currentPointX1 = currentPointX - currentPointX2! + currentPointX; 382 | currentPointY1 = currentPointY - currentPointY2! + currentPointY; 383 | currentPointX2 = currentPointX + double.parse(args[0]); 384 | currentPointY2 = currentPointY + double.parse(args[1]); 385 | currentPointX += double.parse(args[2]); 386 | currentPointY += double.parse(args[3]); 387 | path!.cubicTo( 388 | currentPointX1!, 389 | currentPointY1!, 390 | currentPointX2!, 391 | currentPointY2!, 392 | currentPointX, 393 | currentPointY, 394 | ); 395 | } else { 396 | currentPointX1 = currentPointX + double.parse(args[0]); 397 | currentPointY1 = currentPointY + double.parse(args[1]); 398 | currentPointX += double.parse(args[2]); 399 | currentPointY += double.parse(args[3]); 400 | path!.quadraticBezierTo( 401 | currentPointX1!, 402 | currentPointY1!, 403 | currentPointX, 404 | currentPointY, 405 | ); 406 | } 407 | } else if (firstLetter == "Q") { 408 | currentPointX1 = double.parse(args[0]); 409 | currentPointY1 = double.parse(args[1]); 410 | currentPointX = double.parse(args[2]); 411 | currentPointY = double.parse(args[3]); 412 | path!.quadraticBezierTo( 413 | currentPointX1!, currentPointY1!, currentPointX, currentPointY); 414 | } else if (firstLetter == "q") { 415 | currentPointX1 = currentPointX + double.parse(args[0]); 416 | currentPointY1 = currentPointY + double.parse(args[1]); 417 | currentPointX += double.parse(args[2]); 418 | currentPointY += double.parse(args[3]); 419 | path!.quadraticBezierTo( 420 | currentPointX1!, 421 | currentPointY1!, 422 | currentPointX, 423 | currentPointY, 424 | ); 425 | } else if (firstLetter == "Z" || firstLetter == "z") { 426 | path!.close(); 427 | } 428 | } 429 | videoItem.pathCache[argD] = path!; 430 | }); 431 | return path; 432 | } 433 | 434 | void drawTextOnBitmap( 435 | Canvas canvas, String imageKey, Rect frameRect, int frameAlpha) { 436 | var dynamicText = videoItem.dynamicItem.dynamicText; 437 | if (dynamicText.isEmpty) return; 438 | if (dynamicText[imageKey] == null) return; 439 | 440 | TextPainter? textPainter = dynamicText[imageKey]; 441 | 442 | textPainter?.paint( 443 | canvas, 444 | Offset( 445 | (frameRect.width - textPainter.width) / 2.0, 446 | (frameRect.height - textPainter.height) / 2.0, 447 | ), 448 | ); 449 | } 450 | 451 | @override 452 | bool shouldRepaint(_SVGAPainter oldDelegate) { 453 | if (controller._canvasNeedsClear == true) { 454 | return true; 455 | } 456 | 457 | return !(oldDelegate.controller == controller && 458 | oldDelegate.controller.videoItem == controller.videoItem && 459 | oldDelegate.fit == fit && 460 | oldDelegate.filterQuality == filterQuality && 461 | oldDelegate.clipRect == clipRect); 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /lib/parser.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:ui' as ui; 3 | import 'dart:typed_data' show Uint8List; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/painting.dart' show decodeImageFromList; 6 | import 'package:flutter/services.dart' show rootBundle; 7 | import 'package:http/http.dart' show get; 8 | import 'package:archive/archive.dart' as archive; 9 | // ignore: import_of_legacy_library_into_null_safe 10 | import 'proto/svga.pbserver.dart'; 11 | 12 | const _filterKey = 'SVGAParser'; 13 | 14 | /// You use SVGAParser to load and decode animation files. 15 | class SVGAParser { 16 | const SVGAParser(); 17 | static const shared = SVGAParser(); 18 | 19 | /// Download animation file from remote server, and decode it. 20 | Future decodeFromURL(String url) async { 21 | final response = await get(Uri.parse(url)); 22 | return decodeFromBuffer(response.bodyBytes); 23 | } 24 | 25 | /// Download animation file from bundle assets, and decode it. 26 | Future decodeFromAssets(String path) async { 27 | return decodeFromBuffer((await rootBundle.load(path)).buffer.asUint8List()); 28 | } 29 | 30 | /// Download animation file from buffer, and decode it. 31 | Future decodeFromBuffer(List bytes) { 32 | TimelineTask? timeline; 33 | if (!kReleaseMode) { 34 | timeline = TimelineTask(filterKey: _filterKey) 35 | ..start('DecodeFromBuffer', arguments: {'length': bytes.length}); 36 | } 37 | final inflatedBytes = archive.ZLibDecoder().decodeBytes(bytes); 38 | if (timeline != null) { 39 | timeline.instant('MovieEntity.fromBuffer()', 40 | arguments: {'inflatedLength': inflatedBytes.length}); 41 | } 42 | final movie = MovieEntity.fromBuffer(inflatedBytes); 43 | if (timeline != null) { 44 | timeline.instant('prepareResources()', 45 | arguments: {'images': movie.images.keys.join(',')}); 46 | } 47 | return _prepareResources( 48 | _processShapeItems(movie), 49 | timeline: timeline, 50 | ).whenComplete(() { 51 | if (timeline != null) timeline.finish(); 52 | }); 53 | } 54 | 55 | MovieEntity _processShapeItems(MovieEntity movieItem) { 56 | movieItem.sprites.forEach((sprite) { 57 | List? lastShape; 58 | sprite.frames.forEach((frame) { 59 | if (frame.shapes.isNotEmpty && frame.shapes.length > 0) { 60 | if (frame.shapes[0].type == ShapeEntity_ShapeType.KEEP && 61 | lastShape != null) { 62 | frame.shapes = lastShape; 63 | } else if (frame.shapes.isNotEmpty == true) { 64 | lastShape = frame.shapes; 65 | } 66 | } 67 | }); 68 | }); 69 | return movieItem; 70 | } 71 | 72 | Future _prepareResources(MovieEntity movieItem, 73 | {TimelineTask? timeline}) { 74 | final images = movieItem.images; 75 | if (images.isEmpty) return Future.value(movieItem); 76 | return Future.wait(images.entries.map((item) async { 77 | // result null means a decoding error occurred 78 | final decodeImage = await _decodeImageItem( 79 | item.key, Uint8List.fromList(item.value), 80 | timeline: timeline); 81 | if (decodeImage != null) { 82 | movieItem.bitmapCache[item.key] = decodeImage; 83 | } 84 | })).then((_) => movieItem); 85 | } 86 | 87 | Future _decodeImageItem(String key, Uint8List bytes, 88 | {TimelineTask? timeline}) async { 89 | TimelineTask? task; 90 | if (!kReleaseMode) { 91 | task = TimelineTask(filterKey: _filterKey, parent: timeline) 92 | ..start('DecodeImage', arguments: {'key': key, 'length': bytes.length}); 93 | } 94 | try { 95 | final image = await decodeImageFromList(bytes); 96 | if (task != null) { 97 | task.finish( 98 | arguments: {'imageSize': '${image.width}x${image.height}'}, 99 | ); 100 | } 101 | return image; 102 | } catch (e, stack) { 103 | if (task != null) { 104 | task.finish(arguments: {'error': '$e', 'stack': '$stack'}); 105 | } 106 | assert(() { 107 | FlutterError.reportError(FlutterErrorDetails( 108 | exception: e, 109 | stack: stack, 110 | library: 'svgaplayer', 111 | context: ErrorDescription('during prepare resource'), 112 | informationCollector: () sync* { 113 | yield ErrorSummary('Decoding image failed.'); 114 | }, 115 | )); 116 | return true; 117 | }()); 118 | return null; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/player.dart: -------------------------------------------------------------------------------- 1 | library svgaplayer_flutter_player; 2 | 3 | import 'dart:math'; 4 | import 'package:flutter/rendering.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | // ignore: import_of_legacy_library_into_null_safe 7 | import 'package:svgaplayer_flutter/proto/svga.pb.dart'; 8 | // ignore: import_of_legacy_library_into_null_safe 9 | import 'proto/svga.pbserver.dart'; 10 | import 'dart:typed_data'; 11 | import 'package:path_drawing/path_drawing.dart'; 12 | import 'parser.dart'; 13 | part 'painter.dart'; 14 | part 'simple_player.dart'; 15 | 16 | class SVGAImage extends StatefulWidget { 17 | final SVGAAnimationController _controller; 18 | final BoxFit fit; 19 | final bool clearsAfterStop; 20 | 21 | /// Used to set the filterQuality of drawing the images inside SVGA. 22 | /// 23 | /// Defaults to [FilterQuality.low] 24 | final FilterQuality filterQuality; 25 | 26 | /// If `true`, the SVGA painter may draw beyond the expected canvas bounds 27 | /// and cause additional memory overhead. 28 | /// 29 | /// For backwards compatibility, defaults to `null`, 30 | /// which means allow drawing to overflow canvas bounds. 31 | final bool? allowDrawingOverflow; 32 | 33 | /// If `null`, the viewbox size of [MovieEntity] will be use. 34 | /// 35 | /// Defaults to null. 36 | final Size? preferredSize; 37 | const SVGAImage( 38 | this._controller, { 39 | Key? key, 40 | this.fit = BoxFit.contain, 41 | this.filterQuality = FilterQuality.low, 42 | this.allowDrawingOverflow, 43 | this.clearsAfterStop = true, 44 | this.preferredSize, 45 | }) : super(key: key); 46 | 47 | @override 48 | State createState() => _SVGAImageState(); 49 | 50 | @override 51 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 52 | super.debugFillProperties(properties); 53 | properties.add(DiagnosticsProperty('controller', _controller)); 54 | } 55 | } 56 | 57 | class SVGAAnimationController extends AnimationController { 58 | MovieEntity? _videoItem; 59 | bool _canvasNeedsClear = false; 60 | 61 | SVGAAnimationController({ 62 | required TickerProvider vsync, 63 | }) : super(vsync: vsync, duration: Duration.zero); 64 | 65 | set videoItem(MovieEntity? value) { 66 | assert(!_isDisposed, '$this has been disposed!'); 67 | if (_isDisposed) return; 68 | if (isAnimating) { 69 | stop(); 70 | } 71 | if (value == null) { 72 | clear(); 73 | } 74 | if (_videoItem != null && _videoItem!.autorelease) { 75 | _videoItem!.dispose(); 76 | } 77 | _videoItem = value; 78 | if (value != null) { 79 | final movieParams = value.params; 80 | assert( 81 | movieParams.viewBoxWidth >= 0 && 82 | movieParams.viewBoxHeight >= 0 && 83 | movieParams.frames >= 1, 84 | "Invalid SVGA file!"); 85 | int fps = movieParams.fps; 86 | // avoid dividing by 0, use 20 by default 87 | // see https://github.com/svga/SVGAPlayer-Web/blob/1c5711db068a25006316f9890b11d6666d531c39/src/videoEntity.js#L51 88 | if (fps == 0) fps = 20; 89 | duration = 90 | Duration(milliseconds: (movieParams.frames / fps * 1000).toInt()); 91 | } else { 92 | duration = Duration.zero; 93 | } 94 | // reset progress after videoitem changed 95 | reset(); 96 | } 97 | 98 | MovieEntity? get videoItem => _videoItem; 99 | 100 | /// Current drawing frame index of [videoItem], returns 0 if [videoItem] is null. 101 | int get currentFrame { 102 | final videoItem = _videoItem; 103 | if (videoItem == null) return 0; 104 | return min( 105 | videoItem.params.frames - 1, 106 | max(0, (videoItem.params.frames.toDouble() * value).toInt()), 107 | ); 108 | } 109 | 110 | /// Total frames of [videoItem], returns 0 if [videoItem] is null. 111 | int get frames { 112 | final videoItem = _videoItem; 113 | if (videoItem == null) return 0; 114 | return videoItem.params.frames; 115 | } 116 | 117 | /// mark [_SVGAPainter] needs clear 118 | void clear() { 119 | _canvasNeedsClear = true; 120 | if (!_isDisposed) notifyListeners(); 121 | } 122 | 123 | @override 124 | TickerFuture forward({double? from}) { 125 | assert(_videoItem != null, 126 | 'SVGAAnimationController.forward() called after dispose()?'); 127 | return super.forward(from: from); 128 | } 129 | 130 | bool _isDisposed = false; 131 | @override 132 | void dispose() { 133 | // auto dispose _videoItem when set null 134 | videoItem = null; 135 | _isDisposed = true; 136 | super.dispose(); 137 | } 138 | } 139 | 140 | class _SVGAImageState extends State { 141 | MovieEntity? video; 142 | @override 143 | void initState() { 144 | super.initState(); 145 | video = widget._controller.videoItem; 146 | widget._controller.addListener(_handleChange); 147 | widget._controller.addStatusListener(_handleStatusChange); 148 | } 149 | 150 | @override 151 | void didUpdateWidget(SVGAImage oldWidget) { 152 | super.didUpdateWidget(oldWidget); 153 | if (oldWidget._controller != widget._controller) { 154 | oldWidget._controller.removeListener(_handleChange); 155 | oldWidget._controller.removeStatusListener(_handleStatusChange); 156 | video = widget._controller.videoItem; 157 | widget._controller.addListener(_handleChange); 158 | widget._controller.addStatusListener(_handleStatusChange); 159 | } 160 | } 161 | 162 | void _handleChange() { 163 | if (mounted && 164 | !widget._controller._isDisposed && 165 | video != widget._controller.videoItem) { 166 | setState(() { 167 | // rebuild 168 | video = widget._controller.videoItem; 169 | }); 170 | } 171 | } 172 | 173 | void _handleStatusChange(AnimationStatus status) { 174 | if (status == AnimationStatus.completed && widget.clearsAfterStop) { 175 | widget._controller.clear(); 176 | } 177 | } 178 | 179 | @override 180 | void dispose() { 181 | video = null; 182 | widget._controller.removeListener(_handleChange); 183 | widget._controller.removeStatusListener(_handleStatusChange); 184 | super.dispose(); 185 | } 186 | 187 | @override 188 | Widget build(BuildContext context) { 189 | final video = this.video; 190 | final Size viewBoxSize; 191 | if (video == null || !video.isInitialized()) { 192 | viewBoxSize = Size.zero; 193 | } else { 194 | viewBoxSize = Size(video.params.viewBoxWidth, video.params.viewBoxHeight); 195 | } 196 | if (viewBoxSize.isEmpty) { 197 | return const SizedBox.shrink(); 198 | } 199 | // sugguest the size of CustomPaint 200 | Size preferredSize = viewBoxSize; 201 | if (widget.preferredSize != null) { 202 | preferredSize = 203 | BoxConstraints.tight(widget.preferredSize!).constrain(viewBoxSize); 204 | } 205 | return IgnorePointer( 206 | child: CustomPaint( 207 | painter: _SVGAPainter( 208 | // _SVGAPainter will auto repaint on _controller animating 209 | widget._controller, 210 | fit: widget.fit, 211 | filterQuality: widget.filterQuality, 212 | // default is allowing overflow for backward compatibility 213 | clipRect: widget.allowDrawingOverflow == false, 214 | ), 215 | size: preferredSize, 216 | ), 217 | ); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /lib/proto/svga.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: svga.proto 4 | // 5 | // @dart = 2.12 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields 7 | 8 | // ignore_for_file: UNDEFINED_SHOWN_NAME 9 | import 'dart:core' as $core; 10 | import 'package:protobuf/protobuf.dart' as $pb; 11 | 12 | class ShapeEntity_ShapeType extends $pb.ProtobufEnum { 13 | static const ShapeEntity_ShapeType SHAPE = ShapeEntity_ShapeType._( 14 | 0, 15 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 16 | ? '' 17 | : 'SHAPE'); 18 | static const ShapeEntity_ShapeType RECT = ShapeEntity_ShapeType._( 19 | 1, 20 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 21 | ? '' 22 | : 'RECT'); 23 | static const ShapeEntity_ShapeType ELLIPSE = ShapeEntity_ShapeType._( 24 | 2, 25 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 26 | ? '' 27 | : 'ELLIPSE'); 28 | static const ShapeEntity_ShapeType KEEP = ShapeEntity_ShapeType._( 29 | 3, 30 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 31 | ? '' 32 | : 'KEEP'); 33 | 34 | static const $core.List values = 35 | [ 36 | SHAPE, 37 | RECT, 38 | ELLIPSE, 39 | KEEP, 40 | ]; 41 | 42 | static final $core.Map<$core.int, ShapeEntity_ShapeType> _byValue = 43 | $pb.ProtobufEnum.initByValue(values); 44 | static ShapeEntity_ShapeType? valueOf($core.int value) => _byValue[value]; 45 | 46 | const ShapeEntity_ShapeType._($core.int v, $core.String n) : super(v, n); 47 | } 48 | 49 | class ShapeEntity_ShapeStyle_LineCap extends $pb.ProtobufEnum { 50 | static const ShapeEntity_ShapeStyle_LineCap LineCap_BUTT = 51 | ShapeEntity_ShapeStyle_LineCap._( 52 | 0, 53 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 54 | ? '' 55 | : 'LineCap_BUTT'); 56 | static const ShapeEntity_ShapeStyle_LineCap LineCap_ROUND = 57 | ShapeEntity_ShapeStyle_LineCap._( 58 | 1, 59 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 60 | ? '' 61 | : 'LineCap_ROUND'); 62 | static const ShapeEntity_ShapeStyle_LineCap LineCap_SQUARE = 63 | ShapeEntity_ShapeStyle_LineCap._( 64 | 2, 65 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 66 | ? '' 67 | : 'LineCap_SQUARE'); 68 | 69 | static const $core.List values = 70 | [ 71 | LineCap_BUTT, 72 | LineCap_ROUND, 73 | LineCap_SQUARE, 74 | ]; 75 | 76 | static final $core.Map<$core.int, ShapeEntity_ShapeStyle_LineCap> _byValue = 77 | $pb.ProtobufEnum.initByValue(values); 78 | static ShapeEntity_ShapeStyle_LineCap? valueOf($core.int value) => 79 | _byValue[value]; 80 | 81 | const ShapeEntity_ShapeStyle_LineCap._($core.int v, $core.String n) 82 | : super(v, n); 83 | } 84 | 85 | class ShapeEntity_ShapeStyle_LineJoin extends $pb.ProtobufEnum { 86 | static const ShapeEntity_ShapeStyle_LineJoin LineJoin_MITER = 87 | ShapeEntity_ShapeStyle_LineJoin._( 88 | 0, 89 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 90 | ? '' 91 | : 'LineJoin_MITER'); 92 | static const ShapeEntity_ShapeStyle_LineJoin LineJoin_ROUND = 93 | ShapeEntity_ShapeStyle_LineJoin._( 94 | 1, 95 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 96 | ? '' 97 | : 'LineJoin_ROUND'); 98 | static const ShapeEntity_ShapeStyle_LineJoin LineJoin_BEVEL = 99 | ShapeEntity_ShapeStyle_LineJoin._( 100 | 2, 101 | const $core.bool.fromEnvironment('protobuf.omit_enum_names') 102 | ? '' 103 | : 'LineJoin_BEVEL'); 104 | 105 | static const $core.List values = 106 | [ 107 | LineJoin_MITER, 108 | LineJoin_ROUND, 109 | LineJoin_BEVEL, 110 | ]; 111 | 112 | static final $core.Map<$core.int, ShapeEntity_ShapeStyle_LineJoin> _byValue = 113 | $pb.ProtobufEnum.initByValue(values); 114 | static ShapeEntity_ShapeStyle_LineJoin? valueOf($core.int value) => 115 | _byValue[value]; 116 | 117 | const ShapeEntity_ShapeStyle_LineJoin._($core.int v, $core.String n) 118 | : super(v, n); 119 | } 120 | -------------------------------------------------------------------------------- /lib/proto/svga.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: svga.proto 4 | // 5 | // @dart = 2.12 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package 7 | 8 | import 'dart:core' as $core; 9 | import 'dart:convert' as $convert; 10 | import 'dart:typed_data' as $typed_data; 11 | 12 | @$core.Deprecated('Use movieParamsDescriptor instead') 13 | const MovieParams$json = const { 14 | '1': 'MovieParams', 15 | '2': const [ 16 | const {'1': 'viewBoxWidth', '3': 1, '4': 1, '5': 2, '10': 'viewBoxWidth'}, 17 | const {'1': 'viewBoxHeight', '3': 2, '4': 1, '5': 2, '10': 'viewBoxHeight'}, 18 | const {'1': 'fps', '3': 3, '4': 1, '5': 5, '10': 'fps'}, 19 | const {'1': 'frames', '3': 4, '4': 1, '5': 5, '10': 'frames'}, 20 | ], 21 | }; 22 | 23 | /// Descriptor for `MovieParams`. Decode as a `google.protobuf.DescriptorProto`. 24 | final $typed_data.Uint8List movieParamsDescriptor = $convert.base64Decode( 25 | 'CgtNb3ZpZVBhcmFtcxIiCgx2aWV3Qm94V2lkdGgYASABKAJSDHZpZXdCb3hXaWR0aBIkCg12aWV3Qm94SGVpZ2h0GAIgASgCUg12aWV3Qm94SGVpZ2h0EhAKA2ZwcxgDIAEoBVIDZnBzEhYKBmZyYW1lcxgEIAEoBVIGZnJhbWVz'); 26 | @$core.Deprecated('Use spriteEntityDescriptor instead') 27 | const SpriteEntity$json = const { 28 | '1': 'SpriteEntity', 29 | '2': const [ 30 | const {'1': 'imageKey', '3': 1, '4': 1, '5': 9, '10': 'imageKey'}, 31 | const { 32 | '1': 'frames', 33 | '3': 2, 34 | '4': 3, 35 | '5': 11, 36 | '6': '.com.opensource.svga.FrameEntity', 37 | '10': 'frames' 38 | }, 39 | const {'1': 'matteKey', '3': 3, '4': 1, '5': 9, '10': 'matteKey'}, 40 | ], 41 | }; 42 | 43 | /// Descriptor for `SpriteEntity`. Decode as a `google.protobuf.DescriptorProto`. 44 | final $typed_data.Uint8List spriteEntityDescriptor = $convert.base64Decode( 45 | 'CgxTcHJpdGVFbnRpdHkSGgoIaW1hZ2VLZXkYASABKAlSCGltYWdlS2V5EjgKBmZyYW1lcxgCIAMoCzIgLmNvbS5vcGVuc291cmNlLnN2Z2EuRnJhbWVFbnRpdHlSBmZyYW1lcxIaCghtYXR0ZUtleRgDIAEoCVIIbWF0dGVLZXk='); 46 | @$core.Deprecated('Use audioEntityDescriptor instead') 47 | const AudioEntity$json = const { 48 | '1': 'AudioEntity', 49 | '2': const [ 50 | const {'1': 'audioKey', '3': 1, '4': 1, '5': 9, '10': 'audioKey'}, 51 | const {'1': 'startFrame', '3': 2, '4': 1, '5': 5, '10': 'startFrame'}, 52 | const {'1': 'endFrame', '3': 3, '4': 1, '5': 5, '10': 'endFrame'}, 53 | const {'1': 'startTime', '3': 4, '4': 1, '5': 5, '10': 'startTime'}, 54 | const {'1': 'totalTime', '3': 5, '4': 1, '5': 5, '10': 'totalTime'}, 55 | ], 56 | }; 57 | 58 | /// Descriptor for `AudioEntity`. Decode as a `google.protobuf.DescriptorProto`. 59 | final $typed_data.Uint8List audioEntityDescriptor = $convert.base64Decode( 60 | 'CgtBdWRpb0VudGl0eRIaCghhdWRpb0tleRgBIAEoCVIIYXVkaW9LZXkSHgoKc3RhcnRGcmFtZRgCIAEoBVIKc3RhcnRGcmFtZRIaCghlbmRGcmFtZRgDIAEoBVIIZW5kRnJhbWUSHAoJc3RhcnRUaW1lGAQgASgFUglzdGFydFRpbWUSHAoJdG90YWxUaW1lGAUgASgFUgl0b3RhbFRpbWU='); 61 | @$core.Deprecated('Use layoutDescriptor instead') 62 | const Layout$json = const { 63 | '1': 'Layout', 64 | '2': const [ 65 | const {'1': 'x', '3': 1, '4': 1, '5': 2, '10': 'x'}, 66 | const {'1': 'y', '3': 2, '4': 1, '5': 2, '10': 'y'}, 67 | const {'1': 'width', '3': 3, '4': 1, '5': 2, '10': 'width'}, 68 | const {'1': 'height', '3': 4, '4': 1, '5': 2, '10': 'height'}, 69 | ], 70 | }; 71 | 72 | /// Descriptor for `Layout`. Decode as a `google.protobuf.DescriptorProto`. 73 | final $typed_data.Uint8List layoutDescriptor = $convert.base64Decode( 74 | 'CgZMYXlvdXQSDAoBeBgBIAEoAlIBeBIMCgF5GAIgASgCUgF5EhQKBXdpZHRoGAMgASgCUgV3aWR0aBIWCgZoZWlnaHQYBCABKAJSBmhlaWdodA=='); 75 | @$core.Deprecated('Use transformDescriptor instead') 76 | const Transform$json = const { 77 | '1': 'Transform', 78 | '2': const [ 79 | const {'1': 'a', '3': 1, '4': 1, '5': 2, '10': 'a'}, 80 | const {'1': 'b', '3': 2, '4': 1, '5': 2, '10': 'b'}, 81 | const {'1': 'c', '3': 3, '4': 1, '5': 2, '10': 'c'}, 82 | const {'1': 'd', '3': 4, '4': 1, '5': 2, '10': 'd'}, 83 | const {'1': 'tx', '3': 5, '4': 1, '5': 2, '10': 'tx'}, 84 | const {'1': 'ty', '3': 6, '4': 1, '5': 2, '10': 'ty'}, 85 | ], 86 | }; 87 | 88 | /// Descriptor for `Transform`. Decode as a `google.protobuf.DescriptorProto`. 89 | final $typed_data.Uint8List transformDescriptor = $convert.base64Decode( 90 | 'CglUcmFuc2Zvcm0SDAoBYRgBIAEoAlIBYRIMCgFiGAIgASgCUgFiEgwKAWMYAyABKAJSAWMSDAoBZBgEIAEoAlIBZBIOCgJ0eBgFIAEoAlICdHgSDgoCdHkYBiABKAJSAnR5'); 91 | @$core.Deprecated('Use shapeEntityDescriptor instead') 92 | const ShapeEntity$json = const { 93 | '1': 'ShapeEntity', 94 | '2': const [ 95 | const { 96 | '1': 'type', 97 | '3': 1, 98 | '4': 1, 99 | '5': 14, 100 | '6': '.com.opensource.svga.ShapeEntity.ShapeType', 101 | '10': 'type' 102 | }, 103 | const { 104 | '1': 'shape', 105 | '3': 2, 106 | '4': 1, 107 | '5': 11, 108 | '6': '.com.opensource.svga.ShapeEntity.ShapeArgs', 109 | '9': 0, 110 | '10': 'shape' 111 | }, 112 | const { 113 | '1': 'rect', 114 | '3': 3, 115 | '4': 1, 116 | '5': 11, 117 | '6': '.com.opensource.svga.ShapeEntity.RectArgs', 118 | '9': 0, 119 | '10': 'rect' 120 | }, 121 | const { 122 | '1': 'ellipse', 123 | '3': 4, 124 | '4': 1, 125 | '5': 11, 126 | '6': '.com.opensource.svga.ShapeEntity.EllipseArgs', 127 | '9': 0, 128 | '10': 'ellipse' 129 | }, 130 | const { 131 | '1': 'styles', 132 | '3': 10, 133 | '4': 1, 134 | '5': 11, 135 | '6': '.com.opensource.svga.ShapeEntity.ShapeStyle', 136 | '10': 'styles' 137 | }, 138 | const { 139 | '1': 'transform', 140 | '3': 11, 141 | '4': 1, 142 | '5': 11, 143 | '6': '.com.opensource.svga.Transform', 144 | '10': 'transform' 145 | }, 146 | ], 147 | '3': const [ 148 | ShapeEntity_ShapeArgs$json, 149 | ShapeEntity_RectArgs$json, 150 | ShapeEntity_EllipseArgs$json, 151 | ShapeEntity_ShapeStyle$json 152 | ], 153 | '4': const [ShapeEntity_ShapeType$json], 154 | '8': const [ 155 | const {'1': 'args'}, 156 | ], 157 | }; 158 | 159 | @$core.Deprecated('Use shapeEntityDescriptor instead') 160 | const ShapeEntity_ShapeArgs$json = const { 161 | '1': 'ShapeArgs', 162 | '2': const [ 163 | const {'1': 'd', '3': 1, '4': 1, '5': 9, '10': 'd'}, 164 | ], 165 | }; 166 | 167 | @$core.Deprecated('Use shapeEntityDescriptor instead') 168 | const ShapeEntity_RectArgs$json = const { 169 | '1': 'RectArgs', 170 | '2': const [ 171 | const {'1': 'x', '3': 1, '4': 1, '5': 2, '10': 'x'}, 172 | const {'1': 'y', '3': 2, '4': 1, '5': 2, '10': 'y'}, 173 | const {'1': 'width', '3': 3, '4': 1, '5': 2, '10': 'width'}, 174 | const {'1': 'height', '3': 4, '4': 1, '5': 2, '10': 'height'}, 175 | const {'1': 'cornerRadius', '3': 5, '4': 1, '5': 2, '10': 'cornerRadius'}, 176 | ], 177 | }; 178 | 179 | @$core.Deprecated('Use shapeEntityDescriptor instead') 180 | const ShapeEntity_EllipseArgs$json = const { 181 | '1': 'EllipseArgs', 182 | '2': const [ 183 | const {'1': 'x', '3': 1, '4': 1, '5': 2, '10': 'x'}, 184 | const {'1': 'y', '3': 2, '4': 1, '5': 2, '10': 'y'}, 185 | const {'1': 'radiusX', '3': 3, '4': 1, '5': 2, '10': 'radiusX'}, 186 | const {'1': 'radiusY', '3': 4, '4': 1, '5': 2, '10': 'radiusY'}, 187 | ], 188 | }; 189 | 190 | @$core.Deprecated('Use shapeEntityDescriptor instead') 191 | const ShapeEntity_ShapeStyle$json = const { 192 | '1': 'ShapeStyle', 193 | '2': const [ 194 | const { 195 | '1': 'fill', 196 | '3': 1, 197 | '4': 1, 198 | '5': 11, 199 | '6': '.com.opensource.svga.ShapeEntity.ShapeStyle.RGBAColor', 200 | '10': 'fill' 201 | }, 202 | const { 203 | '1': 'stroke', 204 | '3': 2, 205 | '4': 1, 206 | '5': 11, 207 | '6': '.com.opensource.svga.ShapeEntity.ShapeStyle.RGBAColor', 208 | '10': 'stroke' 209 | }, 210 | const {'1': 'strokeWidth', '3': 3, '4': 1, '5': 2, '10': 'strokeWidth'}, 211 | const { 212 | '1': 'lineCap', 213 | '3': 4, 214 | '4': 1, 215 | '5': 14, 216 | '6': '.com.opensource.svga.ShapeEntity.ShapeStyle.LineCap', 217 | '10': 'lineCap' 218 | }, 219 | const { 220 | '1': 'lineJoin', 221 | '3': 5, 222 | '4': 1, 223 | '5': 14, 224 | '6': '.com.opensource.svga.ShapeEntity.ShapeStyle.LineJoin', 225 | '10': 'lineJoin' 226 | }, 227 | const {'1': 'miterLimit', '3': 6, '4': 1, '5': 2, '10': 'miterLimit'}, 228 | const {'1': 'lineDashI', '3': 7, '4': 1, '5': 2, '10': 'lineDashI'}, 229 | const {'1': 'lineDashII', '3': 8, '4': 1, '5': 2, '10': 'lineDashII'}, 230 | const {'1': 'lineDashIII', '3': 9, '4': 1, '5': 2, '10': 'lineDashIII'}, 231 | ], 232 | '3': const [ShapeEntity_ShapeStyle_RGBAColor$json], 233 | '4': const [ 234 | ShapeEntity_ShapeStyle_LineCap$json, 235 | ShapeEntity_ShapeStyle_LineJoin$json 236 | ], 237 | }; 238 | 239 | @$core.Deprecated('Use shapeEntityDescriptor instead') 240 | const ShapeEntity_ShapeStyle_RGBAColor$json = const { 241 | '1': 'RGBAColor', 242 | '2': const [ 243 | const {'1': 'r', '3': 1, '4': 1, '5': 2, '10': 'r'}, 244 | const {'1': 'g', '3': 2, '4': 1, '5': 2, '10': 'g'}, 245 | const {'1': 'b', '3': 3, '4': 1, '5': 2, '10': 'b'}, 246 | const {'1': 'a', '3': 4, '4': 1, '5': 2, '10': 'a'}, 247 | ], 248 | }; 249 | 250 | @$core.Deprecated('Use shapeEntityDescriptor instead') 251 | const ShapeEntity_ShapeStyle_LineCap$json = const { 252 | '1': 'LineCap', 253 | '2': const [ 254 | const {'1': 'LineCap_BUTT', '2': 0}, 255 | const {'1': 'LineCap_ROUND', '2': 1}, 256 | const {'1': 'LineCap_SQUARE', '2': 2}, 257 | ], 258 | }; 259 | 260 | @$core.Deprecated('Use shapeEntityDescriptor instead') 261 | const ShapeEntity_ShapeStyle_LineJoin$json = const { 262 | '1': 'LineJoin', 263 | '2': const [ 264 | const {'1': 'LineJoin_MITER', '2': 0}, 265 | const {'1': 'LineJoin_ROUND', '2': 1}, 266 | const {'1': 'LineJoin_BEVEL', '2': 2}, 267 | ], 268 | }; 269 | 270 | @$core.Deprecated('Use shapeEntityDescriptor instead') 271 | const ShapeEntity_ShapeType$json = const { 272 | '1': 'ShapeType', 273 | '2': const [ 274 | const {'1': 'SHAPE', '2': 0}, 275 | const {'1': 'RECT', '2': 1}, 276 | const {'1': 'ELLIPSE', '2': 2}, 277 | const {'1': 'KEEP', '2': 3}, 278 | ], 279 | }; 280 | 281 | /// Descriptor for `ShapeEntity`. Decode as a `google.protobuf.DescriptorProto`. 282 | final $typed_data.Uint8List shapeEntityDescriptor = $convert.base64Decode( 283 | 'CgtTaGFwZUVudGl0eRI+CgR0eXBlGAEgASgOMiouY29tLm9wZW5zb3VyY2Uuc3ZnYS5TaGFwZUVudGl0eS5TaGFwZVR5cGVSBHR5cGUSQgoFc2hhcGUYAiABKAsyKi5jb20ub3BlbnNvdXJjZS5zdmdhLlNoYXBlRW50aXR5LlNoYXBlQXJnc0gAUgVzaGFwZRI/CgRyZWN0GAMgASgLMikuY29tLm9wZW5zb3VyY2Uuc3ZnYS5TaGFwZUVudGl0eS5SZWN0QXJnc0gAUgRyZWN0EkgKB2VsbGlwc2UYBCABKAsyLC5jb20ub3BlbnNvdXJjZS5zdmdhLlNoYXBlRW50aXR5LkVsbGlwc2VBcmdzSABSB2VsbGlwc2USQwoGc3R5bGVzGAogASgLMisuY29tLm9wZW5zb3VyY2Uuc3ZnYS5TaGFwZUVudGl0eS5TaGFwZVN0eWxlUgZzdHlsZXMSPAoJdHJhbnNmb3JtGAsgASgLMh4uY29tLm9wZW5zb3VyY2Uuc3ZnYS5UcmFuc2Zvcm1SCXRyYW5zZm9ybRoZCglTaGFwZUFyZ3MSDAoBZBgBIAEoCVIBZBp4CghSZWN0QXJncxIMCgF4GAEgASgCUgF4EgwKAXkYAiABKAJSAXkSFAoFd2lkdGgYAyABKAJSBXdpZHRoEhYKBmhlaWdodBgEIAEoAlIGaGVpZ2h0EiIKDGNvcm5lclJhZGl1cxgFIAEoAlIMY29ybmVyUmFkaXVzGl0KC0VsbGlwc2VBcmdzEgwKAXgYASABKAJSAXgSDAoBeRgCIAEoAlIBeRIYCgdyYWRpdXNYGAMgASgCUgdyYWRpdXNYEhgKB3JhZGl1c1kYBCABKAJSB3JhZGl1c1kaugUKClNoYXBlU3R5bGUSSQoEZmlsbBgBIAEoCzI1LmNvbS5vcGVuc291cmNlLnN2Z2EuU2hhcGVFbnRpdHkuU2hhcGVTdHlsZS5SR0JBQ29sb3JSBGZpbGwSTQoGc3Ryb2tlGAIgASgLMjUuY29tLm9wZW5zb3VyY2Uuc3ZnYS5TaGFwZUVudGl0eS5TaGFwZVN0eWxlLlJHQkFDb2xvclIGc3Ryb2tlEiAKC3N0cm9rZVdpZHRoGAMgASgCUgtzdHJva2VXaWR0aBJNCgdsaW5lQ2FwGAQgASgOMjMuY29tLm9wZW5zb3VyY2Uuc3ZnYS5TaGFwZUVudGl0eS5TaGFwZVN0eWxlLkxpbmVDYXBSB2xpbmVDYXASUAoIbGluZUpvaW4YBSABKA4yNC5jb20ub3BlbnNvdXJjZS5zdmdhLlNoYXBlRW50aXR5LlNoYXBlU3R5bGUuTGluZUpvaW5SCGxpbmVKb2luEh4KCm1pdGVyTGltaXQYBiABKAJSCm1pdGVyTGltaXQSHAoJbGluZURhc2hJGAcgASgCUglsaW5lRGFzaEkSHgoKbGluZURhc2hJSRgIIAEoAlIKbGluZURhc2hJSRIgCgtsaW5lRGFzaElJSRgJIAEoAlILbGluZURhc2hJSUkaQwoJUkdCQUNvbG9yEgwKAXIYASABKAJSAXISDAoBZxgCIAEoAlIBZxIMCgFiGAMgASgCUgFiEgwKAWEYBCABKAJSAWEiQgoHTGluZUNhcBIQCgxMaW5lQ2FwX0JVVFQQABIRCg1MaW5lQ2FwX1JPVU5EEAESEgoOTGluZUNhcF9TUVVBUkUQAiJGCghMaW5lSm9pbhISCg5MaW5lSm9pbl9NSVRFUhAAEhIKDkxpbmVKb2luX1JPVU5EEAESEgoOTGluZUpvaW5fQkVWRUwQAiI3CglTaGFwZVR5cGUSCQoFU0hBUEUQABIICgRSRUNUEAESCwoHRUxMSVBTRRACEggKBEtFRVAQA0IGCgRhcmdz'); 284 | @$core.Deprecated('Use frameEntityDescriptor instead') 285 | const FrameEntity$json = const { 286 | '1': 'FrameEntity', 287 | '2': const [ 288 | const {'1': 'alpha', '3': 1, '4': 1, '5': 2, '10': 'alpha'}, 289 | const { 290 | '1': 'layout', 291 | '3': 2, 292 | '4': 1, 293 | '5': 11, 294 | '6': '.com.opensource.svga.Layout', 295 | '10': 'layout' 296 | }, 297 | const { 298 | '1': 'transform', 299 | '3': 3, 300 | '4': 1, 301 | '5': 11, 302 | '6': '.com.opensource.svga.Transform', 303 | '10': 'transform' 304 | }, 305 | const {'1': 'clipPath', '3': 4, '4': 1, '5': 9, '10': 'clipPath'}, 306 | const { 307 | '1': 'shapes', 308 | '3': 5, 309 | '4': 3, 310 | '5': 11, 311 | '6': '.com.opensource.svga.ShapeEntity', 312 | '10': 'shapes' 313 | }, 314 | ], 315 | }; 316 | 317 | /// Descriptor for `FrameEntity`. Decode as a `google.protobuf.DescriptorProto`. 318 | final $typed_data.Uint8List frameEntityDescriptor = $convert.base64Decode( 319 | 'CgtGcmFtZUVudGl0eRIUCgVhbHBoYRgBIAEoAlIFYWxwaGESMwoGbGF5b3V0GAIgASgLMhsuY29tLm9wZW5zb3VyY2Uuc3ZnYS5MYXlvdXRSBmxheW91dBI8Cgl0cmFuc2Zvcm0YAyABKAsyHi5jb20ub3BlbnNvdXJjZS5zdmdhLlRyYW5zZm9ybVIJdHJhbnNmb3JtEhoKCGNsaXBQYXRoGAQgASgJUghjbGlwUGF0aBI4CgZzaGFwZXMYBSADKAsyIC5jb20ub3BlbnNvdXJjZS5zdmdhLlNoYXBlRW50aXR5UgZzaGFwZXM='); 320 | @$core.Deprecated('Use movieEntityDescriptor instead') 321 | const MovieEntity$json = const { 322 | '1': 'MovieEntity', 323 | '2': const [ 324 | const {'1': 'version', '3': 1, '4': 1, '5': 9, '10': 'version'}, 325 | const { 326 | '1': 'params', 327 | '3': 2, 328 | '4': 1, 329 | '5': 11, 330 | '6': '.com.opensource.svga.MovieParams', 331 | '10': 'params' 332 | }, 333 | const { 334 | '1': 'images', 335 | '3': 3, 336 | '4': 3, 337 | '5': 11, 338 | '6': '.com.opensource.svga.MovieEntity.ImagesEntry', 339 | '10': 'images' 340 | }, 341 | const { 342 | '1': 'sprites', 343 | '3': 4, 344 | '4': 3, 345 | '5': 11, 346 | '6': '.com.opensource.svga.SpriteEntity', 347 | '10': 'sprites' 348 | }, 349 | const { 350 | '1': 'audios', 351 | '3': 5, 352 | '4': 3, 353 | '5': 11, 354 | '6': '.com.opensource.svga.AudioEntity', 355 | '10': 'audios' 356 | }, 357 | ], 358 | '3': const [MovieEntity_ImagesEntry$json], 359 | }; 360 | 361 | @$core.Deprecated('Use movieEntityDescriptor instead') 362 | const MovieEntity_ImagesEntry$json = const { 363 | '1': 'ImagesEntry', 364 | '2': const [ 365 | const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, 366 | const {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, 367 | ], 368 | '7': const {'7': true}, 369 | }; 370 | 371 | /// Descriptor for `MovieEntity`. Decode as a `google.protobuf.DescriptorProto`. 372 | final $typed_data.Uint8List movieEntityDescriptor = $convert.base64Decode( 373 | 'CgtNb3ZpZUVudGl0eRIYCgd2ZXJzaW9uGAEgASgJUgd2ZXJzaW9uEjgKBnBhcmFtcxgCIAEoCzIgLmNvbS5vcGVuc291cmNlLnN2Z2EuTW92aWVQYXJhbXNSBnBhcmFtcxJECgZpbWFnZXMYAyADKAsyLC5jb20ub3BlbnNvdXJjZS5zdmdhLk1vdmllRW50aXR5LkltYWdlc0VudHJ5UgZpbWFnZXMSOwoHc3ByaXRlcxgEIAMoCzIhLmNvbS5vcGVuc291cmNlLnN2Z2EuU3ByaXRlRW50aXR5UgdzcHJpdGVzEjgKBmF1ZGlvcxgFIAMoCzIgLmNvbS5vcGVuc291cmNlLnN2Z2EuQXVkaW9FbnRpdHlSBmF1ZGlvcxo5CgtJbWFnZXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoDFIFdmFsdWU6AjgB'); 374 | -------------------------------------------------------------------------------- /lib/proto/svga.pbserver.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: svga.proto 4 | // 5 | // @dart = 2.12 6 | // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package 7 | 8 | export 'svga.pb.dart'; 9 | -------------------------------------------------------------------------------- /lib/simple_player.dart: -------------------------------------------------------------------------------- 1 | part of svgaplayer_flutter_player; 2 | 3 | class SVGASimpleImage extends StatefulWidget { 4 | final String? resUrl; 5 | final String? assetsName; 6 | 7 | const SVGASimpleImage({Key? key, this.resUrl, this.assetsName}) 8 | : super(key: key); 9 | 10 | @override 11 | State createState() { 12 | return _SVGASimpleImageState(); 13 | } 14 | } 15 | 16 | class _SVGASimpleImageState extends State 17 | with SingleTickerProviderStateMixin { 18 | SVGAAnimationController? animationController; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | animationController = SVGAAnimationController(vsync: this); 24 | _tryDecodeSvga(); 25 | } 26 | 27 | @override 28 | void didUpdateWidget(covariant SVGASimpleImage oldWidget) { 29 | super.didUpdateWidget(oldWidget); 30 | if (oldWidget.resUrl != widget.resUrl || oldWidget.assetsName != widget.assetsName) { 31 | _tryDecodeSvga(); 32 | } 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | if (animationController == null) { 38 | return Container(); 39 | } 40 | return SVGAImage(animationController!); 41 | } 42 | 43 | @override 44 | void dispose() { 45 | animationController?.dispose(); 46 | animationController = null; 47 | super.dispose(); 48 | } 49 | 50 | void _tryDecodeSvga() { 51 | Future decode; 52 | if (widget.resUrl != null) { 53 | decode = SVGAParser.shared.decodeFromURL(widget.resUrl!); 54 | } else if (widget.assetsName != null) { 55 | decode = SVGAParser.shared.decodeFromAssets(widget.assetsName!); 56 | } else { 57 | return; 58 | } 59 | decode.then((videoItem) { 60 | if (mounted && animationController != null) { 61 | animationController! 62 | ..videoItem = videoItem 63 | ..repeat(); 64 | } else { 65 | videoItem.dispose(); 66 | } 67 | }).catchError((e, stack) { 68 | FlutterError.reportError(FlutterErrorDetails( 69 | exception: e, 70 | stack: stack, 71 | library: 'svga library', 72 | informationCollector: () => [ 73 | if (widget.resUrl != null) StringProperty('resUrl', widget.resUrl), 74 | if (widget.assetsName != null) 75 | StringProperty('assetsName', widget.assetsName), 76 | ], 77 | )); 78 | }); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/svgaplayer_flutter.dart: -------------------------------------------------------------------------------- 1 | export 'parser.dart'; 2 | export 'player.dart'; 3 | export 'proto/svga.pb.dart' show MovieEntity, MovieParams, ShapeEntity, FrameEntity; 4 | export 'dynamic_entity.dart'; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1" 3 | } -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: "direct main" 6 | description: 7 | name: archive 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "3.1.2" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "2.9.0" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.2.1" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.1.1" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.16.0" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "3.0.1" 60 | fake_async: 61 | dependency: transitive 62 | description: 63 | name: fake_async 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "1.3.1" 67 | fixnum: 68 | dependency: transitive 69 | description: 70 | name: fixnum 71 | url: "https://pub.flutter-io.cn" 72 | source: hosted 73 | version: "1.0.0" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_lints: 80 | dependency: "direct dev" 81 | description: 82 | name: flutter_lints 83 | url: "https://pub.flutter-io.cn" 84 | source: hosted 85 | version: "1.0.4" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | http: 92 | dependency: "direct main" 93 | description: 94 | name: http 95 | url: "https://pub.flutter-io.cn" 96 | source: hosted 97 | version: "0.13.3" 98 | http_parser: 99 | dependency: transitive 100 | description: 101 | name: http_parser 102 | url: "https://pub.flutter-io.cn" 103 | source: hosted 104 | version: "4.0.0" 105 | lints: 106 | dependency: transitive 107 | description: 108 | name: lints 109 | url: "https://pub.flutter-io.cn" 110 | source: hosted 111 | version: "1.0.1" 112 | matcher: 113 | dependency: transitive 114 | description: 115 | name: matcher 116 | url: "https://pub.flutter-io.cn" 117 | source: hosted 118 | version: "0.12.12" 119 | material_color_utilities: 120 | dependency: transitive 121 | description: 122 | name: material_color_utilities 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "0.1.5" 126 | meta: 127 | dependency: transitive 128 | description: 129 | name: meta 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "1.8.0" 133 | path: 134 | dependency: transitive 135 | description: 136 | name: path 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "1.8.2" 140 | path_drawing: 141 | dependency: "direct main" 142 | description: 143 | name: path_drawing 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "1.0.1" 147 | path_parsing: 148 | dependency: transitive 149 | description: 150 | name: path_parsing 151 | url: "https://pub.flutter-io.cn" 152 | source: hosted 153 | version: "1.0.1" 154 | pedantic: 155 | dependency: transitive 156 | description: 157 | name: pedantic 158 | url: "https://pub.flutter-io.cn" 159 | source: hosted 160 | version: "1.11.0" 161 | protobuf: 162 | dependency: "direct main" 163 | description: 164 | name: protobuf 165 | url: "https://pub.flutter-io.cn" 166 | source: hosted 167 | version: "2.0.0" 168 | sky_engine: 169 | dependency: transitive 170 | description: flutter 171 | source: sdk 172 | version: "0.0.99" 173 | source_span: 174 | dependency: transitive 175 | description: 176 | name: source_span 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "1.9.0" 180 | stack_trace: 181 | dependency: transitive 182 | description: 183 | name: stack_trace 184 | url: "https://pub.flutter-io.cn" 185 | source: hosted 186 | version: "1.10.0" 187 | stream_channel: 188 | dependency: transitive 189 | description: 190 | name: stream_channel 191 | url: "https://pub.flutter-io.cn" 192 | source: hosted 193 | version: "2.1.0" 194 | string_scanner: 195 | dependency: transitive 196 | description: 197 | name: string_scanner 198 | url: "https://pub.flutter-io.cn" 199 | source: hosted 200 | version: "1.1.1" 201 | term_glyph: 202 | dependency: transitive 203 | description: 204 | name: term_glyph 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "1.2.1" 208 | test_api: 209 | dependency: transitive 210 | description: 211 | name: test_api 212 | url: "https://pub.flutter-io.cn" 213 | source: hosted 214 | version: "0.4.12" 215 | typed_data: 216 | dependency: transitive 217 | description: 218 | name: typed_data 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "1.3.0" 222 | vector_math: 223 | dependency: transitive 224 | description: 225 | name: vector_math 226 | url: "https://pub.flutter-io.cn" 227 | source: hosted 228 | version: "2.1.2" 229 | sdks: 230 | dart: ">=2.17.0-0 <3.0.0" 231 | flutter: ">=1.24.0-7.0" 232 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: svgaplayer_flutter 2 | description: The SVGAPlayer implementation of Flutter using CustomPainter. 3 | version: 2.2.0 4 | homepage: https://github.com/yyued/SVGAPlayer-Flutter 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | protobuf: ^2.0.0 13 | http: ^0.13.3 14 | path_drawing: ^1.0.0 15 | archive: ^3.1.2 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | flutter_lints: ^1.0.4 21 | -------------------------------------------------------------------------------- /svgaplayer_flutter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/svgaplayer_flutter_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('svgaplayer_flutter'); 6 | 7 | setUp(() { 8 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 9 | return '42'; 10 | }); 11 | }); 12 | 13 | tearDown(() { 14 | channel.setMockMethodCallHandler(null); 15 | }); 16 | } 17 | --------------------------------------------------------------------------------