Flutter:签名板封装

签名板

依赖安装

cpp 复制代码
  # 图片处理
  image: ^4.1.3
  # 签名
  signature: ^5.4.1

封装组件

js 复制代码
import 'dart:typed_data';
import 'package:dogex/common/index.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:signature/signature.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';
import 'dart:io';

class SignaturePad {
  /// 显示签名板
  /// [backgroundColor] 签名区域背景色
  /// [titleColor] 标题文字颜色
  /// [penColor] 签名笔颜色
  /// [onConfirm] 确认回调,返回签名图片的PNG字节数据
  static Future<void> show({
    Color backgroundColor = Colors.white,
    Color titleColor = Colors.white,
    Color penColor = Colors.black,
    required Function(Uint8List?) onConfirm,
    bool convertToJpg = false,
    int jpgQuality = 90,
  }) async {
    final SignatureController controller = SignatureController(
      penStrokeWidth: 3,
      penColor: penColor,
      exportBackgroundColor: backgroundColor,
    );

    await Get.bottomSheet(
      PopScope(
        canPop: false, // 禁止通过返回键或滑动关闭
        child: Container(
          height: 800.w,
          decoration: BoxDecoration(
            color: AppTheme.color2223,
            borderRadius: BorderRadius.vertical(
              top: Radius.circular(30.w),
            ),
          ),
          child: Column(
            children: [
              // 顶部标题栏
              Container(
                padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 20.w),
                child: Column(
                  children: [
                    // 标题和清除按钮
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        TextWidget.body(
                          '签署',
                          size: 32.sp,
                          color: titleColor,
                          weight: FontWeight.w600,
                        ),
                        GestureDetector(
                          onTap: () {
                            controller.clear();
                          },
                          child: Container(
                            padding: EdgeInsets.all(10.w),
                            child: TextWidget.body(
                              '清除',
                              size: 28.sp,
                              color: titleColor,
                            ),
                          ),
                        ),
                      ],
                    ),
                    SizedBox(height: 10.w),
                    // 提示文字
                    TextWidget.body(
                      '请在下方区域签名',
                      size: 24.sp,
                      color: AppTheme.color999,
                    ),
                  ],
                ),
              ),
              // 签名区域
              Expanded(
                child: Container(
                  margin: EdgeInsets.symmetric(horizontal: 30.w),
                  decoration: BoxDecoration(
                    color: backgroundColor,
                    borderRadius: BorderRadius.circular(20.w),
                  ),
                  child: Signature(
                    controller: controller,
                    // backgroundColor: backgroundColor,
                  ),
                ),
              ),
              // 底部按钮
              Container(
                padding: EdgeInsets.all(30.w),
                child: Row(
                  children: [
                    // 取消按钮
                    Expanded(
                      child: Container(
                        height: 90.w,
                        decoration: BoxDecoration(
                          color: AppTheme.blockBgColor,
                          borderRadius: BorderRadius.circular(45.w),
                        ),
                        child: TextButton(
                          onPressed: () {
                            Get.back();
                          },
                          style: TextButton.styleFrom(
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(45.w),
                            ),
                          ),
                          child: TextWidget.body(
                            '取消',
                            size: 28.sp,
                            color: titleColor,
                          ),
                        ),
                      ),
                    ),
                    SizedBox(width: 20.w),
                    // 确认按钮
                    Expanded(
                      child: Container(
                        height: 90.w,
                        decoration: BoxDecoration(
                          color: AppTheme.primaryBlue,
                          borderRadius: BorderRadius.circular(45.w),
                        ),
                        child: TextButton(
                          onPressed: () async {
                            if (controller.isEmpty) {
                              Loading.toast('请签名后再确认');
                              return;
                            }
                            
                            final pngData = await controller.toPngBytes();
                            
                            if (pngData != null) {
                              if (convertToJpg) {
                                try {
                                  // 转换为JPG格式
                                  final jpgData = await _convertPngToJpg(pngData, jpgQuality);
                                  Get.back();
                                  onConfirm(jpgData);
                                } catch (e) {
                                  Loading.toast('图片转换失败');
                                  Get.back();
                                  onConfirm(pngData); // 转换失败时返回原始PNG数据
                                }
                              } else {
                                Get.back();
                                onConfirm(pngData);
                              }
                            } else {
                              Loading.toast('签名生成失败');
                            }
                          },
                          style: TextButton.styleFrom(
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(45.w),
                            ),
                          ),
                          child: TextWidget.body(
                            '确认',
                            size: 28.sp,
                            color: Colors.white,
                            weight: FontWeight.w600,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
      isScrollControlled: true,
      enableDrag: false, // 禁止拖拽关闭
      isDismissible: false, // 禁止点击外部关闭
    );

    // 释放控制器
    controller.dispose();
  }

  /// 将PNG字节数据转换为JPG格式
  static Future<Uint8List> _convertPngToJpg(Uint8List pngData, int quality) async {
    // 使用image包解码PNG
    final pngImage = img.decodePng(pngData);
    if (pngImage == null) {
      throw Exception('无法解码PNG图片');
    }
    
    // 转换为JPG格式
    final jpgData = img.encodeJpg(pngImage, quality: quality);
    return Uint8List.fromList(jpgData);
  }

  /// 保存签名图片到本地文件
  static Future<String?> saveSignatureToFile(Uint8List imageData, {bool isJpg = false}) async {
    try {
      final directory = await getApplicationDocumentsDirectory();
      final timestamp = DateTime.now().millisecondsSinceEpoch;
      final extension = isJpg ? 'jpg' : 'png';
      final path = '${directory.path}/signature_$timestamp.$extension';
      
      final file = File(path);
      await file.writeAsBytes(imageData);
      return path;
    } catch (e) {
      print('保存签名图片失败: $e');
      return null;
    }
  }
} 

页面调用

js 复制代码
  // 弹出签名框
  void onSignPopup() {
    SignaturePad.show(
      backgroundColor: Colors.white,
      titleColor: AppTheme.colorfff,
      penColor: Colors.black,
      convertToJpg: true, // 转换为JPG格式
      jpgQuality: 90, // JPG质量
      onConfirm: (data) async {
        if (data != null) {
          // 保存签名图片到本地文件
          final filePath = await SignaturePad.saveSignatureToFile(
            data, 
            isJpg: true
          );
          
          if (filePath != null) {
            // 保存到相册
            // 这里将输入图片的临时路径
            print('签名保存成功: $filePath');
          } else {
            Loading.toast('签名保存失败');
          }
        }
      },
    );
  }
相关推荐
nbsaas-boot5 分钟前
SQL Server 窗口函数全指南(函数用法与场景)
开发语言·数据库·python·sql·sql server
东方佑7 分钟前
递归推理树(RR-Tree)系统:构建认知推理的骨架结构
开发语言·r语言·r-tree
Warren9810 分钟前
Java Stream流的使用
java·开发语言·windows·spring boot·后端·python·硬件工程
伍哥的传说38 分钟前
Radash.js 现代化JavaScript实用工具库详解 – 轻量级Lodash替代方案
开发语言·javascript·ecmascript·tree-shaking·radash.js·debounce·throttle
xidianhuihui1 小时前
go install报错: should be v0 or v1, not v2问题解决
开发语言·后端·golang
架构师沉默1 小时前
Java优雅使用Spring Boot+MQTT推送与订阅
java·开发语言·spring boot
前端程序媛-Tian1 小时前
【dropdown组件填坑指南】—怎么实现下拉框的位置计算
前端·javascript·vue
iamlujingtao2 小时前
js多边形算法:获取多边形中心点,且必定在多边形内部
javascript·算法
嘉琪0012 小时前
实现视频实时马赛克
linux·前端·javascript
DebugKitty2 小时前
C语言14-指针4-二维数组传参、指针数组传参、viod*指针
c语言·开发语言·算法·指针传参·void指针·数组指针传参