Преглед на файлове

[flutter] Fix dependency cycle with widget->drawable, refactor examples.

Mario Zechner преди 2 години
родител
ревизия
7bb1d65bfe

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

@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+import 'package:spine_flutter/spine_flutter.dart';
+
+class AnimationStateEvents extends StatelessWidget {
+  const AnimationStateEvents({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+    final controller = SpineWidgetController((controller) {
+      for (final bone in controller.skeleton.getBones()) {
+        print(bone);
+      }
+      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.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller)
+          )
+        ]
+      )
+    );
+  }
+}

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

@@ -0,0 +1,51 @@
+import 'dart:ui' as ui;
+
+import 'package:flutter/material.dart';
+import 'package:spine_flutter/spine_flutter.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;
+  SkeletonDrawable? _drawable;
+  final List<Image> _skinImages = [];
+
+  @override
+  void initState() {
+    super.initState();
+    SkeletonDrawable.fromAsset("assets/mix-and-match-pro.skel", "assets/mix-and-match.atlas").then((drawable) async {
+      for (var skin in drawable.skeletonData.getSkins()) {
+        var recorder = ui.PictureRecorder();
+        var canvas = Canvas(recorder, const Rect.fromLTWH(0, 0, thumbnailSize, thumbnailSize));
+        canvas.drawRect(Rect(0, 0, 200, 200), ColorP)
+        var imageData = await (await recorder.endRecording().toImage(thumbnailSize.toInt(), thumbnailSize.toInt())).toByteData(format: ui.ImageByteFormat.png);
+        _skinImages.add(Image.memory(imageData!.buffer.asUint8List()));
+      }
+      _drawable = drawable;
+      setState(() {});
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+        appBar: AppBar(title: const Text('Skins')),
+        body: _skinImages.isEmpty
+            ? const SizedBox()
+            : Row(
+            children: [
+              Expanded(
+                  child:ListView(
+                      children: _skinImages
+                  )
+              ),
+            ]
+        )
+    );
+  }
+}

+ 21 - 182
spine-flutter/example/lib/main.dart

@@ -1,8 +1,14 @@
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:spine_flutter/spine_flutter.dart';
+
+import 'simple_animation.dart';
+import 'animation_state_events.dart';
+import 'pause_play_animation.dart';
+import 'skins.dart';
+import 'dress_up.dart';
 
 class ExampleSelector extends StatelessWidget {
+  const ExampleSelector({super.key});
+
   @override
   Widget build(BuildContext context) {
     const spacer = SizedBox(height: 10);
@@ -59,6 +65,18 @@ class ExampleSelector extends StatelessWidget {
                   );
                 },
               ),
+              spacer,
+              ElevatedButton(
+                child: const Text('Skins'),
+                onPressed: () {
+                  Navigator.push(
+                    context,
+                    MaterialPageRoute<void>(
+                      builder: (context) => const DressUp(),
+                    ),
+                  );
+                },
+              ),
               spacer
             ]
           )
@@ -67,187 +85,8 @@ class ExampleSelector extends StatelessWidget {
   }
 }
 
-class SimpleAnimation extends StatelessWidget {
-  const SimpleAnimation({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    reportLeaks();
-    final controller = SpineWidgetController((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.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
-      // body: SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", controller),
-      // body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
-      // body: SpineWidget.asset("assets/skeleton.json", "assets/skeleton.atlas", controller, alignment: Alignment.topLeft, fit: BoxFit.cover),
-    );
-  }
-}
-
-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((controller) {
-      controller.animationState?.setAnimationByName(0, "walk", true);
-    });
-  }
-
-  void _togglePlaystate() {
-    _controller.togglePlay();
-    setState(() {});
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    reportLeaks();
-
-    return Scaffold(
-      appBar: AppBar(title: const Text('Play/Pause')),
-      body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", _controller),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _togglePlaystate,
-        child: Icon(_controller.isPlaying ? Icons.pause : Icons.play_arrow),
-      ),
-    );
-  }
-}
-
-class Skins extends StatefulWidget {
-  const Skins({Key? key}) : super(key: key);
-
-  @override
-  SkinsState createState() => SkinsState();
-}
-
-class SkinsState extends State<Skins> {
-  Atlas? _atlas;
-  SkeletonData? _skeletonData;
-  final Map<String, bool> _availableSkins = {};
-  Skin? _customSkin;
-  late SpineWidgetController _controller;
-
-  @override
-  void initState() {
-    super.initState();
-
-    Atlas.fromAsset(rootBundle, "assets/mix-and-match.atlas").then((atlas) async {
-      _skeletonData = await SkeletonData.fromAsset(atlas, rootBundle, "assets/mix-and-match-pro.skel");
-      _atlas = atlas;
-      for (var skin in _skeletonData?.getSkins() ?? []) {
-        _availableSkins[skin.getName()] = false;
-      }
-
-      _controller = SpineWidgetController((controller) {
-        controller.animationState?.setAnimationByName(0, "walk", true);
-      });
-
-      setState(() => _toggleSkin("full-skins/girl"));
-    });
-  }
-
-  void _toggleSkin(String skinName) {
-    _availableSkins[skinName] = !_availableSkins[skinName]!;
-
-    if (_customSkin != null) {
-      _customSkin?.dispose();
-      _customSkin = null;
-    }
-
-    _customSkin = Skin("custom-skin");
-    for (var skinName in _availableSkins.keys) {
-      if (_availableSkins[skinName] == true) {
-        var skin = _controller.skeletonData?.findSkin(skinName);
-        if (skin != null)
-          _customSkin?.addSkin(skin);
-      }
-    }
-    _controller.skeleton?.setSkin(_customSkin!);
-    _controller.skeleton?.setToSetupPose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Scaffold(
-        appBar: AppBar(title: const Text('Skins')),
-        body: _skeletonData == null
-            ? SizedBox()
-            : Row(
-            children: [
-              Expanded(
-                  child:ListView(
-                    children: _availableSkins.keys.map((skinName) {
-                      return CheckboxListTile(
-                        title: Text(skinName),
-                        value: _availableSkins[skinName],
-                        onChanged: (bool? value) {
-                          _toggleSkin(skinName);
-                          setState(() => {});
-                        },
-                      );
-                    }).toList()
-                  )
-              ),
-              Expanded(
-                  child: SpineWidget.raw(_skeletonData, _atlas, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]))
-              )
-            ]
-        )
-    );
-  }
-}
-
-class AnimationStateEvents extends StatelessWidget {
-  const AnimationStateEvents({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    reportLeaks();
-    final controller = SpineWidgetController((controller) {
-      for (final bone in controller.skeleton!.getBones()) {
-        print(bone);
-      }
-      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: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
-    );
-  }
-}
-
 void main() {
-  runApp(MaterialApp(
+  runApp(const MaterialApp(
       title: "Spine Examples",
       home: ExampleSelector()
   ));

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

@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import 'package:spine_flutter/spine_flutter.dart';
+
+class PlayPauseAnimation extends StatefulWidget {
+  const PlayPauseAnimation({Key? key}) : super(key: key);
+
+  @override
+  PlayPauseAnimationState createState() => PlayPauseAnimationState();
+}
+
+class PlayPauseAnimationState extends State<PlayPauseAnimation> {
+  late SpineWidgetController controller;
+  late bool isPlaying;
+
+  @override
+  void initState() {
+    super.initState();
+    controller = SpineWidgetController((controller) {
+      controller.animationState.setAnimationByName(0, "walk", true);
+    });
+    isPlaying = true;
+  }
+
+  void _togglePlay() {
+    isPlaying = !isPlaying;
+    controller.animationState.setTimeScale(isPlaying ? 1 : 0);
+    setState(() {});
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Play/Pause')),
+      body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
+      floatingActionButton: FloatingActionButton(
+        onPressed: _togglePlay,
+        child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
+      ),
+    );
+  }
+}

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

@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+import 'package:spine_flutter/spine_flutter.dart';
+
+class SimpleAnimation extends StatelessWidget {
+  const SimpleAnimation({Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    reportLeaks();
+    final controller = SpineWidgetController((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.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
+      // body: SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", controller),
+      // body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
+      // body: SpineWidget.asset("assets/skeleton.json", "assets/skeleton.atlas", controller, alignment: Alignment.topLeft, fit: BoxFit.cover),
+    );
+  }
+}

+ 79 - 0
spine-flutter/example/lib/skins.dart

@@ -0,0 +1,79 @@
+import 'package:flutter/material.dart';
+import 'package:spine_flutter/spine_flutter.dart';
+
+class Skins extends StatefulWidget {
+  const Skins({Key? key}) : super(key: key);
+
+  @override
+  SkinsState createState() => SkinsState();
+}
+
+class SkinsState extends State<Skins> {
+  SkeletonDrawable? _drawable;
+  late SpineWidgetController _controller;
+  final Map<String, bool> _selectedSkins = {};
+  Skin? _customSkin;
+
+  @override
+  void initState() {
+    super.initState();
+    SkeletonDrawable.fromAsset("assets/mix-and-match-pro.skel", "assets/mix-and-match.atlas").then((drawable) {
+      for (var skin in drawable.skeletonData.getSkins()) {
+        _selectedSkins[skin.getName()] = false;
+      }
+      _controller = SpineWidgetController((controller) {
+        controller.animationState.setAnimationByName(0, "walk", true);
+      });
+      drawable.skeleton.setSkinByName("full-skins/girl");
+      _selectedSkins["full-skins/girl"] = true;
+      _drawable = drawable;
+      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 = _controller.skeletonData.findSkin(skinName);
+        if (skin != null) _customSkin?.addSkin(skin);
+      }
+    }
+    _controller.skeleton.setSkin(_customSkin!);
+    _controller.skeleton.setToSetupPose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+        appBar: AppBar(title: const Text('Skins')),
+        body: _drawable == null
+            ? const SizedBox()
+            : Row(
+            children: [
+              Expanded(
+                  child:ListView(
+                      children: _selectedSkins.keys.map((skinName) {
+                        return CheckboxListTile(
+                          title: Text(skinName),
+                          value: _selectedSkins[skinName],
+                          onChanged: (bool? value) {
+                            _toggleSkin(skinName);
+                            setState(() => {});
+                          },
+                        );
+                      }).toList()
+                  )
+              ),
+              Expanded(
+                  child: SpineWidget.drawable(_drawable, _controller, boundsProvider: SkinAndAnimationBounds(["full-skins/girl"]))
+              )
+            ]
+        )
+    );
+  }
+}

+ 39 - 4
spine-flutter/lib/spine_flutter.dart

@@ -143,11 +143,27 @@ class SkeletonData {
     return SkeletonData._(result.skeletonData);
   }
 
-  static Future<SkeletonData> fromAsset(Atlas atlas, AssetBundle assetBundle, String skeletonFileName) async {
-    if (skeletonFileName.endsWith(".json")) {
-      return fromJson(atlas, await assetBundle.loadString(skeletonFileName));
+  static Future<SkeletonData> fromAsset(AssetBundle assetBundle, Atlas atlas, String skeletonFile) async {
+    if (skeletonFile.endsWith(".json")) {
+      return fromJson(atlas, await assetBundle.loadString(skeletonFile));
     } else {
-      return fromBinary(atlas, (await assetBundle.load(skeletonFileName)).buffer.asUint8List());
+      return fromBinary(atlas, (await assetBundle.load(skeletonFile)).buffer.asUint8List());
+    }
+  }
+
+  static Future<SkeletonData> fromFile(Atlas atlas, String skeletonFile) async {
+    if (skeletonFile.endsWith(".json")) {
+      return fromJson(atlas, convert.utf8.decode(await File(skeletonFile).readAsBytes()));
+    } else {
+      return fromBinary(atlas, await File(skeletonFile).readAsBytes());
+    }
+  }
+
+  static Future<SkeletonData> fromHttp(Atlas atlas, String skeletonFile) async {
+    if (skeletonFile.endsWith(".json")) {
+      return fromJson(atlas, convert.utf8.decode((await http.get(Uri.parse(skeletonFile))).bodyBytes));
+    } else {
+      return fromBinary(atlas, (await http.get(Uri.parse(skeletonFile))).bodyBytes);
     }
   }
 
@@ -3165,6 +3181,25 @@ class SkeletonDrawable {
     animationState = AnimationState._(_drawable.ref.animationState, _drawable.ref.animationStateEvents);
   }
 
+
+  static Future<SkeletonDrawable> fromAsset(String skeletonFile, String atlasFile) async {
+    var atlas = await Atlas.fromAsset(rootBundle, atlasFile);
+    var skeletonData = await SkeletonData.fromAsset(rootBundle, atlas, skeletonFile);
+    return SkeletonDrawable(atlas, skeletonData, true);
+  }
+
+  static Future<SkeletonDrawable> fromFile(String skeletonFile, String atlasFile) async {
+    var atlas = await Atlas.fromFile(atlasFile);
+    var skeletonData = await SkeletonData.fromFile(atlas, skeletonFile);
+    return SkeletonDrawable(atlas, skeletonData, true);
+  }
+
+  static Future<SkeletonDrawable> fromHttp(String skeletonFile, String atlasFile) async {
+    var atlas = await Atlas.fromUrl(atlasFile);
+    var skeletonData = await SkeletonData.fromHttp(atlas, skeletonFile);
+    return SkeletonDrawable(atlas, skeletonData, true);
+  }
+
   void update(double delta) {
     if (_disposed) return;
     animationState.update(delta);

+ 75 - 89
spine-flutter/lib/spine_widget.dart

@@ -11,50 +11,51 @@ import 'package:http/http.dart' as http;
 import 'spine_flutter.dart';
 
 class SpineWidgetController {
-  Atlas? _atlas;
-  SkeletonData? _data;
   SkeletonDrawable? _drawable;
   final void Function(SpineWidgetController controller)? onInitialized;
   bool initialized = false;
 
   SpineWidgetController([this.onInitialized]);
 
-  void _initialize(Atlas atlas, SkeletonData data, SkeletonDrawable drawable) {
-    if (initialized)
-      throw Exception("SpineWidgetController already initialized. A controller can only be used with one widget.");
-    _atlas = atlas;
-    _data = data;
+  void _initialize(SkeletonDrawable drawable) {
+    if (_drawable != null) throw Exception("SpineWidgetController already initialized. A controller can only be used with one widget.");
     _drawable = drawable;
-    onInitialized?.call(this);
     initialized = true;
+    onInitialized?.call(this);
   }
 
-  Atlas? get atlas => _atlas;
-
-  SkeletonData? get skeletonData => _data;
-
-  AnimationStateData? get animationStateData => _drawable?.animationStateData;
-
-  AnimationState? get animationState => _drawable?.animationState;
+  Atlas get atlas  {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+    return _drawable!.atlas;
+  }
 
-  Skeleton? get skeleton => _drawable?.skeleton;
+  SkeletonData get skeletonData {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+    return _drawable!.skeletonData;
+  }
 
-  void pause() {
-    _drawable?.animationState.setTimeScale(0);
+  AnimationStateData get animationStateData {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+    return _drawable!.animationStateData;
   }
 
-  void play() {
-    _drawable?.animationState.setTimeScale(1);
+  AnimationState get animationState {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+    return _drawable!.animationState;
   }
 
-  void togglePlay() {
-    _drawable?.animationState.setTimeScale(isPlaying ? 0 : 1);
+  Skeleton get skeleton {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+      return _drawable!.skeleton;
   }
 
-  bool get isPlaying => _drawable?.animationState.getTimeScale() != 0;
+  SkeletonDrawable get drawable {
+    if (_drawable == null) throw Exception("Controller is not initialized yet.");
+    return _drawable!;
+  }
 }
 
-enum AssetType { Asset, File, Http, Raw }
+enum AssetType { Asset, File, Http, Drawable }
 
 abstract class BoundsProvider {
   const BoundsProvider();
@@ -122,51 +123,47 @@ class ComputedBounds extends BoundsProvider {
 
 class SpineWidget extends StatefulWidget {
   final AssetType _assetType;
-  final String? skeletonFile;
-  final String? atlasFile;
-  final SkeletonData? skeletonData;
-  final Atlas? atlas;
-  final SpineWidgetController controller;
-  final BoxFit fit;
-  final Alignment alignment;
-  final BoundsProvider boundsProvider;
-  final bool sizedByBounds;
-
-  const SpineWidget.asset(this.skeletonFile, this.atlasFile, this.controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+  final String? _skeletonFile;
+  final String? _atlasFile;
+  final SkeletonDrawable? _drawable;
+  final SpineWidgetController _controller;
+  final BoxFit _fit;
+  final Alignment _alignment;
+  final BoundsProvider _boundsProvider;
+  final bool _sizedByBounds;
+
+  const SpineWidget.asset(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.Asset,
-        fit = fit ?? BoxFit.contain,
-        alignment = alignment ?? Alignment.center,
-        boundsProvider = boundsProvider ?? const SetupPoseBounds(),
-        sizedByBounds = sizedByBounds ?? false,
-        skeletonData = null,
-        atlas = null;
-
-  const SpineWidget.file(this.skeletonFile, this.atlasFile, this.controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+        _fit = fit ?? BoxFit.contain,
+        _alignment = alignment ?? Alignment.center,
+        _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
+        _sizedByBounds = sizedByBounds ?? false,
+        _drawable = null;
+
+  const SpineWidget.file(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.File,
-        fit = fit ?? BoxFit.contain,
-        alignment = alignment ?? Alignment.center,
-        boundsProvider = boundsProvider ?? const SetupPoseBounds(),
-        sizedByBounds = sizedByBounds ?? false,
-        skeletonData = null,
-        atlas = null;
-
-  const SpineWidget.http(this.skeletonFile, this.atlasFile, this.controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+        _fit = fit ?? BoxFit.contain,
+        _alignment = alignment ?? Alignment.center,
+        _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
+        _sizedByBounds = sizedByBounds ?? false,
+        _drawable = null;
+
+  const SpineWidget.http(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.Http,
-        fit = fit ?? BoxFit.contain,
-        alignment = alignment ?? Alignment.center,
-        boundsProvider = boundsProvider ?? const SetupPoseBounds(),
-        sizedByBounds = sizedByBounds ?? false,
-        skeletonData = null,
-        atlas = null;
-
-  const SpineWidget.raw(this.skeletonData, this.atlas, this.controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
-      : _assetType = AssetType.Raw,
-        fit = fit ?? BoxFit.contain,
-        alignment = alignment ?? Alignment.center,
-        boundsProvider = boundsProvider ?? const SetupPoseBounds(),
-        sizedByBounds = sizedByBounds ?? false,
-        skeletonFile = null,
-        atlasFile = null;
+        _fit = fit ?? BoxFit.contain,
+        _alignment = alignment ?? Alignment.center,
+        _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
+        _sizedByBounds = sizedByBounds ?? false,
+        _drawable = null;
+
+  const SpineWidget.drawable(this._drawable, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+      : _assetType = AssetType.Drawable,
+        _fit = fit ?? BoxFit.contain,
+        _alignment = alignment ?? Alignment.center,
+        _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
+        _sizedByBounds = sizedByBounds ?? false,
+        _skeletonFile = null,
+        _atlasFile = null;
 
   @override
   State<SpineWidget> createState() => _SpineWidgetState();
@@ -178,16 +175,16 @@ class _SpineWidgetState extends State<SpineWidget> {
   @override
   void initState() {
     super.initState();
-    if (widget._assetType == AssetType.Raw) {
-      loadRaw(widget.skeletonData!, widget.atlas!);
+    if (widget._assetType == AssetType.Drawable) {
+      loadDrawable(widget._drawable!);
     } else {
-      loadFromAsset(widget.skeletonFile!, widget.atlasFile!, widget._assetType);
+      loadFromAsset(widget._skeletonFile!, widget._atlasFile!, widget._assetType);
     }
   }
 
-  void loadRaw(SkeletonData skeletonData, Atlas atlas) {
-    skeletonDrawable = SkeletonDrawable(atlas, skeletonData, false);
-    widget.controller._initialize(atlas, skeletonData, skeletonDrawable!);
+  void loadDrawable(SkeletonDrawable drawable) {
+    skeletonDrawable = drawable;
+    widget._controller._initialize(skeletonDrawable!);
     skeletonDrawable?.update(0);
     setState(() {});
   }
@@ -198,35 +195,24 @@ class _SpineWidgetState extends State<SpineWidget> {
 
     switch (assetType) {
       case AssetType.Asset:
-        atlas = await Atlas.fromAsset(rootBundle, atlasFile);
-        skeletonData = skeletonFile.endsWith(".json")
-            ? SkeletonData.fromJson(atlas, await rootBundle.loadString(skeletonFile))
-            : SkeletonData.fromBinary(atlas, (await rootBundle.load(skeletonFile)).buffer.asUint8List());
+        loadDrawable(await SkeletonDrawable.fromAsset(skeletonFile, atlasFile));
         break;
       case AssetType.File:
-        atlas = await Atlas.fromFile(atlasFile);
-        skeletonData = skeletonFile.endsWith(".json")
-            ? SkeletonData.fromJson(atlas, utf8.decode(await File(skeletonFile).readAsBytes()))
-            : SkeletonData.fromBinary(atlas, await File(skeletonFile).readAsBytes());
+        loadDrawable(await SkeletonDrawable.fromFile(skeletonFile, atlasFile));
         break;
       case AssetType.Http:
-        atlas = await Atlas.fromUrl(atlasFile);
-        skeletonData = skeletonFile.endsWith(".json")
-            ? SkeletonData.fromJson(atlas, utf8.decode((await http.get(Uri.parse(skeletonFile))).bodyBytes))
-            : SkeletonData.fromBinary(atlas, (await http.get(Uri.parse(skeletonFile))).bodyBytes);
+        loadDrawable(await SkeletonDrawable.fromHttp(skeletonFile, atlasFile));
         break;
-      case AssetType.Raw:
-        throw Exception("Raw assets can not be loaded via loadFromAsset().");
+      case AssetType.Drawable:
+        throw Exception("Drawable can not be loaded via loadFromAsset().");
     }
-
-    loadRaw(skeletonData, atlas);
   }
 
   @override
   Widget build(BuildContext context) {
     if (skeletonDrawable != null) {
       print("Skeleton loaded, rebuilding painter");
-      return _SpineRenderObjectWidget(skeletonDrawable!, widget.fit, widget.alignment, widget.boundsProvider, widget.sizedByBounds);
+      return _SpineRenderObjectWidget(skeletonDrawable!, widget._fit, widget._alignment, widget._boundsProvider, widget._sizedByBounds);
     } else {
       print("Skeleton not loaded yet");
       return const SizedBox();