dress_up.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. ///
  2. /// Spine Runtimes License Agreement
  3. /// Last updated July 28, 2023. Replaces all prior versions.
  4. ///
  5. /// Copyright (c) 2013-2023, Esoteric Software LLC
  6. ///
  7. /// Integration of the Spine Runtimes into software or otherwise creating
  8. /// derivative works of the Spine Runtimes is permitted under the terms and
  9. /// conditions of Section 2 of the Spine Editor License Agreement:
  10. /// http://esotericsoftware.com/spine-editor-license
  11. ///
  12. /// Otherwise, it is permitted to integrate the Spine Runtimes into software or
  13. /// otherwise create derivative works of the Spine Runtimes (collectively,
  14. /// "Products"), provided that each user of the Products must obtain their own
  15. /// Spine Editor license and redistribution of the Products in any form must
  16. /// include this license and copyright notice.
  17. ///
  18. /// THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. /// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. /// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. /// DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. /// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. /// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. /// BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. /// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. /// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
  27. /// SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. ///
  29. import 'package:spine_flutter/raw_image_provider.dart';
  30. import 'package:spine_flutter/spine_flutter.dart';
  31. import 'package:flutter/material.dart';
  32. import 'package:flutter/painting.dart' as painting;
  33. class DressUp extends StatefulWidget {
  34. const DressUp({Key? key}) : super(key: key);
  35. @override
  36. DressUpState createState() => DressUpState();
  37. }
  38. class DressUpState extends State<DressUp> {
  39. static const double thumbnailSize = 200;
  40. late SkeletonDrawable _drawable;
  41. Skin? _customSkin;
  42. final Map<String, RawImageData> _skinImages = {};
  43. final Map<String, bool> _selectedSkins = {};
  44. @override
  45. void initState() {
  46. reportLeaks();
  47. super.initState();
  48. SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) async {
  49. _drawable = drawable;
  50. for (var skin in drawable.skeletonData.getSkins()) {
  51. if (skin.getName() == "default") continue;
  52. var skeleton = drawable.skeleton;
  53. skeleton.setSkin(skin);
  54. skeleton.setToSetupPose();
  55. skeleton.update(0);
  56. skeleton.updateWorldTransform(Physics.update);
  57. _skinImages[skin.getName()] = await drawable.renderToRawImageData(thumbnailSize, thumbnailSize, 0xffffffff);
  58. _selectedSkins[skin.getName()] = false;
  59. }
  60. _toggleSkin("full-skins/girl");
  61. setState(() {});
  62. });
  63. }
  64. void _toggleSkin(String skinName) {
  65. _selectedSkins[skinName] = !_selectedSkins[skinName]!;
  66. _drawable.skeleton.setSkinByName("default");
  67. if (_customSkin != null) _customSkin?.dispose();
  68. _customSkin = Skin("custom-skin");
  69. for (var skinName in _selectedSkins.keys) {
  70. if (_selectedSkins[skinName] == true) {
  71. var skin = _drawable.skeletonData.findSkin(skinName);
  72. if (skin != null) _customSkin?.addSkin(skin);
  73. }
  74. }
  75. _drawable.skeleton.setSkin(_customSkin!);
  76. _drawable.skeleton.setSlotsToSetupPose();
  77. }
  78. @override
  79. Widget build(BuildContext context) {
  80. final controller = SpineWidgetController(onInitialized: (controller) {
  81. controller.animationState.setAnimationByName(0, "dance", true);
  82. });
  83. return Scaffold(
  84. appBar: AppBar(title: const Text('Dress Up')),
  85. body: _skinImages.isEmpty
  86. ? const SizedBox()
  87. : Row(children: [
  88. SizedBox(
  89. width: thumbnailSize,
  90. child: ListView(
  91. children: _skinImages.keys.map((skinName) {
  92. var rawImageData = _skinImages[skinName]!;
  93. var image = Image(image: RawImageProvider(rawImageData));
  94. var box = SizedBox(width: 200, height: 200, child: image);
  95. return GestureDetector(
  96. onTap: () {
  97. _toggleSkin(skinName);
  98. setState(() {});
  99. },
  100. child: _selectedSkins[skinName] == true
  101. ? box
  102. // Does not work on web.
  103. //: ColorFiltered(colorFilter: const ColorFilter.mode(Colors.grey, painting.BlendMode.saturation,), child: box)
  104. : Container(
  105. foregroundDecoration: const BoxDecoration(
  106. color: Colors.grey,
  107. backgroundBlendMode: painting.BlendMode.saturation,
  108. ),
  109. child: box));
  110. }).toList()),
  111. ),
  112. Expanded(
  113. child: SpineWidget.fromDrawable(
  114. _drawable,
  115. controller,
  116. boundsProvider: SkinAndAnimationBounds(skins: ["full-skins/girl"]),
  117. ))
  118. ]));
  119. }
  120. @override
  121. void dispose() {
  122. super.dispose();
  123. _drawable.dispose();
  124. _customSkin?.dispose();
  125. }
  126. }