You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何从网络加载Sprite Sheet并分割为List<Image>?

替代Flame实现网络精灵图分割为List的方案

你需要在非游戏Flutter项目中从网络拉取精灵图,按JSON配置分割成List<ui.Image>,Flame的方案因依赖本地资源、输出SpriteWidget不符合需求,这里提供基于Flutter原生API的实现方案。

核心思路

  • 用原生dart:ui与HTTP工具加载网络图片和配置文件
  • 通过Canvas裁剪精灵图的指定区域,生成独立的ui.Image对象

具体实现代码

1. 导入依赖

import 'dart:ui' as ui;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

2. 加载网络图片为ui.Image

Future<ui.Image> loadNetworkSpriteSheet(String imageUrl) async {
  final response = await http.get(Uri.parse(imageUrl));
  if (response.statusCode != 200) {
    throw Exception('Failed to load sprite sheet image');
  }
  final codec = await ui.instantiateImageCodec(response.bodyBytes);
  final frame = await codec.getNextFrame();
  return frame.image;
}

3. 加载并解析精灵图配置JSON

Future<Map<String, dynamic>> loadSpriteConfig(String configUrl) async {
  final response = await http.get(Uri.parse(configUrl));
  if (response.statusCode != 200) {
    throw Exception('Failed to load sprite config');
  }
  return jsonDecode(response.body);
}

4. 分割精灵图为List<ui.Image>

Future<List<ui.Image>> splitSpriteSheet(ui.Image sheetImage, Map<String, dynamic> config) async {
  final frames = config['frames'] as Map<String, dynamic>;
  final List<ui.Image> spriteList = [];

  for (final frameEntry in frames.entries) {
    final frameData = frameEntry.value['frame'] as Map<String, dynamic>;
    // 根据你的JSON结构调整字段名,示例适配常见的精灵图JSON格式
    final x = frameData['x'] as int;
    final y = frameData['y'] as int;
    final width = frameData['w'] ?? frameData['width'] as int;
    final height = frameData['h'] ?? frameData['height'] as int;

    // 用PictureRecorder绘制裁剪后的子图
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);

    // 源图中的裁剪区域
    final sourceRect = Rect.fromLTWH(x.toDouble(), y.toDouble(), width.toDouble(), height.toDouble());
    // 目标画布的显示区域
    final destRect = Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble());

    // 将源图指定区域绘制到目标画布
    canvas.drawImageRect(
      sheetImage,
      sourceRect,
      destRect,
      Paint(),
    );

    final picture = recorder.endRecording();
    final spriteImage = await picture.toImage(width, height);
    spriteList.add(spriteImage);
  }

  return spriteList;
}

5. 整合使用示例

void main() async {
  // 必须初始化Flutter UI绑定,否则无法使用dart:ui的图片API
  WidgetsFlutterBinding.ensureInitialized();

  try {
    const sheetUrl = 'https://your-domain.com/spritesheet.png';
    const configUrl = 'https://your-domain.com/spritesheet.json';

    final sheetImage = await loadNetworkSpriteSheet(sheetUrl);
    final spriteConfig = await loadSpriteConfig(configUrl);
    final spriteImages = await splitSpriteSheet(sheetImage, spriteConfig);

    // spriteImages就是你需要的List<ui.Image>,可直接转换为Flutter的Image组件
    final spriteWidgets = spriteImages.map((img) {
      return Image(image: MemoryImage(img.toByteData()!.buffer.asUint8List()));
    }).toList();
  } catch (e) {
    print('Error: $e');
  }
}

注意事项

  • 资源释放:ui.Image是原生资源,不再使用时需调用img.dispose()避免内存泄漏
  • JSON字段适配:根据实际JSON结构调整frameData中的字段名(比如部分JSON用width而非w)
  • 异常处理:代码包含基础异常捕获,可根据项目需求扩展错误处理逻辑

内容的提问来源于stack exchange,提问作者Kürşat Şayhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 10:33:11