Browse Source

[flutter] Assets can now be loaded from bundles, local files, or via http.

Mario Zechner 3 years ago
parent
commit
2e78d64a23

+ 3 - 3
spine-flutter/example/lib/main.dart

@@ -37,9 +37,9 @@ class Spineboy extends StatelessWidget {
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(title: const Text('Spineboy')),
-      body: const Center(
-        child: SpineWidget("assets/spineboy-pro.skel", "assets/spineboy.atlas")
-      ),
+      body: const SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas"),
+      // body: const SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas"),
+      // body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
     );
   }
 }

+ 0 - 2
spine-flutter/example/macos/Runner/DebugProfile.entitlements

@@ -2,8 +2,6 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
-	<key>com.apple.security.app-sandbox</key>
-	<true/>
 	<key>com.apple.security.cs.allow-jit</key>
 	<true/>
 	<key>com.apple.security.network.server</key>

+ 21 - 0
spine-flutter/example/pubspec.lock

@@ -81,6 +81,20 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.13.5"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.0.1"
   lints:
     dependency: transitive
     description:
@@ -177,6 +191,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.4.9"
+  typed_data:
+    dependency: transitive
+    description:
+      name: typed_data
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.3.1"
   vector_math:
     dependency: transitive
     description:

+ 23 - 6
spine-flutter/lib/spine_flutter.dart

@@ -1,9 +1,11 @@
+import 'dart:convert' as convert;
 import 'dart:ffi';
 import 'dart:io';
 import 'dart:typed_data';
 import 'dart:ui';
 import 'package:ffi/ffi.dart';
 import 'package:flutter/rendering.dart';
+import 'package:http/http.dart' as http;
 
 import 'package:flutter/services.dart';
 import 'spine_flutter_bindings_generated.dart';
@@ -22,8 +24,9 @@ class SpineAtlas {
 
   SpineAtlas(this._atlas, this.atlasPages, this.atlasPagePaints): _disposed = false;
 
-  static Future<SpineAtlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
-    final atlasData = await assetBundle.loadString(atlasFileName);
+  static Future<SpineAtlas> _load(String atlasFileName, Future<Uint8List> Function(String name) loadFile) async {
+    final atlasBytes = await loadFile(atlasFileName);
+    final atlasData = convert.utf8.decode(atlasBytes);
     final atlasDataNative = atlasData.toNativeUtf8();
     final atlas = _bindings.spine_atlas_load(atlasDataNative.cast());
     calloc.free(atlasDataNative);
@@ -40,8 +43,8 @@ class SpineAtlas {
     for (int i = 0; i < atlas.ref.numImagePaths; i++) {
       final Pointer<Utf8> atlasPageFile = atlas.ref.imagePaths[i].cast();
       final imagePath = Path.join(atlasDir, atlasPageFile.toDartString());
-      var imageData = await assetBundle.load(imagePath);
-      final Codec codec = await instantiateImageCodec(imageData.buffer.asUint8List());
+      var imageData = await loadFile(imagePath);
+      final Codec codec = await instantiateImageCodec(imageData);
       final FrameInfo frameInfo = await codec.getNextFrame();
       final Image image = frameInfo.image;
       atlasPages.add(image);
@@ -54,6 +57,20 @@ class SpineAtlas {
     return SpineAtlas(atlas, atlasPages, atlasPagePaints);
   }
 
+  static Future<SpineAtlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
+    return _load(atlasFileName, (file) async => (await assetBundle.load(file)).buffer.asUint8List());
+  }
+
+  static Future<SpineAtlas> fromFile(String atlasFileName) async {
+    return _load(atlasFileName, (file) => File(file).readAsBytes());
+  }
+
+  static Future<SpineAtlas> fromUrl(String atlasFileName) async {
+    return _load(atlasFileName, (file) async {
+      return (await http.get(Uri.parse(file))).bodyBytes;
+    });
+  }
+
   void dispose() {
     if (_disposed) return;
     _disposed = true;
@@ -80,9 +97,9 @@ class SpineSkeletonData {
     return SpineSkeletonData(skeletonData);
   }
 
-  static SpineSkeletonData fromBinary(SpineAtlas atlas, ByteData binary) {
+  static SpineSkeletonData fromBinary(SpineAtlas atlas, Uint8List binary) {
     final Pointer<Uint8> binaryNative = malloc.allocate(binary.lengthInBytes);
-    binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary.buffer.asUint8List());
+    binaryNative.asTypedList(binary.lengthInBytes).setAll(0, binary);
     final skeletonData = _bindings.spine_skeleton_data_load_binary(atlas._atlas, binaryNative.cast(), binary.lengthInBytes);
     malloc.free(binaryNative);
     if (skeletonData.ref.error.address != nullptr.address) {

+ 49 - 10
spine-flutter/lib/spine_widget.dart

@@ -1,15 +1,31 @@
+import 'dart:convert';
+import 'dart:io';
+
 import 'package:flutter/rendering.dart';
 import 'package:flutter/scheduler.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
+import 'package:http/http.dart' as http;
 
 import 'spine_flutter.dart';
 
+abstract class SpineWidgetController {
+}
+
+enum AssetType {
+  Asset,
+  File,
+  Http
+}
+
 class SpineWidget extends StatefulWidget {
   final String skeletonFile;
   final String atlasFile;
+  final AssetType _assetType;
 
-  const SpineWidget(this.skeletonFile, this.atlasFile, {super.key});
+  const SpineWidget.asset(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.Asset;
+  const SpineWidget.file(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.File;
+  const SpineWidget.http(this.skeletonFile, this.atlasFile, {super.key}): _assetType = AssetType.Http;
 
   @override
   State<SpineWidget> createState() => _SpineWidgetState();
@@ -21,17 +37,40 @@ class _SpineWidgetState extends State<SpineWidget> {
   @override
   void initState() {
     super.initState();
-    loadSkeleton(widget.skeletonFile, widget.atlasFile);
+    loadSkeleton(widget.skeletonFile, widget.atlasFile, widget._assetType);
   }
 
-  void loadSkeleton(String skeletonFile, String atlasFile) async {
-    final atlas =
-    await SpineAtlas.fromAsset(rootBundle, atlasFile);
-    final skeletonData = skeletonFile.endsWith(".json")
-        ? SpineSkeletonData.fromJson(
-        atlas, await rootBundle.loadString(skeletonFile))
-        : SpineSkeletonData.fromBinary(
-        atlas, await rootBundle.load(skeletonFile));
+  void loadSkeleton(String skeletonFile, String atlasFile, AssetType assetType) async {
+    late SpineAtlas atlas;
+    late SpineSkeletonData skeletonData;
+
+    switch (assetType) {
+      case AssetType.Asset:
+        atlas = await SpineAtlas.fromAsset(rootBundle, atlasFile);
+        skeletonData = skeletonFile.endsWith(".json")
+            ? SpineSkeletonData.fromJson(
+            atlas, await rootBundle.loadString(skeletonFile))
+            : SpineSkeletonData.fromBinary(
+            atlas, (await rootBundle.load(skeletonFile)).buffer.asUint8List());
+        break;
+      case AssetType.File:
+        atlas = await SpineAtlas.fromFile(atlasFile);
+        skeletonData = skeletonFile.endsWith(".json")
+            ? SpineSkeletonData.fromJson(
+            atlas, utf8.decode(await File(skeletonFile).readAsBytes()))
+            : SpineSkeletonData.fromBinary(
+            atlas, await File(skeletonFile).readAsBytes());
+        break;
+      case AssetType.Http:
+        atlas = await SpineAtlas.fromUrl(atlasFile);
+        skeletonData = skeletonFile.endsWith(".json")
+            ? SpineSkeletonData.fromJson(
+            atlas, utf8.decode((await http.get(Uri.parse(skeletonFile))).bodyBytes))
+            : SpineSkeletonData.fromBinary(
+            atlas, (await http.get(Uri.parse(skeletonFile))).bodyBytes);
+        break;
+    }
+
     skeletonDrawable = SpineSkeletonDrawable(atlas, skeletonData);
     skeletonDrawable?.update(0.016);
     setState(() {});

+ 1 - 0
spine-flutter/pubspec.yaml

@@ -12,6 +12,7 @@ dependencies:
     sdk: flutter
   plugin_platform_interface: ^2.0.2
   ffi: ^1.1.2
+  http: ^0.13.5
 
 dev_dependencies:
   ffigen: ^4.1.2