mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
[flutter] Assets can now be loaded from bundles, local files, or via http.
This commit is contained in:
parent
2438f0c39e
commit
2e78d64a23
@ -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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(() {});
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user