Flutter中如何显示精灵表(Spritesheet)中的指定区域?
在Flutter非游戏应用中显示SpriteSheet中的单个Sprite(修复centerSlice报错)
你遇到的断言错误,根源是误用了centerSlice属性:centerSlice是专门用于9-Patch图像拉伸的,它要求原始图像必须完整显示(即sourceSize == inputSize),但你的需求是只显示图像的一部分,完全违背了这个属性的设计初衷,因此触发了报错。
以下是两种适合非游戏场景的正确实现方案:
方法1:ClipRect + Align(轻量易实现)
通过Align定位到大图中目标Sprite的位置,再用ClipRect裁剪出指定大小的区域,无需额外处理图像加载,框架自动管理。
class SpriteImage extends StatelessWidget { final String assetName; final int index; final int rowCount; final int columnCount; final double spriteWidth; final double spriteHeight; const SpriteImage({ super.key, required this.assetName, required this.index, required this.rowCount, required this.columnCount, required this.spriteWidth, required this.spriteHeight, }); @override Widget build(BuildContext context) { final totalWidth = spriteWidth * columnCount; final totalHeight = spriteHeight * rowCount; final rowIndex = index ~/ columnCount; final columnIndex = index % columnCount; // 计算Align的对齐比例,定位到目标Sprite中心 final alignmentX = -(1.0 - (columnIndex + 0.5) * spriteWidth / totalWidth); final alignmentY = -(1.0 - (rowIndex + 0.5) * spriteHeight / totalHeight); return ClipRect( child: SizedBox( width: spriteWidth, height: spriteHeight, child: Align( alignment: Alignment(alignmentX, alignmentY), widthFactor: totalWidth / spriteWidth, heightFactor: totalHeight / spriteHeight, child: Image.asset(assetName, fit: BoxFit.none), ), ), ); } }
方法2:CustomPainter(灵活可控)
如果需要自定义绘制逻辑(比如缩放、旋转),可以用CustomPainter直接绘制SpriteSheet的指定区域,适合复杂场景。
class SpriteImage extends StatelessWidget { final String assetName; final int index; final int rowCount; final int columnCount; final double spriteWidth; final double spriteHeight; const SpriteImage({ super.key, required this.assetName, required this.index, required this.rowCount, required this.columnCount, required this.spriteWidth, required this.spriteHeight, }); @override Widget build(BuildContext context) { return SizedBox( width: spriteWidth, height: spriteHeight, child: CustomPaint( painter: _SpritePainter( assetName: assetName, index: index, rowCount: rowCount, columnCount: columnCount, spriteSize: Size(spriteWidth, spriteHeight), ), ), ); } } class _SpritePainter extends CustomPainter { final String assetName; final int index; final int rowCount; final int columnCount; final Size spriteSize; late final Image image; bool _isImageLoaded = false; _SpritePainter({ required this.assetName, required this.index, required this.rowCount, required this.columnCount, required this.spriteSize, }) { // 预加载图像并监听加载完成 AssetImage(assetName).resolve(const ImageConfiguration()).addListener( ImageStreamListener((info, _) { image = info.image; _isImageLoaded = true; notifyListeners(); }), ); } @override void paint(Canvas canvas, Size size) { if (!_isImageLoaded) return; final rowIndex = index ~/ columnCount; final columnIndex = index % columnCount; // 定义Sprite在大图中的源区域 final srcRect = Rect.fromLTWH( columnIndex * spriteSize.width, rowIndex * spriteSize.height, spriteSize.width, spriteSize.height, ); // 定义绘制到屏幕的目标区域 final dstRect = Rect.fromLTWH(0, 0, size.width, size.height); canvas.drawImageRect(image, srcRect, dstRect, Paint()); } @override bool shouldRepaint(covariant _SpritePainter oldDelegate) { return oldDelegate.index != index || oldDelegate.assetName != assetName; } }
注意事项
- 确保
spriteWidth和spriteHeight与SpriteSheet中单个Sprite的实际像素尺寸一致,避免拉伸变形; - 方法1适合大多数简单场景,方法2适合需要自定义绘制逻辑的复杂场景。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

