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

Flutter中如何为图片添加拍立得相框并预览保存?

Flutter拍立得相框实现思路

方案一:Widget层级叠加(快速实现预览)

这是最简单的实时预览方案,无需处理图片字节,直接通过Stack将用户图片与相框叠加即可。

两种实现方式:

  1. 使用透明相框资源
    准备一张中间区域透明的PNG格式拍立得相框图片,通过Stack将用户图片放在下层,相框图片放在上层:
Stack(
  alignment: Alignment.center,
  children: [
    // 用户选中的图片,按相框比例裁剪适配
    ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: Image.file(
        selectedImage,
        width: 300,
        height: 350,
        fit: BoxFit.cover,
      ),
    ),
    // 拍立得相框资源(中间透明)
    Image.asset(
      'assets/polaroid_frame.png',
      width: 350,
      height: 400,
      fit: BoxFit.contain,
    ),
  ],
)
  1. 纯代码自定义相框
    如果没有现成相框资源,直接用Container和Column实现白色边框、圆角与底部留白:
Container(
  width: 350,
  height: 400,
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(12),
    boxShadow: [BoxShadow(blurRadius: 8, color: Colors.black12)],
  ),
  child: Column(
    children: [
      // 图片展示区域(带内边距)
      Expanded(
        flex: 8,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(8),
            child: Image.file(selectedImage, fit: BoxFit.cover),
          ),
        ),
      ),
      // 底部留白区域
      Expanded(flex: 2, child: SizedBox()),
    ],
  ),
)

方案二:使用image包合成图片(用于保存/分享)

如果需要将带相框的图片保存到本地或分享,用image包处理图片合成,解决你之前遇到的问题,步骤如下:

  1. 添加依赖:
dependencies:
  image: ^4.0.17
  path_provider: ^2.1.1
  1. 合成代码实现:
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';

Future<img.Image> compositePolaroid(String userImagePath, String frameAssetPath) async {
  // 加载用户选择的图片
  final userImageBytes = await File(userImagePath).readAsBytes();
  final userImage = img.decodeImage(userImageBytes)!;

  // 加载相框资源
  final frameAssetData = await rootBundle.load(frameAssetPath);
  final frameImage = img.decodeImage(frameAssetData.buffer.asUint8List())!;

  // 定义相框中间可展示图片的尺寸(根据你的相框资源调整)
  const targetImgWidth = 300;
  const targetImgHeight = 350;

  // 缩放用户图片到适配尺寸
  final resizedUserImg = img.copyResize(userImage, width: targetImgWidth, height: targetImgHeight);

  // 计算图片在相框中的居中偏移量
  final xOffset = (frameImage.width - targetImgWidth) ~/ 2;
  final yOffset = (frameImage.height - targetImgHeight) ~/ 2;

  // 将用户图片绘制到相框的对应位置
  img.copyInto(
    frameImage,
    resizedUserImg,
    dstX: xOffset,
    dstY: yOffset,
  );

  return frameImage;
}
  1. 保存合成后的图片:
// 调用合成方法
final compositeImg = await compositePolaroid(selectedImage.path, 'assets/polaroid_frame.png');
// 获取保存路径
final saveDir = await getApplicationDocumentsDirectory();
final savePath = '${saveDir.path}/polaroid_${DateTime.now().millisecondsSinceEpoch}.png';
// 保存为PNG文件
await File(savePath).writeAsBytes(img.encodePng(compositeImg));

注意事项:

  • 确保相框资源是带透明通道的PNG,否则会完全覆盖用户图片;
  • 精准匹配targetImgWidth/targetImgHeight和相框中间透明区域的实际尺寸,避免偏移。

方案三:CustomPaint自定义绘制(高度定制)

如果需要高度定制相框样式(比如添加文字、特殊纹理),可以用CustomPaint直接绘制:

import 'package:flutter/material.dart';

class PolaroidPainter extends CustomPainter {
  final ImageProvider imageProvider;

  PolaroidPainter(this.imageProvider);

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制白色相框背景
    final framePaint = Paint()..color = Colors.white;
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, size.width, size.height),
        Radius.circular(12),
      ),
      framePaint,
    );

    // 绘制阴影效果
    final shadowPaint = Paint()
      ..color = Colors.black12
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, 8);
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        Rect.fromLTWH(4, 4, size.width - 8, size.height - 8),
        Radius.circular(12),
      ),
      shadowPaint,
    );

    // 定义图片展示区域
    final imageRect = Rect.fromLTWH(16, 16, size.width - 32, size.height * 0.8 - 16);
    canvas.clipRRect(RRect.fromRectAndRadius(imageRect, Radius.circular(8)));

    // 加载并绘制用户图片
    imageProvider.resolve(ImageConfiguration()).addListener(
      ImageStreamListener((info, _) {
        canvas.drawImageRect(
          info.image,
          Rect.fromLTWH(0, 0, info.image.width.toDouble(), info.image.height.toDouble()),
          imageRect,
          Paint(),
        );
        PaintingBinding.instance.scheduleFrame();
      }),
    );
  }

  @override
  bool shouldRepaint(covariant PolaroidPainter oldDelegate) {
    return oldDelegate.imageProvider != imageProvider;
  }
}

使用方式:

CustomPaint(
  size: Size(350, 400),
  painter: PolaroidPainter(FileImage(selectedImage)),
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 18:22:32