Flutter签名板封装
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 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 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 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(‘签名保存失败’); } } }, ); }