当前位置: 首页 > article >正文

Flutter:签名板封装

签名板
在这里插入图片描述

依赖安装

  # 图片处理
  image: ^4.1.3
  # 签名
  signature: ^5.4.1

封装组件

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;
    }
  }
} 

页面调用

  // 弹出签名框
  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('签名保存失败');
          }
        }
      },
    );
  }

http://www.kler.cn/a/588980.html

相关文章:

  • 沐数科技数据开发岗笔试题2025
  • 《C#上位机开发从门外到门内》3-2::Modbus数据采集系统
  • HTTP+DNS综合实验
  • 谷粒商城:性能压测JVM堆区
  • C# Winform 实现换肤,并自定义皮肤功能
  • C#—闭包详解
  • 群体智能优化算法-旗鱼优化算法 (Sailfish Optimizer, SFO,含Matlab源代码)
  • SQL Server表数据变更捕获的5种方法及实战对比
  • C++(初阶)(五)——类和对象(下)
  • 【极光 Orbit·STC8x】05. GPIO库函数驱动LED流动
  • 【区块链+乡村振兴】四川云龙肉牛产业数字化平台 | FISCO BCOS 应用案例
  • 操作系统八股文整理(一)
  • Celery在Django中的作用
  • day05_Java高级
  • 深度学习正则化技术之权重衰减法、暂退法(通俗易懂版)
  • 一款基于Python的从常规文档里提取图片的简单工具开发方案
  • 30、Vuex 为啥可以进行缓存处理
  • 【Leetcode 每日一题】3306. 元音辅音字符串计数 I
  • linux:环境变量,进程地址空间
  • 网络编程基础