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

This commit is contained in:
Mario Zechner 2022-08-26 14:15:05 +02:00
parent 2438f0c39e
commit 2e78d64a23
6 changed files with 97 additions and 21 deletions

View File

@ -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"),
);
}
}

View File

@ -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>

View File

@ -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:

View File

@ -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) {

View File

@ -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(() {});

View File

@ -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