Browse Source

[flutter] Backport to 4.1.

Mario Zechner 2 years ago
parent
commit
8e16576e94
100 changed files with 4004 additions and 0 deletions
  1. 30 0
      spine-flutter/.gitignore
  2. 42 0
      spine-flutter/.metadata
  3. 3 0
      spine-flutter/.pubignore
  4. 34 0
      spine-flutter/CHANGELOG.md
  5. 26 0
      spine-flutter/LICENSE
  6. 77 0
      spine-flutter/README.md
  7. 4 0
      spine-flutter/analysis_options.yaml
  8. 9 0
      spine-flutter/android/.gitignore
  9. 59 0
      spine-flutter/android/build.gradle
  10. 1 0
      spine-flutter/android/settings.gradle
  11. 3 0
      spine-flutter/android/src/main/AndroidManifest.xml
  12. 25 0
      spine-flutter/compile-wasm.sh
  13. 46 0
      spine-flutter/example/.gitignore
  14. 16 0
      spine-flutter/example/README.md
  15. 29 0
      spine-flutter/example/analysis_options.yaml
  16. 13 0
      spine-flutter/example/android/.gitignore
  17. 71 0
      spine-flutter/example/android/app/build.gradle
  18. 8 0
      spine-flutter/example/android/app/src/debug/AndroidManifest.xml
  19. 34 0
      spine-flutter/example/android/app/src/main/AndroidManifest.xml
  20. 6 0
      spine-flutter/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
  21. 6 0
      spine-flutter/example/android/app/src/main/kotlin/com/example/spine_flutter_example/MainActivity.kt
  22. 12 0
      spine-flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml
  23. 12 0
      spine-flutter/example/android/app/src/main/res/drawable/launch_background.xml
  24. BIN
      spine-flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  25. BIN
      spine-flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  26. BIN
      spine-flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  27. BIN
      spine-flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  28. BIN
      spine-flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  29. 18 0
      spine-flutter/example/android/app/src/main/res/values-night/styles.xml
  30. 18 0
      spine-flutter/example/android/app/src/main/res/values/styles.xml
  31. 8 0
      spine-flutter/example/android/app/src/profile/AndroidManifest.xml
  32. 31 0
      spine-flutter/example/android/build.gradle
  33. 3 0
      spine-flutter/example/android/gradle.properties
  34. 6 0
      spine-flutter/example/android/gradle/wrapper/gradle-wrapper.properties
  35. 11 0
      spine-flutter/example/android/settings.gradle
  36. 409 0
      spine-flutter/example/assets/chibi/chibi-stickers.atlas
  37. BIN
      spine-flutter/example/assets/chibi/chibi-stickers.skel
  38. BIN
      spine-flutter/example/assets/chibi/chibi-stickers.webp
  39. BIN
      spine-flutter/example/assets/dragon-ess.skel
  40. 123 0
      spine-flutter/example/assets/dragon.atlas
  41. BIN
      spine-flutter/example/assets/dragon.png
  42. BIN
      spine-flutter/example/assets/dragon_2.png
  43. BIN
      spine-flutter/example/assets/dragon_3.png
  44. BIN
      spine-flutter/example/assets/dragon_4.png
  45. BIN
      spine-flutter/example/assets/dragon_5.png
  46. BIN
      spine-flutter/example/assets/dragon_6.png
  47. BIN
      spine-flutter/example/assets/mix-and-match-pro.skel
  48. 362 0
      spine-flutter/example/assets/mix-and-match.atlas
  49. BIN
      spine-flutter/example/assets/mix-and-match.png
  50. 541 0
      spine-flutter/example/assets/spineboy-pro.json
  51. BIN
      spine-flutter/example/assets/spineboy-pro.skel
  52. 101 0
      spine-flutter/example/assets/spineboy.atlas
  53. BIN
      spine-flutter/example/assets/spineboy.png
  54. 34 0
      spine-flutter/example/ios/.gitignore
  55. 26 0
      spine-flutter/example/ios/Flutter/AppFrameworkInfo.plist
  56. 2 0
      spine-flutter/example/ios/Flutter/Debug.xcconfig
  57. 2 0
      spine-flutter/example/ios/Flutter/Release.xcconfig
  58. 41 0
      spine-flutter/example/ios/Podfile
  59. 22 0
      spine-flutter/example/ios/Podfile.lock
  60. 551 0
      spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj
  61. 7 0
      spine-flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  62. 10 0
      spine-flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata
  63. 13 0
      spine-flutter/example/ios/Runner/AppDelegate.swift
  64. 122 0
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
  65. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  66. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  67. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  68. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  69. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  70. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  71. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  72. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  73. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  74. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  75. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  76. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  77. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  78. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  79. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]
  80. 23 0
      spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
  81. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
  82. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/[email protected]
  83. BIN
      spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/[email protected]
  84. 5 0
      spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
  85. 37 0
      spine-flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
  86. 26 0
      spine-flutter/example/ios/Runner/Base.lproj/Main.storyboard
  87. 51 0
      spine-flutter/example/ios/Runner/Info.plist
  88. 1 0
      spine-flutter/example/ios/Runner/Runner-Bridging-Header.h
  89. 30 0
      spine-flutter/example/lib/animated_login.dart
  90. 39 0
      spine-flutter/example/lib/animation_state_events.dart
  91. 23 0
      spine-flutter/example/lib/debug_rendering.dart
  92. 104 0
      spine-flutter/example/lib/dress_up.dart
  93. 151 0
      spine-flutter/example/lib/flame_example.dart
  94. 52 0
      spine-flutter/example/lib/ik_following.dart
  95. 139 0
      spine-flutter/example/lib/main.dart
  96. 49 0
      spine-flutter/example/lib/pause_play_animation.dart
  97. 20 0
      spine-flutter/example/lib/simple_animation.dart
  98. 1 0
      spine-flutter/example/linux/.gitignore
  99. 138 0
      spine-flutter/example/linux/CMakeLists.txt
  100. 88 0
      spine-flutter/example/linux/flutter/CMakeLists.txt

+ 30 - 0
spine-flutter/.gitignore

@@ -0,0 +1,30 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.packages
+build/

+ 42 - 0
spine-flutter/.metadata

@@ -0,0 +1,42 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+  revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+  channel: stable
+
+project_type: plugin_ffi
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+    - platform: android
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+    - platform: ios
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+    - platform: linux
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+    - platform: macos
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+    - platform: windows
+      create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+      base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 3 - 0
spine-flutter/.pubignore

@@ -0,0 +1,3 @@
+!src/spine-cpp
+!ios/Classes/spine-cpp
+!macos/Classes/spine-cpp

+ 34 - 0
spine-flutter/CHANGELOG.md

@@ -0,0 +1,34 @@
+# 4.2.11
+* Update README.md with setup and development instructions.
+
+# 4.2.10
+* Update README.md to point to Junji's Dart-only Spine runtime.
+
+# 4.2.9
+* Fix atlas parsing.
+
+# 4.2.8
+* Change reversed positional argument order in `SpineWidget` constructors.
+
+# 4.2.7
+* Change package name from  `esotericsoftware_spine_flutter` to `spine_flutter`.
+
+# 4.2.6
+* Fix analyzer errors, fix code style to adhere to Dart standards.
+
+# 4.2.5
+* Implemented batching of render commands, reducing the number of draw calls. 60/120fps for 100 Spineboys on all platforms.
+
+# 0.0.4
+* Clean-up `fromAsset()` factory methods so the atlas comes before skeleton data file name.
+* Rename `Vector2` to `Vec2`.
+* Make the bundle configurable in `SpineWidget.asset()`.
+
+# 0.0.3
+* Lower macOS deployment target to 10.11.
+
+# 0.0.2
+* Fix package name in build system `spine_flutter` > `esotericsoftware_spine_flutter`.
+
+# 0.0.1
+Initial test release.

+ 26 - 0
spine-flutter/LICENSE

@@ -0,0 +1,26 @@
+Spine Runtimes License Agreement
+Last updated September 24, 2021. Replaces all prior versions.
+
+Copyright (c) 2013-2021, Esoteric Software LLC
+
+Integration of the Spine Runtimes into software or otherwise creating
+derivative works of the Spine Runtimes is permitted under the terms and
+conditions of Section 2 of the Spine Editor License Agreement:
+http://esotericsoftware.com/spine-editor-license
+
+Otherwise, it is permitted to integrate the Spine Runtimes into software
+or otherwise create derivative works of the Spine Runtimes (collectively,
+"Products"), provided that each user of the Products must obtain their own
+Spine Editor license and redistribution of the Products in any form must
+include this license and copyright notice.
+
+THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 77 - 0
spine-flutter/README.md

@@ -0,0 +1,77 @@
+# spine-flutter
+
+The spine-godot runtime provides functionality to load, manipulate and render [Spine](https://esotericsoftware.com) skeletal animation data using [Flutter](https://flutter.dev/). spine-flutter is based on [spine-cpp](../spine-cpp) and supports desktop and mobile Flutter deployment targets. spine-flutter does not support Flutter's web deployment target.
+
+# See the [spine-flutter documentation](https://esotericsoftware.com/spine-flutter) for in-depth information.
+
+The `spine_flutter` package name was previously used to publish the [Spine Flutter Runtime in plain Dart](https://github.com/jtakakura/spine_flutter/) by Junji Takakura. Junji has kindly transferred the package name to us and is now publishing his Dart-only Spine Flutter Runtime under the package name [spine_flutter_dart](https://pub.dev/packages/spine_flutter_dart).
+
+## Licensing
+
+You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge.
+
+You can integrate the Spine Runtimes into your software free of charge, but users of your software must have their own [Spine license](https://esotericsoftware.com/spine-purchase). Please make your users aware of this requirement! This option is often chosen by those making development tools, such as an SDK, game toolkit, or software library.
+
+In order to distribute your software containing the Spine Runtimes to others that don't have a Spine license, you need a [Spine license](https://esotericsoftware.com/spine-purchase) at the time of integration. Then you can distribute your software containing the Spine Runtimes however you like, provided others don't modify it or use it to create new software. If others want to do that, they'll need their own Spine license.
+
+For the official legal terms governing the Spine Runtimes, please read the [Spine Runtimes License Agreement](https://esotericsoftware.com/spine-runtimes-license) and Section 2 of the [Spine Editor License Agreement](https://esotericsoftware.com/spine-editor-license#s2).
+
+## Spine version
+
+spine-flutter works with data exported from Spine 4.2.xx.
+
+spine-flutter supports all Spine features, except two-color tinting and the screen blend mode.
+
+## Supported platforms
+The spine-flutter runtime works on desktop, mobile and web. Web deployment requires canvaskit, which will add about 2mb of dependencies to your web deployment. You can compile your app for web with Canvaskit like this:
+
+```
+flutter build web --web-renderer canvaskit
+```
+
+## Setup
+To add `spine_flutter` to your Flutter project, add the following dependency to your `pubspec.yaml` file:
+
+```yaml
+dependencies:
+  ...
+  spine_flutter: ^4.2.11
+```
+
+In your `main()`, add these two lines in the beginning to initialize the Spine Flutter runtime:
+
+```dart
+void main() {
+    WidgetsFlutterBinding.ensureInitialized();
+    await initSpineFlutter(enableMemoryDebugging: false);
+    ...
+}
+```
+
+## Example
+If you have pulled the `spine_flutter` package from [pub.dev](https://pub.dev) directly, you can run the example in the `example/` folder as is:
+
+```bash
+cd path/to/downloaded/spine_flutter
+cd example
+flutter run
+```
+
+Otherwise you can run the example like this:
+
+1. install the [Flutter SDK](https://docs.flutter.dev/get-started/install), then run `flutter doctor` which will instruct you what other dependencies to install.
+2. Clone this repository `git clone https://github.com/esotericsoftware/spine-runtimes`
+3. Run `setup.sh` in the `spine-flutter/` folder. On Windows, you can use [Git Bash](https://gitforwindows.org/) included in Git for Window to run the `setup.sh` Bash script.
+
+You can then open `spine-flutter` in an IDE or editor of your choice that supports Flutter, like [IntelliJ IDEA/Android Studio](https://docs.flutter.dev/get-started/editor?tab=androidstudio) or [Visual Studio Code](https://docs.flutter.dev/get-started/editor?tab=vscode) to inspect and run the example. 
+
+Alternatively, you can run the example from the [command line](https://docs.flutter.dev/get-started/test-drive?tab=terminal).
+
+## Development
+If all you modify are the Dart sources of the plugin, then the development setup is the same as the setup described under "Example" above.
+
+If you need to work on the `dart:ffi` bindings for `spine-cpp`, you will also need to install [Emscripten](https://emscripten.org/docs/getting_started/downloads.html).
+
+To generate the bindings based on the `src/spine_flutter.h` header, run `dart run ffigen --config ffigen.yaml`. After the bindings have been generated, you must replace the line `import 'dart:ffi' as ffi;` with `import 'ffi_proxy.dart' as ffi;` in the file `src/spine_flutter_bindings_generated.dart`. Otherwise the bindings will not compile for the web.
+
+If you made changes to `spine-cpp` or the source files in `src/`, you must run `compile-wasm.sh`. This will compile `spine-cpp` and the bindings for the Web and place updated versions of `libspine_flutter.js` and `libspine_flutter.wasm` in the `lib/assets/` folder. For web builds, the `initSpineFlutterFFI()` function in `lib/init_web.dart` will load these files from the package's asset bundle.

+ 4 - 0
spine-flutter/analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 9 - 0
spine-flutter/android/.gitignore

@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx

+ 59 - 0
spine-flutter/android/build.gradle

@@ -0,0 +1,59 @@
+// The Android Gradle Plugin builds the native code with the Android NDK.
+
+group 'com.example.spine_flutter'
+version '1.0'
+
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        // The Android Gradle Plugin knows how to build native code with the NDK.
+        classpath 'com.android.tools.build:gradle:7.1.2'
+    }
+}
+
+rootProject.allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+    // Bumping the plugin compileSdkVersion requires all clients of this plugin
+    // to bump the version in their app.
+    compileSdkVersion 31
+
+    // Bumping the plugin ndkVersion requires all clients of this plugin to bump
+    // the version in their app and to download a newer version of the NDK.
+    ndkVersion "21.1.6352462"
+
+    // Invoke the shared CMake build with the Android Gradle Plugin.
+    externalNativeBuild {
+        cmake {
+            path "../src/CMakeLists.txt"
+
+            // The default CMake version for the Android Gradle Plugin is 3.10.2.
+            // https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
+            //
+            // The Flutter tooling requires that developers have CMake 3.10 or later
+            // installed. You should not increase this version, as doing so will cause
+            // the plugin to fail to compile for some customers of the plugin.
+            // version "3.10.2"
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        minSdkVersion 16
+    }
+}

+ 1 - 0
spine-flutter/android/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'spine_flutter'

+ 3 - 0
spine-flutter/android/src/main/AndroidManifest.xml

@@ -0,0 +1,3 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.example.spine_flutter">
+</manifest>

+ 25 - 0
spine-flutter/compile-wasm.sh

@@ -0,0 +1,25 @@
+#!/bin/sh
+dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
+pushd $dir > /dev/null
+mkdir -p lib/assets/
+# Need to use -O2, as -O3 applies the Closure compiler to native function names.
+# The entries for exported functions in Module.asm will be scrambled so
+# EmscriptenModule._fromJs() is unable to parse them and link them with original
+# names set on the module, e.g. Module._spine_get_major_version.
+emcc \
+	-Isrc/spine-cpp/include \
+	-O2 --closure 1 -fno-rtti -fno-exceptions -lc++abi -lc++ \
+	-s STRICT=1 \
+	-s LLD_REPORT_UNDEFINED \
+	-s MODULARIZE=1 \
+	-s ALLOW_MEMORY_GROWTH=1 \
+	-s ALLOW_TABLE_GROWTH \
+	-s MALLOC=emmalloc \
+	-s EXPORT_ALL=1 \
+	-s EXPORTED_FUNCTIONS='["_malloc", "_free"]' \
+	--no-entry \
+	-s EXPORT_NAME=libspine_flutter \
+	src/spine_flutter.cpp `find src/spine-cpp/src -type f` \
+	-o lib/assets/libspine_flutter.js
+	ls -lah lib/assets
+popd

+ 46 - 0
spine-flutter/example/.gitignore

@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release

+ 16 - 0
spine-flutter/example/README.md

@@ -0,0 +1,16 @@
+# spine_flutter_example
+
+Demonstrates how to use the spine_flutter plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.

+ 29 - 0
spine-flutter/example/analysis_options.yaml

@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+  # The lint rules applied to this project can be customized in the
+  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+  # included above or to enable additional rules. A list of all available lints
+  # and their documentation is published at
+  # https://dart-lang.github.io/linter/lints/index.html.
+  #
+  # Instead of disabling a lint rule for the entire project in the
+  # section below, it can also be suppressed for a single line of code
+  # or a specific dart file by using the `// ignore: name_of_lint` and
+  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+  # producing the lint.
+  rules:
+    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
+    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 13 - 0
spine-flutter/example/android/.gitignore

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

+ 71 - 0
spine-flutter/example/android/app/build.gradle

@@ -0,0 +1,71 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion flutter.compileSdkVersion
+    ndkVersion flutter.ndkVersion
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId "com.example.spine_flutter_example"
+        // You can update the following values to match your application needs.
+        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
+        minSdkVersion flutter.minSdkVersion
+        targetSdkVersion flutter.targetSdkVersion
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}

+ 8 - 0
spine-flutter/example/android/app/src/debug/AndroidManifest.xml

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

+ 34 - 0
spine-flutter/example/android/app/src/main/AndroidManifest.xml

@@ -0,0 +1,34 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.spine_flutter_example">
+   <application
+        android:label="spine_flutter_example"
+        android:name="${applicationName}"
+        android:icon="@mipmap/ic_launcher">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:theme="@style/LaunchTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+    </application>
+</manifest>

+ 6 - 0
spine-flutter/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt

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

+ 6 - 0
spine-flutter/example/android/app/src/main/kotlin/com/example/spine_flutter_example/MainActivity.kt

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

+ 12 - 0
spine-flutter/example/android/app/src/main/res/drawable-v21/launch_background.xml

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

+ 12 - 0
spine-flutter/example/android/app/src/main/res/drawable/launch_background.xml

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

BIN
spine-flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
spine-flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
spine-flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
spine-flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
spine-flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 18 - 0
spine-flutter/example/android/app/src/main/res/values-night/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 18 - 0
spine-flutter/example/android/app/src/main/res/values/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 8 - 0
spine-flutter/example/android/app/src/profile/AndroidManifest.xml

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

+ 31 - 0
spine-flutter/example/android/build.gradle

@@ -0,0 +1,31 @@
+buildscript {
+    ext.kotlin_version = '1.6.10'
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:7.1.2'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 3 - 0
spine-flutter/example/android/gradle.properties

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

+ 6 - 0
spine-flutter/example/android/gradle/wrapper/gradle-wrapper.properties

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

+ 11 - 0
spine-flutter/example/android/settings.gradle

@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

+ 409 - 0
spine-flutter/example/assets/chibi/chibi-stickers.atlas

@@ -0,0 +1,409 @@
+chibi-stickers.webp
+size:2017,491
+filter:Linear,Linear
+scale:0.5
+common/angry-mark
+bounds:1801,65,42,41
+common/big-purple-fear
+bounds:1640,212,134,71
+offsets:0,0,134,72
+rotate:90
+common/big-tear
+bounds:1454,15,33,82
+common/eye-3
+bounds:1611,2,15,26
+rotate:90
+common/eye-closed-happy
+bounds:1685,348,25,9
+common/eye-dafault
+bounds:1993,51,22,21
+common/eye-equal
+bounds:1271,146,25,15
+common/eye-fire
+bounds:1673,49,26,28
+common/eye-half-open
+bounds:1475,101,26,16
+rotate:90
+common/eye-heart
+bounds:1094,2,26,23
+rotate:90
+common/eye-reverse-v
+bounds:1945,26,26,16
+common/eye-sideway-v
+bounds:945,2,21,23
+rotate:90
+common/eye-slant-close
+bounds:321,72,23,16
+common/eye-small-dot
+bounds:1120,146,14,14
+offsets:0,1,15,15
+common/eye-sparkle
+bounds:1810,4,30,29
+rotate:90
+common/eye-star
+bounds:534,235,29,27
+rotate:90
+common/eye-twirl
+bounds:970,2,21,23
+rotate:90
+common/eye-u
+bounds:733,238,24,17
+rotate:90
+common/eye-x
+bounds:1993,74,25,22
+rotate:90
+common/lamp
+bounds:1319,2,47,65
+common/mouth-3
+bounds:1581,2,15,28
+rotate:90
+common/mouth-bracket
+bounds:1450,2,34,11
+common/mouth-doubt
+bounds:1639,2,26,15
+common/mouth-fangs
+bounds:549,300,39,14
+rotate:90
+common/mouth-line
+bounds:1601,212,36,7
+common/mouth-neutral
+bounds:807,2,27,12
+common/mouth-o-tall
+bounds:1993,101,22,33
+common/mouth-open-smile
+bounds:359,33,36,22
+rotate:90
+common/mouth-rectangle
+bounds:875,2,35,21
+common/mouth-reverse-v
+bounds:1368,2,27,10
+common/mouth-s
+bounds:2003,262,41,11
+rotate:90
+common/mouth-smile-little
+bounds:1928,71,33,19
+rotate:90
+common/mouth-toungue-sticking-out
+bounds:912,2,31,21
+common/mouth-u
+bounds:383,33,36,19
+rotate:90
+common/mouth-v
+bounds:563,235,27,14
+rotate:90
+common/mouth-x
+bounds:1944,4,21,20
+common/purple-fear-lines
+bounds:1807,108,48,28
+common/shadow
+bounds:359,2,111,29
+offsets:1,1,113,31
+common/small-drop-line
+bounds:1925,22,16,17
+rotate:90
+common/small-purple-fear
+bounds:1941,451,54,38
+common/tear
+bounds:995,3,20,19
+rotate:90
+erikari/arm
+bounds:1383,99,28,90
+rotate:90
+erikari/arm-shoulder-decoration
+bounds:1765,2,32,43
+rotate:90
+erikari/back-hair
+bounds:565,348,158,141
+erikari/back-hair-long
+bounds:2,235,220,254
+erikari/blush
+bounds:555,43,29,18
+rotate:90
+erikari/body
+bounds:575,2,70,98
+rotate:90
+erikari/bracelet
+bounds:1841,2,33,11
+rotate:90
+erikari/collar
+bounds:1320,99,61,62
+erikari/ear
+bounds:105,2,34,42
+rotate:90
+erikari/eyebrow
+bounds:2003,241,19,12
+offsets:0,0,20,12
+rotate:90
+erikari/hair-front
+bounds:517,103,130,65
+rotate:90
+erikari/hair-side
+bounds:1137,176,43,132
+rotate:90
+erikari/hat-border
+bounds:1685,450,254,39
+erikari/hat-top
+bounds:346,71,160,60
+rotate:90
+erikari/head-base
+bounds:1060,221,143,125
+erikari/leg
+bounds:2,8,28,101
+rotate:90
+erikari/leg-decoration
+bounds:1412,2,36,13
+erikari/skirt
+bounds:224,233,164,101
+erikari/strawberries-decoration
+bounds:1271,163,112,56
+harri/arm
+bounds:1320,69,28,90
+rotate:90
+harri/back-hair
+bounds:725,348,158,141
+harri/back-hair-long
+bounds:1412,17,40,80
+harri/beard
+bounds:1397,2,10,11
+rotate:90
+harri/blush
+bounds:1240,156,29,18
+luke/blush
+bounds:1240,156,29,18
+nate/blush
+bounds:1240,156,29,18
+spineboy/blush
+bounds:1240,156,29,18
+harri/body
+bounds:675,2,70,98
+rotate:90
+harri/body-decoration
+bounds:287,2,70,67
+harri/ear
+bounds:149,2,34,42
+rotate:90
+soeren/ear
+bounds:149,2,34,42
+rotate:90
+spineboy/ear
+bounds:149,2,34,42
+rotate:90
+harri/eyebrow
+bounds:2003,305,22,12
+rotate:90
+harri/hair-front
+bounds:1866,358,143,90
+harri/head-base
+bounds:1205,221,143,125
+luke/head-base
+bounds:1205,221,143,125
+soeren/head-base
+bounds:1205,221,143,125
+spineboy/head-base
+bounds:1205,221,143,125
+harri/leg
+bounds:1017,132,28,101
+rotate:90
+harri/sword
+bounds:565,264,185,82
+luke/arm
+bounds:1489,71,28,90
+rotate:90
+luke/arm-shoulder-decoration
+bounds:1801,36,31,27
+luke/back-hair
+bounds:885,348,158,141
+luke/body
+bounds:775,16,70,98
+rotate:90
+luke/eyebrow
+bounds:836,2,27,12
+nate/eyebrow
+bounds:836,2,27,12
+spineboy/eyebrow
+bounds:836,2,27,12
+luke/face-cover
+bounds:224,336,169,153
+luke/glasses-shadow
+bounds:584,181,147,81
+luke/hair-decoration
+bounds:408,103,130,107
+rotate:90
+luke/hair-front
+bounds:1013,162,122,57
+luke/leg
+bounds:1016,30,28,101
+rotate:90
+luke/shield
+bounds:1495,131,88,104
+rotate:90
+luke/skirt
+bounds:1845,73,81,31
+luke/sword
+bounds:1601,139,102,71
+offsets:0,0,104,71
+mario/arm
+bounds:1489,41,28,90
+rotate:90
+mario/back-hair
+bounds:395,341,168,148
+mario/back-hair-long
+bounds:194,2,86,91
+rotate:90
+mario/beard
+bounds:1858,136,147,93
+mario/blush
+bounds:1854,17,29,18
+mario/body
+bounds:1018,60,70,98
+rotate:90
+mario/ear
+bounds:1949,100,34,42
+rotate:90
+mario/eyebrow
+bounds:287,71,32,17
+mario/hair-front
+bounds:874,158,137,66
+mario/head-base
+bounds:1350,221,143,125
+mario/leg
+bounds:1137,146,28,101
+rotate:90
+misaki/arm
+bounds:1489,11,28,90
+rotate:90
+misaki/back-hair
+bounds:1045,348,158,141
+misaki/back-hair-long
+bounds:2,38,190,195
+misaki/belt
+bounds:1016,2,76,26
+misaki/blush
+bounds:1925,40,29,18
+rotate:90
+misaki/body
+bounds:1701,36,70,98
+rotate:90
+misaki/ear
+bounds:1949,64,34,42
+rotate:90
+misaki/eyebrow
+bounds:775,2,30,12
+misaki/glasses
+bounds:875,72,141,35
+misaki/glasses-side
+bounds:1240,146,8,23
+rotate:90
+misaki/hair-front
+bounds:733,88,140,59
+misaki/hair-side
+bounds:875,109,47,140
+rotate:90
+misaki/head-base
+bounds:1495,221,143,125
+misaki/leg
+bounds:1601,109,28,101
+rotate:90
+misaki/skirt
+bounds:1385,129,108,90
+nate/arm
+bounds:1581,49,28,90
+rotate:90
+nate/back-hair
+bounds:1205,348,158,141
+nate/beard
+bounds:584,111,147,68
+nate/body
+bounds:1120,74,70,98
+rotate:90
+nate/ear
+bounds:1845,37,34,42
+rotate:90
+nate/glasses
+bounds:584,74,141,35
+nate/glasses-side
+bounds:555,33,8,16
+rotate:90
+nate/hair-front
+bounds:390,235,142,65
+nate/head-base
+bounds:1713,232,143,125
+nate/leg
+bounds:1704,108,28,101
+rotate:90
+sinisa/arm
+bounds:1581,19,28,90
+rotate:90
+sinisa/back-hair
+bounds:1365,348,158,141
+sinisa/beard
+bounds:875,25,139,45
+sinisa/blush
+bounds:1945,44,29,18
+sinisa/body
+bounds:1119,2,70,98
+rotate:90
+sinisa/body-decoration
+bounds:555,74,27,27
+sinisa/ear
+bounds:1889,29,34,42
+sinisa/eyebrow
+bounds:1997,451,38,18
+offsets:0,0,38,19
+rotate:90
+sinisa/hair-front
+bounds:1713,138,143,92
+sinisa/head-base
+bounds:1858,231,143,125
+sinisa/leg
+bounds:1495,101,28,101
+rotate:90
+soeren/arm
+bounds:1673,6,28,90
+rotate:90
+soeren/back-hair
+bounds:194,90,150,141
+soeren/beard
+bounds:408,33,145,68
+soeren/blush
+bounds:1885,9,29,18
+soeren/body
+bounds:1219,2,70,98
+rotate:90
+soeren/eyebrow
+bounds:2003,329,27,12
+rotate:90
+soeren/glasses
+bounds:395,302,152,37
+soeren/glasses-side
+bounds:1486,2,7,20
+rotate:90
+soeren/glove
+bounds:1368,14,42,53
+soeren/hair-front
+bounds:752,233,159,113
+soeren/leg
+bounds:1598,79,28,101
+rotate:90
+spineboy/arm
+bounds:1857,106,28,90
+rotate:90
+spineboy/arm-decoration
+bounds:534,266,32,29
+rotate:90
+spineboy/arm-shoulder-decoration
+bounds:1973,19,23,23
+spineboy/back-hair
+bounds:1525,348,158,141
+spineboy/body
+bounds:1220,74,70,98
+rotate:90
+spineboy/glasses
+bounds:1685,359,179,89
+spineboy/glasses-shadow
+bounds:733,149,139,82
+spineboy/hair-front
+bounds:913,226,145,120
+spineboy/leg
+bounds:472,2,29,101
+rotate:90

BIN
spine-flutter/example/assets/chibi/chibi-stickers.skel


BIN
spine-flutter/example/assets/chibi/chibi-stickers.webp


BIN
spine-flutter/example/assets/dragon-ess.skel


+ 123 - 0
spine-flutter/example/assets/dragon.atlas

@@ -0,0 +1,123 @@
+dragon.png
+	size: 1024, 1024
+	filter: Linear, Linear
+back
+	bounds: 564, 534, 190, 185
+chest
+	bounds: 2, 645, 136, 122
+chin
+	bounds: 140, 619, 214, 146
+front-toe-a
+	bounds: 2, 862, 29, 50
+	rotate: 90
+front-toe-b
+	bounds: 467, 835, 56, 57
+	rotate: 90
+head
+	bounds: 756, 398, 296, 260
+	rotate: 90
+left-front-leg
+	bounds: 599, 834, 84, 57
+left-front-thigh
+	bounds: 782, 819, 84, 72
+left-rear-leg
+	bounds: 356, 558, 206, 177
+left-rear-thigh
+	bounds: 216, 767, 91, 149
+	rotate: 90
+left-wing01
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing02
+	bounds: 2, 2, 264, 589
+	rotate: 90
+right-front-leg
+	bounds: 113, 769, 101, 89
+right-front-thigh
+	bounds: 758, 709, 108, 108
+right-rear-leg
+	bounds: 640, 721, 116, 100
+right-rear-thigh
+	bounds: 367, 742, 91, 149
+	rotate: 90
+right-rear-toe
+	bounds: 2, 781, 109, 77
+tail01
+	bounds: 868, 696, 120, 153
+	rotate: 90
+tail02
+	bounds: 518, 737, 95, 120
+	rotate: 90
+tail03
+	bounds: 868, 818, 73, 92
+	rotate: 90
+tail04
+	bounds: 526, 835, 56, 71
+	rotate: 90
+tail05
+	bounds: 406, 839, 52, 59
+	rotate: 90
+tail06
+	bounds: 685, 823, 95, 68
+thiagobrayner
+	bounds: 54, 860, 350, 31
+
+dragon_2.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing03
+	bounds: 2, 534, 264, 589
+	rotate: 90
+left-wing04
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing05
+	bounds: 593, 209, 264, 589
+left-wing06
+	bounds: 2, 2, 264, 589
+	rotate: 90
+
+dragon_3.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing07
+	bounds: 2, 694, 264, 589
+	rotate: 90
+left-wing08
+	bounds: 2, 428, 264, 589
+	rotate: 90
+left-wing09
+	bounds: 593, 369, 264, 589
+right-wing01
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_4.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing02
+	bounds: 2, 369, 365, 643
+right-wing03
+	bounds: 369, 369, 365, 643
+right-wing04
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_5.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing05
+	bounds: 2, 369, 365, 643
+right-wing06
+	bounds: 369, 369, 365, 643
+right-wing07
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_6.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing08
+	bounds: 2, 2, 365, 643
+right-wing09
+	bounds: 369, 2, 365, 643

BIN
spine-flutter/example/assets/dragon.png


BIN
spine-flutter/example/assets/dragon_2.png


BIN
spine-flutter/example/assets/dragon_3.png


BIN
spine-flutter/example/assets/dragon_4.png


BIN
spine-flutter/example/assets/dragon_5.png


BIN
spine-flutter/example/assets/dragon_6.png


BIN
spine-flutter/example/assets/mix-and-match-pro.skel


+ 362 - 0
spine-flutter/example/assets/mix-and-match.atlas

@@ -0,0 +1,362 @@
+mix-and-match.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+base-head
+	bounds: 587, 2, 95, 73
+boy/arm-front
+	bounds: 558, 271, 36, 115
+boy/backpack
+	bounds: 235, 109, 119, 153
+boy/backpack-pocket
+	bounds: 328, 73, 34, 62
+	rotate: 90
+boy/backpack-strap-front
+	bounds: 665, 79, 38, 88
+boy/backpack-up
+	bounds: 395, 364, 21, 70
+	rotate: 90
+boy/body
+	bounds: 251, 264, 97, 132
+	rotate: 90
+boy/boot-ribbon-front
+	bounds: 648, 131, 9, 11
+boy/collar
+	bounds: 744, 4, 73, 29
+	rotate: 90
+boy/ear
+	bounds: 383, 109, 19, 23
+	rotate: 90
+boy/eye-back-low-eyelid
+	bounds: 739, 284, 17, 6
+	rotate: 90
+boy/eye-back-pupil
+	bounds: 832, 443, 8, 9
+	rotate: 90
+boy/eye-back-up-eyelid
+	bounds: 558, 264, 23, 5
+boy/eye-back-up-eyelid-back
+	bounds: 802, 491, 19, 10
+	rotate: 90
+boy/eye-front-low-eyelid
+	bounds: 386, 363, 22, 7
+	rotate: 90
+boy/eye-front-pupil
+	bounds: 816, 389, 9, 9
+boy/eye-front-up-eyelid
+	bounds: 160, 71, 31, 6
+	rotate: 90
+boy/eye-front-up-eyelid-back
+	bounds: 801, 434, 26, 9
+	rotate: 90
+boy/eye-iris-back
+	bounds: 618, 264, 17, 17
+boy/eye-iris-front
+	bounds: 727, 264, 18, 18
+boy/eye-white-back
+	bounds: 580, 131, 20, 12
+boy/eye-white-front
+	bounds: 510, 130, 27, 13
+boy/eyebrow-back
+	bounds: 751, 88, 20, 11
+	rotate: 90
+boy/eyebrow-front
+	bounds: 483, 130, 25, 11
+boy/hair-back
+	bounds: 494, 388, 122, 81
+	rotate: 90
+boy/hair-bangs
+	bounds: 667, 284, 70, 37
+boy/hair-side
+	bounds: 789, 374, 25, 43
+boy/hand-backfingers
+	bounds: 467, 364, 19, 21
+boy/hand-front-fingers
+	bounds: 488, 364, 19, 21
+boy/hat
+	bounds: 615, 417, 93, 56
+	rotate: 90
+boy/leg-front
+	bounds: 138, 104, 31, 158
+boy/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl-blue-cape/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl-spring-dress/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+boy/mouth-smile
+	bounds: 705, 79, 29, 7
+boy/nose
+	bounds: 836, 473, 17, 10
+	rotate: 90
+boy/pompom
+	bounds: 747, 273, 48, 43
+	rotate: 90
+boy/zip
+	bounds: 648, 144, 14, 23
+girl-blue-cape/back-eyebrow
+	bounds: 602, 131, 18, 12
+girl-blue-cape/body-dress
+	bounds: 2, 264, 109, 246
+girl-blue-cape/body-ribbon
+	bounds: 615, 283, 50, 38
+girl-blue-cape/cape-back
+	bounds: 2, 69, 134, 193
+girl-blue-cape/cape-back-up
+	bounds: 386, 387, 123, 106
+	rotate: 90
+girl-blue-cape/cape-ribbon
+	bounds: 675, 264, 50, 18
+girl-blue-cape/cape-shoulder-back
+	bounds: 751, 110, 49, 59
+girl-blue-cape/cape-shoulder-front
+	bounds: 113, 264, 62, 76
+	rotate: 90
+girl-blue-cape/cape-up-front
+	bounds: 399, 264, 98, 117
+	rotate: 90
+girl-blue-cape/ear
+	bounds: 775, 2, 19, 23
+girl-spring-dress/ear
+	bounds: 775, 2, 19, 23
+girl/ear
+	bounds: 775, 2, 19, 23
+girl-blue-cape/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl-spring-dress/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl-blue-cape/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl-spring-dress/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl-blue-cape/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl-spring-dress/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl-blue-cape/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl-spring-dress/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl-blue-cape/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl-spring-dress/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl-blue-cape/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl-spring-dress/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl-blue-cape/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl-spring-dress/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl-blue-cape/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl-spring-dress/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl-blue-cape/eye-iris-back
+	bounds: 637, 264, 17, 17
+girl-blue-cape/eye-iris-front
+	bounds: 802, 471, 18, 18
+girl-blue-cape/eye-white-back
+	bounds: 596, 264, 20, 16
+girl-spring-dress/eye-white-back
+	bounds: 596, 264, 20, 16
+girl-blue-cape/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl-spring-dress/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl-blue-cape/front-eyebrow
+	bounds: 608, 149, 18, 12
+	rotate: 90
+girl-blue-cape/hair-back
+	bounds: 508, 145, 117, 98
+	rotate: 90
+girl-blue-cape/hair-bangs
+	bounds: 673, 419, 91, 40
+	rotate: 90
+girl-blue-cape/hair-head-side-back
+	bounds: 196, 331, 30, 52
+	rotate: 90
+girl-blue-cape/hair-head-side-front
+	bounds: 738, 323, 41, 42
+girl-blue-cape/hair-side
+	bounds: 473, 3, 36, 71
+girl-blue-cape/hand-front-fingers
+	bounds: 509, 365, 19, 21
+girl-spring-dress/hand-front-fingers
+	bounds: 509, 365, 19, 21
+girl-blue-cape/leg-front
+	bounds: 168, 72, 30, 158
+	rotate: 90
+girl-blue-cape/mouth-smile
+	bounds: 736, 79, 29, 7
+girl-spring-dress/mouth-smile
+	bounds: 736, 79, 29, 7
+girl/mouth-smile
+	bounds: 736, 79, 29, 7
+girl-blue-cape/nose
+	bounds: 747, 264, 11, 7
+girl-spring-dress/nose
+	bounds: 747, 264, 11, 7
+girl/nose
+	bounds: 747, 264, 11, 7
+girl-blue-cape/sleeve-back
+	bounds: 767, 79, 42, 29
+girl-blue-cape/sleeve-front
+	bounds: 408, 76, 52, 119
+	rotate: 90
+girl-spring-dress/arm-front
+	bounds: 596, 282, 17, 111
+girl-spring-dress/back-eyebrow
+	bounds: 801, 420, 18, 12
+girl-spring-dress/body-up
+	bounds: 179, 4, 64, 66
+girl-spring-dress/cloak-down
+	bounds: 775, 27, 50, 50
+girl-spring-dress/cloak-up
+	bounds: 360, 7, 64, 58
+	rotate: 90
+girl-spring-dress/eye-iris-back
+	bounds: 656, 264, 17, 17
+girl-spring-dress/eye-iris-front
+	bounds: 814, 492, 18, 18
+girl-spring-dress/front-eyebrow
+	bounds: 822, 472, 18, 12
+	rotate: 90
+girl-spring-dress/hair-back
+	bounds: 196, 363, 147, 93
+	rotate: 90
+girl-spring-dress/hair-bangs
+	bounds: 696, 326, 91, 40
+	rotate: 90
+girl-spring-dress/hair-head-side-back
+	bounds: 529, 76, 30, 52
+girl-spring-dress/hair-head-side-front
+	bounds: 781, 323, 41, 42
+girl-spring-dress/hair-side
+	bounds: 511, 3, 36, 71
+girl-spring-dress/leg-front
+	bounds: 171, 104, 30, 158
+girl-spring-dress/neck
+	bounds: 138, 70, 20, 32
+girl-spring-dress/shoulder-ribbon
+	bounds: 622, 131, 36, 24
+	rotate: 90
+girl-spring-dress/skirt
+	bounds: 113, 328, 182, 81
+	rotate: 90
+girl-spring-dress/underskirt
+	bounds: 2, 2, 175, 65
+girl/arm-front
+	bounds: 577, 395, 36, 115
+girl/back-eyebrow
+	bounds: 834, 492, 18, 12
+	rotate: 90
+girl/bag-base
+	bounds: 191, 264, 62, 58
+	rotate: 90
+girl/bag-strap-front
+	bounds: 385, 265, 12, 96
+girl/bag-top
+	bounds: 738, 367, 49, 50
+girl/body
+	bounds: 356, 130, 97, 132
+girl/boot-ribbon-front
+	bounds: 539, 130, 13, 13
+girl/eye-iris-back
+	bounds: 821, 424, 17, 17
+girl/eye-iris-front
+	bounds: 812, 443, 18, 18
+girl/eye-white-back
+	bounds: 814, 5, 20, 16
+	rotate: 90
+girl/front-eyebrow
+	bounds: 816, 400, 18, 12
+	rotate: 90
+girl/hair-back
+	bounds: 291, 363, 147, 93
+	rotate: 90
+girl/hair-bangs
+	bounds: 715, 419, 91, 40
+	rotate: 90
+girl/hair-flap-down-front
+	bounds: 288, 5, 70, 65
+girl/hair-head-side-back
+	bounds: 561, 77, 30, 52
+girl/hair-head-side-front
+	bounds: 757, 419, 41, 42
+	rotate: 90
+girl/hair-patch
+	bounds: 245, 4, 66, 41
+	rotate: 90
+girl/hair-side
+	bounds: 549, 3, 36, 71
+girl/hair-strand-back-1
+	bounds: 684, 3, 58, 74
+girl/hair-strand-back-2
+	bounds: 692, 171, 91, 58
+	rotate: 90
+girl/hair-strand-back-3
+	bounds: 615, 323, 92, 79
+	rotate: 90
+girl/hair-strand-front-1
+	bounds: 518, 269, 38, 94
+girl/hair-strand-front-2
+	bounds: 593, 79, 70, 50
+girl/hair-strand-front-3
+	bounds: 705, 88, 44, 81
+girl/hand-front-fingers
+	bounds: 530, 365, 19, 21
+girl/hat
+	bounds: 608, 169, 93, 82
+	rotate: 90
+girl/leg-front
+	bounds: 203, 104, 30, 158
+girl/pompom
+	bounds: 757, 462, 48, 43
+	rotate: 90
+girl/scarf
+	bounds: 455, 143, 119, 51
+	rotate: 90
+girl/scarf-back
+	bounds: 420, 2, 72, 51
+	rotate: 90
+girl/zip
+	bounds: 356, 109, 19, 25
+	rotate: 90

BIN
spine-flutter/example/assets/mix-and-match.png


File diff suppressed because it is too large
+ 541 - 0
spine-flutter/example/assets/spineboy-pro.json


BIN
spine-flutter/example/assets/spineboy-pro.skel


+ 101 - 0
spine-flutter/example/assets/spineboy.atlas

@@ -0,0 +1,101 @@
+spineboy.png
+	size: 1024, 256
+	filter: Linear, Linear
+	scale: 0.5
+crosshair
+	bounds: 813, 160, 45, 45
+eye-indifferent
+	bounds: 569, 2, 47, 45
+eye-surprised
+	bounds: 643, 7, 47, 45
+	rotate: 90
+front-bracer
+	bounds: 811, 51, 29, 40
+front-fist-closed
+	bounds: 807, 93, 38, 41
+front-fist-open
+	bounds: 815, 210, 43, 44
+front-foot
+	bounds: 706, 64, 63, 35
+	rotate: 90
+front-shin
+	bounds: 80, 11, 41, 92
+front-thigh
+	bounds: 754, 12, 23, 56
+front-upper-arm
+	bounds: 618, 5, 23, 49
+goggles
+	bounds: 214, 20, 131, 83
+gun
+	bounds: 347, 14, 105, 102
+	rotate: 90
+head
+	bounds: 80, 105, 136, 149
+hoverboard-board
+	bounds: 2, 8, 246, 76
+	rotate: 90
+hoverboard-thruster
+	bounds: 478, 2, 30, 32
+hoverglow-small
+	bounds: 218, 117, 137, 38
+	rotate: 90
+mouth-grind
+	bounds: 775, 80, 47, 30
+	rotate: 90
+mouth-oooo
+	bounds: 779, 31, 47, 30
+	rotate: 90
+mouth-smile
+	bounds: 783, 207, 47, 30
+	rotate: 90
+muzzle-glow
+	bounds: 779, 4, 25, 25
+muzzle-ring
+	bounds: 451, 14, 25, 105
+muzzle01
+	bounds: 664, 60, 67, 40
+	rotate: 90
+muzzle02
+	bounds: 580, 56, 68, 42
+	rotate: 90
+muzzle03
+	bounds: 478, 36, 83, 53
+	rotate: 90
+muzzle04
+	bounds: 533, 49, 75, 45
+	rotate: 90
+muzzle05
+	bounds: 624, 56, 68, 38
+	rotate: 90
+neck
+	bounds: 806, 8, 18, 21
+portal-bg
+	bounds: 258, 121, 133, 133
+portal-flare1
+	bounds: 690, 2, 56, 30
+	rotate: 90
+portal-flare2
+	bounds: 510, 3, 57, 31
+portal-flare3
+	bounds: 722, 4, 58, 30
+	rotate: 90
+portal-shade
+	bounds: 393, 121, 133, 133
+portal-streaks1
+	bounds: 528, 126, 126, 128
+portal-streaks2
+	bounds: 656, 129, 125, 125
+rear-bracer
+	bounds: 826, 13, 28, 36
+rear-foot
+	bounds: 743, 70, 57, 30
+	rotate: 90
+rear-shin
+	bounds: 174, 14, 38, 89
+rear-thigh
+	bounds: 783, 158, 28, 47
+rear-upper-arm
+	bounds: 783, 136, 20, 44
+	rotate: 90
+torso
+	bounds: 123, 13, 49, 90

BIN
spine-flutter/example/assets/spineboy.png


+ 34 - 0
spine-flutter/example/ios/.gitignore

@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3

+ 26 - 0
spine-flutter/example/ios/Flutter/AppFrameworkInfo.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>en</string>
+  <key>CFBundleExecutable</key>
+  <string>App</string>
+  <key>CFBundleIdentifier</key>
+  <string>io.flutter.flutter.app</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>App</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+  <key>MinimumOSVersion</key>
+  <string>11.0</string>
+</dict>
+</plist>

+ 2 - 0
spine-flutter/example/ios/Flutter/Debug.xcconfig

@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"

+ 2 - 0
spine-flutter/example/ios/Flutter/Release.xcconfig

@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"

+ 41 - 0
spine-flutter/example/ios/Podfile

@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '11.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+  'Debug' => :debug,
+  'Profile' => :release,
+  'Release' => :release,
+}
+
+def flutter_root
+  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+  unless File.exist?(generated_xcode_build_settings_path)
+    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+  end
+
+  File.foreach(generated_xcode_build_settings_path) do |line|
+    matches = line.match(/FLUTTER_ROOT\=(.*)/)
+    return matches[1].strip if matches
+  end
+  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+  use_frameworks!
+  use_modular_headers!
+
+  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    flutter_additional_ios_build_settings(target)
+  end
+end

+ 22 - 0
spine-flutter/example/ios/Podfile.lock

@@ -0,0 +1,22 @@
+PODS:
+  - Flutter (1.0.0)
+  - spine_flutter (0.0.1):
+    - Flutter
+
+DEPENDENCIES:
+  - Flutter (from `Flutter`)
+  - spine_flutter (from `.symlinks/plugins/spine_flutter/ios`)
+
+EXTERNAL SOURCES:
+  Flutter:
+    :path: Flutter
+  spine_flutter:
+    :path: ".symlinks/plugins/spine_flutter/ios"
+
+SPEC CHECKSUMS:
+  Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
+  spine_flutter: 25063c3b7039f2fc0986b260b801a8bbc8906a3f
+
+PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
+
+COCOAPODS: 1.11.3

+ 551 - 0
spine-flutter/example/ios/Runner.xcodeproj/project.pbxproj

@@ -0,0 +1,551 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 54;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+		777D8D112000B6AC703D6124 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE47748514E81CED33B6EDD5 /* Pods_Runner.framework */; };
+		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		0A0ED2F7954A1E765F7844E6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		21C14CF0973DAFF83E6DB0B0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		3F7F5074FAFBCDFAE6EF8D07 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
+		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		BE47748514E81CED33B6EDD5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				777D8D112000B6AC703D6124 /* Pods_Runner.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		8B9D75E8C5E0009A3BF2A9E6 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				BE47748514E81CED33B6EDD5 /* Pods_Runner.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "<group>";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				97C146EF1CF9000F007C117D /* Products */,
+				AFB2390791D3B8AA864ACD02 /* Pods */,
+				8B9D75E8C5E0009A3BF2A9E6 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FD1CF9000F007C117D /* Assets.xcassets */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		AFB2390791D3B8AA864ACD02 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				21C14CF0973DAFF83E6DB0B0 /* Pods-Runner.debug.xcconfig */,
+				3F7F5074FAFBCDFAE6EF8D07 /* Pods-Runner.release.xcconfig */,
+				0A0ED2F7954A1E765F7844E6 /* Pods-Runner.profile.xcconfig */,
+			);
+			name = Pods;
+			path = Pods;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				B3F9EFA5320DF0D4B11CC671 /* [CP] Check Pods Manifest.lock */,
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+				8436C011D9463CCA25592170 /* [CP] Embed Pods Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1300;
+				ORGANIZATIONNAME = "";
+				TargetAttributes = {
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+						LastSwiftMigration = 1100;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+		};
+		8436C011D9463CCA25592170 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			alwaysOutOfDate = 1;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+		B3F9EFA5320DF0D4B11CC671 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			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";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		249021D3217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Profile;
+		};
+		249021D4217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.spineFlutterExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Profile;
+		};
+		97C147031CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		97C147041CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		97C147061CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.spineFlutterExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Debug;
+		};
+		97C147071CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.spineFlutterExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug */,
+				97C147041CF9000F007C117D /* Release */,
+				249021D3217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug */,
+				97C147071CF9000F007C117D /* Release */,
+				249021D4217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}

+ 7 - 0
spine-flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>

+ 10 - 0
spine-flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>

+ 13 - 0
spine-flutter/example/ios/Runner/AppDelegate.swift

@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+  override func application(
+    _ application: UIApplication,
+    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+  ) -> Bool {
+    GeneratedPluginRegistrant.register(with: self)
+    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+  }
+}

+ 122 - 0
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,122 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "3x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "[email protected]",
+      "scale" : "3x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "[email protected]",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/[email protected]


+ 23 - 0
spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "[email protected]",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "[email protected]",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/[email protected]


BIN
spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/[email protected]


+ 5 - 0
spine-flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md

@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+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.

+ 37 - 0
spine-flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+                        </constraints>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="LaunchImage" width="168" height="185"/>
+    </resources>
+</document>

+ 26 - 0
spine-flutter/example/ios/Runner/Base.lproj/Main.storyboard

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+    </dependencies>
+    <scenes>
+        <!--Flutter View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>

+ 51 - 0
spine-flutter/example/ios/Runner/Info.plist

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>Spine Flutter</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>spine_flutter_example</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(FLUTTER_BUILD_NAME)</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(FLUTTER_BUILD_NUMBER)</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<false/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
+	<key>UIApplicationSupportsIndirectInputEvents</key>
+	<true/>
+</dict>
+</plist>

+ 1 - 0
spine-flutter/example/ios/Runner/Runner-Bridging-Header.h

@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"

+ 30 - 0
spine-flutter/example/lib/animated_login.dart

@@ -0,0 +1,30 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class AnimatedLogin extends StatelessWidget {
+  const AnimatedLogin({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      controller.skeleton.setSkinByName("nate");
+      controller.skeleton.setToSetupPose();
+      controller.animationState.setAnimationByName(0, "login/look-left-down", true);
+    });
+
+    return Scaffold(
+        appBar: AppBar(title: const Text('Animated login')),
+        body: Container(
+            margin: const EdgeInsets.all(15.0),
+            padding: const EdgeInsets.all(3.0),
+            decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)),
+            child: SpineWidget.fromAsset(
+              "assets/chibi/chibi-stickers.atlas",
+              "assets/chibi/chibi-stickers.skel",
+              controller,
+              boundsProvider: SkinAndAnimationBounds(skins: ["nate"], animation: "login/look-left-down"),
+              sizedByBounds: true,
+            )));
+  }
+}

+ 39 - 0
spine-flutter/example/lib/animation_state_events.dart

@@ -0,0 +1,39 @@
+// ignore_for_file: avoid_print
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class AnimationStateEvents extends StatelessWidget {
+  const AnimationStateEvents({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      controller.skeleton.setScaleX(0.5);
+      controller.skeleton.setScaleY(0.5);
+      controller.skeleton.findSlot("gun")?.setColor(Color(1, 0, 0, 1));
+      controller.animationStateData.setDefaultMix(0.2);
+      controller.animationState.setAnimationByName(0, "walk", true).setListener((type, trackEntry, event) {
+        print("Walk animation event $type");
+      });
+      controller.animationState.addAnimationByName(0, "jump", false, 2);
+      controller.animationState.addAnimationByName(0, "run", true, 0).setListener((type, trackEntry, event) {
+        print("Run animation event $type");
+      });
+      controller.animationState.setListener((type, trackEntry, event) {
+        if (type == EventType.event) {
+          print(
+              "User event: { name: ${event?.getData().getName()}, intValue: ${event?.getIntValue()}, floatValue: intValue: ${event?.getFloatValue()}, stringValue: ${event?.getStringValue()} }");
+        }
+      });
+      print("Current: ${controller.animationState.getCurrent(0)?.getAnimation().getName()}");
+    });
+
+    return Scaffold(
+        appBar: AppBar(title: const Text('Spineboy')),
+        body: Column(children: [
+          const Text("See output in console!"),
+          Expanded(child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller))
+        ]));
+  }
+}

+ 23 - 0
spine-flutter/example/lib/debug_rendering.dart

@@ -0,0 +1,23 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class DebugRendering extends StatelessWidget {
+  const DebugRendering({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+
+    const debugRenderer = DebugRenderer();
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      controller.animationState.setAnimationByName(0, "walk", true);
+    }, onAfterPaint: (controller, canvas, commands) {
+      debugRenderer.render(controller.drawable, canvas, commands);
+    });
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Debug Renderer')),
+      body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
+    );
+  }
+}

+ 104 - 0
spine-flutter/example/lib/dress_up.dart

@@ -0,0 +1,104 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/painting.dart' as painting;
+import 'package:raw_image_provider/raw_image_provider.dart';
+
+class DressUp extends StatefulWidget {
+  const DressUp({Key? key}) : super(key: key);
+
+  @override
+  DressUpState createState() => DressUpState();
+}
+
+class DressUpState extends State<DressUp> {
+  static const double thumbnailSize = 200;
+  late SkeletonDrawable _drawable;
+  Skin? _customSkin;
+  final Map<String, RawImageData> _skinImages = {};
+  final Map<String, bool> _selectedSkins = {};
+
+  @override
+  void initState() {
+    reportLeaks();
+    super.initState();
+    SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) async {
+      _drawable = drawable;
+      for (var skin in drawable.skeletonData.getSkins()) {
+        if (skin.getName() == "default") continue;
+        var skeleton = drawable.skeleton;
+        skeleton.setSkin(skin);
+        skeleton.setToSetupPose();
+        skeleton.updateWorldTransform();
+        _skinImages[skin.getName()] = await drawable.renderToRawImageData(thumbnailSize, thumbnailSize, 0xffffffff);
+        _selectedSkins[skin.getName()] = false;
+      }
+      _toggleSkin("full-skins/girl");
+      setState(() {});
+    });
+  }
+
+  void _toggleSkin(String skinName) {
+    _selectedSkins[skinName] = !_selectedSkins[skinName]!;
+    if (_customSkin != null) _customSkin?.dispose();
+    _customSkin = Skin("custom-skin");
+    for (var skinName in _selectedSkins.keys) {
+      if (_selectedSkins[skinName] == true) {
+        var skin = _drawable.skeletonData.findSkin(skinName);
+        if (skin != null) _customSkin?.addSkin(skin);
+      }
+    }
+    _drawable.skeleton.setSkin(_customSkin!);
+    _drawable.skeleton.setToSetupPose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      controller.animationState.setAnimationByName(0, "dance", true);
+    });
+
+    return Scaffold(
+        appBar: AppBar(title: const Text('Dress Up')),
+        body: _skinImages.isEmpty
+            ? const SizedBox()
+            : Row(children: [
+                SizedBox(
+                  width: thumbnailSize,
+                  child: ListView(
+                      children: _skinImages.keys.map((skinName) {
+                    var rawImageData = _skinImages[skinName]!;
+                    var image = Image(image: RawImageProvider(rawImageData));
+                    var box = SizedBox(width: 200, height: 200, child: image);
+                    return GestureDetector(
+                        onTap: () {
+                          _toggleSkin(skinName);
+                          setState(() {});
+                        },
+                        child: _selectedSkins[skinName] == true
+                            ? box
+                            // Does not work on web.
+                            //: ColorFiltered(colorFilter: const ColorFilter.mode(Colors.grey, painting.BlendMode.saturation,), child: box)
+                            : Container(
+                                foregroundDecoration: const BoxDecoration(
+                                  color: Colors.grey,
+                                  backgroundBlendMode: painting.BlendMode.saturation,
+                                ),
+                                child: box));
+                  }).toList()),
+                ),
+                Expanded(
+                    child: SpineWidget.fromDrawable(
+                  _drawable,
+                  controller,
+                  boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),
+                ))
+              ]));
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    _drawable.dispose();
+    _customSkin?.dispose();
+  }
+}

+ 151 - 0
spine-flutter/example/lib/flame_example.dart

@@ -0,0 +1,151 @@
+import 'dart:math';
+
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flame/components.dart';
+import 'package:flame/game.dart';
+import 'package:flutter/material.dart';
+
+class SpineComponent extends PositionComponent {
+  final BoundsProvider _boundsProvider;
+  final SkeletonDrawable _drawable;
+  late final Bounds _bounds;
+  final bool _ownsDrawable;
+
+  SpineComponent(
+    this._drawable, {
+    bool ownsDrawable = true,
+    BoundsProvider boundsProvider = const SetupPoseBounds(),
+    super.position,
+    super.scale,
+    double super.angle = 0.0,
+    Anchor super.anchor = Anchor.topLeft,
+    super.children,
+    super.priority,
+  })  : _ownsDrawable = ownsDrawable,
+        _boundsProvider = boundsProvider {
+    _drawable.update(0);
+    _bounds = _boundsProvider.computeBounds(_drawable);
+    size = Vector2(_bounds.width, _bounds.height);
+  }
+
+  static Future<SpineComponent> fromAssets(
+    String atlasFile,
+    String skeletonFile, {
+    AssetBundle? bundle,
+    BoundsProvider boundsProvider = const SetupPoseBounds(),
+    Vector2? position,
+    Vector2? scale,
+    double angle = 0.0,
+    Anchor anchor = Anchor.topLeft,
+    Iterable<Component>? children,
+    int? priority,
+  }) async {
+    return SpineComponent(await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle),
+        ownsDrawable: true,
+        boundsProvider: boundsProvider,
+        position: position,
+        scale: scale,
+        angle: angle,
+        anchor: anchor,
+        children: children,
+        priority: priority);
+  }
+
+  void dispose() {
+    if (_ownsDrawable) {
+      _drawable.dispose();
+    }
+  }
+
+  @override
+  void update(double dt) {
+    _drawable.update(dt);
+  }
+
+  @override
+  void render(Canvas canvas) {
+    canvas.save();
+    canvas.translate(-_bounds.x, -_bounds.y);
+    _drawable.renderToCanvas(canvas);
+    canvas.restore();
+  }
+
+  get animationState => _drawable.animationState;
+
+  get animationStateData => _drawable.animationStateData;
+
+  get skeleton => _drawable.skeleton;
+}
+
+class SimpleFlameExample extends FlameGame {
+  late final SpineComponent spineboy;
+
+  @override
+  Future<void> onLoad() async {
+    // Load the Spineboy atlas and skeleton data from asset files
+    // and create a SpineComponent from them, scaled down and
+    // centered on the screen
+    spineboy = await SpineComponent.fromAssets("assets/spineboy.atlas", "assets/spineboy-pro.skel",
+        scale: Vector2(0.4, 0.4), anchor: Anchor.center, position: Vector2(size.x / 2, size.y / 2));
+
+    // Set the "walk" animation on track 0 in looping mode
+    spineboy.animationState.setAnimationByName(0, "walk", true);
+    await add(spineboy);
+  }
+
+  @override
+  void onDetach() {
+    // Dispose the native resources that have been loaded for spineboy.
+    spineboy.dispose();
+  }
+}
+
+class PreloadAndShareSpineDataExample extends FlameGame {
+  late final SkeletonData cachedSkeletonData;
+  late final Atlas cachedAtlas;
+  late final List<SpineComponent> spineboys = [];
+
+  @override
+  Future<void> onLoad() async {
+    // Pre-load the atlas and skeleton data once.
+    cachedAtlas = await Atlas.fromAsset("assets/spineboy.atlas");
+    cachedSkeletonData = await SkeletonData.fromAsset(cachedAtlas, "assets/spineboy-pro.skel");
+
+    // Instantiate many spineboys from the pre-loaded data. Each SpineComponent
+    // gets their own SkeletonDrawable copy derived from the cached data. The
+    // SkeletonDrawable copies do not own the underlying skeleton data and atlas.
+    final rng = Random();
+    for (int i = 0; i < 100; i++) {
+      final drawable = SkeletonDrawable(cachedAtlas, cachedSkeletonData, false);
+      final scale = 0.1 + rng.nextDouble() * 0.2;
+      final position = Vector2(rng.nextDouble() * size.x, rng.nextDouble() * size.y);
+      final spineboy = SpineComponent(drawable, scale: Vector2(scale, scale), position: position);
+      spineboy.animationState.setAnimationByName(0, "walk", true);
+      spineboys.add(spineboy);
+      await add(spineboy);
+    }
+  }
+
+  @override
+  void onDetach() {
+    // Dispose the pre-loaded atlas and skeleton data when the game/scene is removed.
+    cachedAtlas.dispose();
+    cachedSkeletonData.dispose();
+
+    // Dispose each spineboy and its internal SkeletonDrawable.
+    for (final spineboy in spineboys) {
+      spineboy.dispose();
+    }
+  }
+}
+
+class SpineFlameGameWidget extends StatelessWidget {
+  final FlameGame game;
+
+  const SpineFlameGameWidget(this.game, {super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(appBar: AppBar(title: const Text('Flame Integration')), body: GameWidget(game: game));
+  }
+}

+ 52 - 0
spine-flutter/example/lib/ik_following.dart

@@ -0,0 +1,52 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class IkFollowing extends StatefulWidget {
+  const IkFollowing({Key? key}) : super(key: key);
+
+  @override
+  IkFollowingState createState() => IkFollowingState();
+}
+
+class IkFollowingState extends State<IkFollowing> {
+  late SpineWidgetController controller;
+  Offset? crossHairPosition;
+
+  @override
+  void initState() {
+    super.initState();
+
+    controller = SpineWidgetController(onInitialized: (controller) {
+      // Set the walk animation on track 0, let it loop
+      controller.animationState.setAnimationByName(0, "walk", true);
+      controller.animationState.setAnimationByName(1, "aim", true);
+    }, onAfterUpdateWorldTransforms: (controller) {
+      var worldPosition = crossHairPosition;
+      if (worldPosition == null) return;
+      var bone = controller.skeleton.findBone("crosshair");
+      if (bone == null) return;
+      var parent = bone.getParent();
+      if (parent == null) return;
+      var position = parent.worldToLocal(worldPosition.dx, worldPosition.dy);
+      bone.setX(position.x);
+      bone.setY(position.y);
+    });
+  }
+
+  void _updateBonePosition(Offset position) {
+    crossHairPosition = controller.toSkeletonCoordinates(position);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+
+    return Scaffold(
+        appBar: AppBar(title: const Text('IK Following')),
+        body: GestureDetector(
+          onPanDown: (drag) => _updateBonePosition(drag.localPosition),
+          onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
+          child: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
+        ));
+  }
+}

+ 139 - 0
spine-flutter/example/lib/main.dart

@@ -0,0 +1,139 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+import 'package:spine_flutter_example/animated_login.dart';
+import 'package:spine_flutter_example/debug_rendering.dart';
+
+import 'animation_state_events.dart';
+import 'dress_up.dart';
+import 'flame_example.dart';
+import 'ik_following.dart';
+import 'pause_play_animation.dart';
+import 'simple_animation.dart';
+
+class ExampleSelector extends StatelessWidget {
+  const ExampleSelector({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    const spacer = SizedBox(height: 10);
+    return Scaffold(
+        appBar: AppBar(title: const Text('Spine Examples')),
+        body: Center(
+            child: Column(mainAxisSize: MainAxisSize.min, children: [
+          ElevatedButton(
+            child: const Text('Simple Animation'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const SimpleAnimation(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Pause/Play animation'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const PlayPauseAnimation(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Animation State Listener'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const AnimationStateEvents(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Debug Rendering'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const DebugRendering(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Animated login'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const AnimatedLogin(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Dress Up'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const DressUp(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('IK Following'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => const IkFollowing(),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Flame: Simple Example'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => SpineFlameGameWidget(SimpleFlameExample()),
+                ),
+              );
+            },
+          ),
+          spacer,
+          ElevatedButton(
+            child: const Text('Flame: Pre-load and share Spine data'),
+            onPressed: () {
+              Navigator.push(
+                context,
+                MaterialPageRoute<void>(
+                  builder: (context) => SpineFlameGameWidget(PreloadAndShareSpineDataExample()),
+                ),
+              );
+            },
+          ),
+          spacer
+        ])));
+  }
+}
+
+void main() async {
+  WidgetsFlutterBinding.ensureInitialized();
+  await initSpineFlutter(enableMemoryDebugging: false);
+  runApp(const MaterialApp(title: "Spine Examples", home: ExampleSelector()));
+}

+ 49 - 0
spine-flutter/example/lib/pause_play_animation.dart

@@ -0,0 +1,49 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class PlayPauseAnimation extends StatefulWidget {
+  const PlayPauseAnimation({Key? key}) : super(key: key);
+
+  @override
+  PlayPauseAnimationState createState() => PlayPauseAnimationState();
+}
+
+class PlayPauseAnimationState extends State<PlayPauseAnimation> {
+  late SpineWidgetController controller;
+
+  @override
+  void initState() {
+    super.initState();
+    controller = SpineWidgetController(onInitialized: (controller) {
+      controller.animationState.setAnimationByName(0, "flying", true);
+    });
+  }
+
+  void _togglePlay() {
+    if (controller.isPlaying) {
+      controller.pause();
+    } else {
+      controller.resume();
+    }
+    setState(() {});
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Play/Pause')),
+      body: SpineWidget.fromAsset(
+        "assets/dragon.atlas",
+        "assets/dragon-ess.skel",
+        controller,
+        boundsProvider: SkinAndAnimationBounds(animation: "flying"),
+      ),
+      floatingActionButton: FloatingActionButton(
+        onPressed: _togglePlay,
+        child: Icon(controller.isPlaying ? Icons.pause : Icons.play_arrow),
+      ),
+    );
+  }
+}

+ 20 - 0
spine-flutter/example/lib/simple_animation.dart

@@ -0,0 +1,20 @@
+import 'package:spine_flutter/spine_flutter.dart';
+import 'package:flutter/material.dart';
+
+class SimpleAnimation extends StatelessWidget {
+  const SimpleAnimation({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      // Set the walk animation on track 0, let it loop
+      controller.animationState.setAnimationByName(0, "walk", true);
+    });
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Simple Animation')),
+      body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
+    );
+  }
+}

+ 1 - 0
spine-flutter/example/linux/.gitignore

@@ -0,0 +1 @@
+flutter/ephemeral

+ 138 - 0
spine-flutter/example/linux/CMakeLists.txt

@@ -0,0 +1,138 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "spine_flutter_example")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.spine_flutter")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  set(CMAKE_BUILD_TYPE "Debug" CACHE
+    STRING "Flutter build mode" FORCE)
+  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+    "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+  target_compile_features(${TARGET} PUBLIC cxx_std_14)
+  target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+  target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
+  target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+  "main.cc"
+  "my_application.cc"
+  "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+  PROPERTIES
+  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+  set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+  file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+  " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+  COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+  COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+  COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+  install(FILES "${bundled_library}"
+    DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+    COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+  file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+  " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+  DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+  install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+    COMPONENT Runtime)
+endif()

+ 88 - 0
spine-flutter/example/linux/flutter/CMakeLists.txt

@@ -0,0 +1,88 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+    set(NEW_LIST "")
+    foreach(element ${${LIST_NAME}})
+        list(APPEND NEW_LIST "${PREFIX}${element}")
+    endforeach(element)
+    set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+  "fl_basic_message_channel.h"
+  "fl_binary_codec.h"
+  "fl_binary_messenger.h"
+  "fl_dart_project.h"
+  "fl_engine.h"
+  "fl_json_message_codec.h"
+  "fl_json_method_codec.h"
+  "fl_message_codec.h"
+  "fl_method_call.h"
+  "fl_method_channel.h"
+  "fl_method_codec.h"
+  "fl_method_response.h"
+  "fl_plugin_registrar.h"
+  "fl_plugin_registry.h"
+  "fl_standard_message_codec.h"
+  "fl_standard_method_codec.h"
+  "fl_string_codec.h"
+  "fl_value.h"
+  "fl_view.h"
+  "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+  "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+  PkgConfig::GTK
+  PkgConfig::GLIB
+  PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+    ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+  COMMAND ${CMAKE_COMMAND} -E env
+    ${FLUTTER_TOOL_ENVIRONMENT}
+    "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+  VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+  "${FLUTTER_LIBRARY}"
+  ${FLUTTER_LIBRARY_HEADERS}
+)

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