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

Flutter实现assets目录下PNG图片叠加合成的可行方案示例寻求

Flutter 轻量实现多张Assets PNG叠加合成方案

以下实现基于原生Canvas绘制能力,无第三方依赖,符合你不想引入重度库的需求。

前置配置

首先确认你已经在pubspec.yaml中注册了图片对应的assets目录,示例配置如下:

flutter:
  assets:
    - assets/pngs/ # 替换为你实际存放PNG的assets路径

核心实现逻辑

  • 先将assets目录下的PNG文件加载为dart:ui库的Image对象
  • 自定义CustomPainter按层级依次绘制所有图片
  • 支持直接预览合成效果,也可导出为Uint8List用于保存或后续处理

可运行完整示例代码

首先导入必要依赖:

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

1. 自定义合成绘制器

class ImageComposePainter extends CustomPainter {
  // 待合成的图片列表,按列表顺序绘制,后加入的图片会叠加在上层
  final List<ui.Image> images;
  // 每张图片对应绘制的偏移位置,可自行调整,默认都从左上角开始
  final List<Offset> offsets;

  ImageComposePainter({required this.images, required this.offsets})
      : assert(images.length == offsets.length);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..isAntiAlias = true;
    for (int i = 0; i < images.length; i++) {
      canvas.drawImage(images[i], offsets[i], paint);
    }
  }

  @override
  bool shouldRepaint(covariant ImageComposePainter oldDelegate) {
    return images != oldDelegate.images || offsets != oldDelegate.offsets;
  }
}

2. 加载Assets图片为ui.Image的工具方法

Future<ui.Image> loadUiImageFromAssets(String path) async {
  final ByteData data = await rootBundle.load(path);
  final Uint8List bytes = data.buffer.asUint8List();
  final ui.Codec codec = await ui.instantiateImageCodec(bytes);
  final ui.FrameInfo frameInfo = await codec.getNextFrame();
  return frameInfo.image;
}

3. 页面使用示例

class ComposeImageDemo extends StatefulWidget {
  const ComposeImageDemo({super.key});

  @override
  State<ComposeImageDemo> createState() => _ComposeImageDemoState();
}

class _ComposeImageDemoState extends State<ComposeImageDemo> {
  List<ui.Image> _images = [];
  final List<Offset> _offsets = [
    const Offset(0, 0), // 第一层背景图位置
    const Offset(20, 20), // 第二层叠加图位置
    const Offset(50, 50), // 第三层叠加图位置
  ];
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadAllImages();
  }

  Future<void> _loadAllImages() async {
    // 替换为你自己的assets图片路径
    final imagePaths = [
      'assets/pngs/bg.png',
      'assets/pngs/layer1.png',
      'assets/pngs/layer2.png',
    ];
    for (String path in imagePaths) {
      final img = await loadUiImageFromAssets(path);
      _images.add(img);
    }
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('图片合成示例')),
      body: Center(
        child: _isLoading
            ? const CircularProgressIndicator()
            : CustomPaint(
                painter: ImageComposePainter(images: _images, offsets: _offsets),
                // 调整为你合成图片的实际尺寸
                size: const Size(300, 300),
              ),
      ),
    );
  }
}

扩展:导出合成后的图片为Uint8List

如果需要拿到合成后的图片数据,添加如下方法即可:

Future<Uint8List?> exportComposedImage(List<ui.Image> images, List<Offset> offsets, Size size) async {
  final ui.PictureRecorder recorder = ui.PictureRecorder();
  final Canvas canvas = Canvas(recorder);
  final paint = Paint()..isAntiAlias = true;
  for (int i = 0; i < images.length; i++) {
    canvas.drawImage(images[i], offsets[i], paint);
  }
  final ui.Picture picture = recorder.endRecording();
  final ui.Image img = await picture.toImage(size.width.toInt(), size.height.toInt());
  final ByteData? byteData = await img.toByteData(format: ui.ImageByteFormat.png);
  return byteData?.buffer.asUint8List();
}

导出的Uint8List可直接通过Image.memory()方法显示,也可写入本地文件保存。

内容的提问来源于stack exchange,提问作者user6097845

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 19:27:02