| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/rendering.dart'; | 
					
						
							|  |  |  | import 'dart:ui' as ui; | 
					
						
							|  |  |  | import 'package:flutter/services.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'package:path_provider/path_provider.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MineSignPage extends StatelessWidget { | 
					
						
							|  |  |  |   const MineSignPage({super.key}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Scaffold( | 
					
						
							|  |  |  |       body: const SignatureConfirmPage(), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SignatureConfirmPage extends StatefulWidget { | 
					
						
							|  |  |  |   const SignatureConfirmPage({super.key}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<SignatureConfirmPage> createState() => _SignatureConfirmPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _SignatureConfirmPageState extends State<SignatureConfirmPage> { | 
					
						
							|  |  |  |   final GlobalKey _signatureKey = GlobalKey(); | 
					
						
							|  |  |  |   List<Offset?> _points = []; | 
					
						
							|  |  |  |   bool _hasSignature = false; | 
					
						
							|  |  |  |   File? fileN; | 
					
						
							|  |  |  |   Uint8List? _postBytes; | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |   late String imagepath = ""; | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   void _clearSignature() { | 
					
						
							|  |  |  |     setState(() { | 
					
						
							|  |  |  |       _points.clear(); | 
					
						
							|  |  |  |       _hasSignature = false; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _confirmSignature() { | 
					
						
							|  |  |  |     if (!_hasSignature) { | 
					
						
							|  |  |  |       ScaffoldMessenger.of(context).showSnackBar( | 
					
						
							|  |  |  |         const SnackBar(content: Text('请先签名')), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _saveSignPic(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // // 保存签名逻辑
 | 
					
						
							|  |  |  |     // ScaffoldMessenger.of(context).showSnackBar(
 | 
					
						
							|  |  |  |     //   const SnackBar(content: Text('签名已确认')),
 | 
					
						
							|  |  |  |     // );
 | 
					
						
							|  |  |  |     //模拟保存后返回
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // 保存签名
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |   void _saveSignPic() async { | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |     RenderRepaintBoundary boundary = _signatureKey.currentContext! | 
					
						
							|  |  |  |         .findRenderObject() as RenderRepaintBoundary; | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |     var image = await boundary.toImage(pixelRatio: 1); | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |     ByteData? byteData = | 
					
						
							|  |  |  |     await image.toByteData(format: ui.ImageByteFormat.png); | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |     int timestamp = DateTime.now().millisecondsSinceEpoch; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |     Directory dir = await getTemporaryDirectory(); | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |     // 在文件名中添加时间戳
 | 
					
						
							|  |  |  |     String path = '${dir.path}/sign_$timestamp.png'; | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     File file2 = File(path); | 
					
						
							|  |  |  |     // 检查文件是否存在
 | 
					
						
							|  |  |  |     if (await file2.exists()) { | 
					
						
							|  |  |  |       await file2.delete(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var file = await File(path).create(recursive: true); | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |     if (byteData != null) { | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |       file.writeAsBytesSync(byteData.buffer.asInt8List(), flush: true); | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       setState(() { | 
					
						
							|  |  |  |         _postBytes = byteData.buffer.asUint8List(); | 
					
						
							|  |  |  |         fileN = file; | 
					
						
							|  |  |  |         imagepath = file.path; | 
					
						
							|  |  |  |         Future.delayed(const Duration(milliseconds: 500), () { | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |           Navigator.pop(context, imagepath); | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     SystemChrome.setPreferredOrientations([ | 
					
						
							|  |  |  |       DeviceOrientation.landscapeRight, | 
					
						
							|  |  |  |       DeviceOrientation.landscapeLeft, | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     // 不要忘记重置方向设置,以避免影响其他页面或应用。
 | 
					
						
							|  |  |  |     SystemChrome.setPreferredOrientations([ | 
					
						
							|  |  |  |       DeviceOrientation.portraitUp, | 
					
						
							|  |  |  |       DeviceOrientation.portraitDown, | 
					
						
							|  |  |  |       DeviceOrientation.landscapeLeft, | 
					
						
							|  |  |  |       DeviceOrientation.landscapeRight, | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Scaffold( | 
					
						
							|  |  |  |       body: SafeArea( | 
					
						
							|  |  |  |         child: Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             // 标题区域
 | 
					
						
							|  |  |  |             _buildTitleBar(), | 
					
						
							|  |  |  |             // MyAppbar(title: "签字"),
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 签字区域
 | 
					
						
							|  |  |  |             Expanded( | 
					
						
							|  |  |  |               child: _buildSignatureArea(), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 按钮区域
 | 
					
						
							|  |  |  |             _buildActionButtons(), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildTitleBar() { | 
					
						
							|  |  |  |     return Padding( | 
					
						
							|  |  |  |       padding: const EdgeInsets.symmetric(horizontal: 16), | 
					
						
							|  |  |  |       child: Row( | 
					
						
							|  |  |  |         mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |         children: [ | 
					
						
							|  |  |  |           IconButton( | 
					
						
							|  |  |  |             icon: const Icon(Icons.arrow_back), | 
					
						
							|  |  |  |             onPressed: () => Navigator.pop(context), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           const Text( | 
					
						
							|  |  |  |             '签字', | 
					
						
							|  |  |  |             style: TextStyle( | 
					
						
							|  |  |  |               fontSize: 20, | 
					
						
							|  |  |  |               fontWeight: FontWeight.bold, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           const SizedBox(width: 48), // 占位保持标题居中
 | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildSignatureArea() { | 
					
						
							|  |  |  |     return Padding( | 
					
						
							|  |  |  |       padding: const EdgeInsets.symmetric(horizontal: 24), | 
					
						
							|  |  |  |       child: Container( | 
					
						
							|  |  |  |         decoration: BoxDecoration( | 
					
						
							|  |  |  |           borderRadius: BorderRadius.circular(12), | 
					
						
							|  |  |  |           border: Border.all(color: Colors.grey[300]!), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         child: Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // 签字画布
 | 
					
						
							|  |  |  |             Expanded( | 
					
						
							|  |  |  |               child: GestureDetector( | 
					
						
							|  |  |  |                 onPanStart: (details) { | 
					
						
							|  |  |  |                   setState(() { | 
					
						
							|  |  |  |                     _points.add(details.localPosition); | 
					
						
							|  |  |  |                     _hasSignature = true; | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 onPanUpdate: (details) { | 
					
						
							|  |  |  |                   setState(() { | 
					
						
							|  |  |  |                     _points.add(details.localPosition); | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 onPanEnd: (details) { | 
					
						
							|  |  |  |                   setState(() { | 
					
						
							|  |  |  |                     _points.add(null); | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 child: RepaintBoundary( | 
					
						
							|  |  |  |                   key: _signatureKey, | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |                   child: Container( | 
					
						
							|  |  |  |                     // 给画布添加白色背景,不透明导出
 | 
					
						
							|  |  |  |                     color: Colors.white, | 
					
						
							|  |  |  |                     child: Stack( | 
					
						
							|  |  |  |                       children: [ | 
					
						
							|  |  |  |                         // if (imagepath.length > 0)
 | 
					
						
							|  |  |  |                         // Image.file(
 | 
					
						
							|  |  |  |                         //   File(imagepath), // 显示选择的图片文件
 | 
					
						
							|  |  |  |                         //   fit: BoxFit.contain, // 设置图片填充方式为完整显示,保持宽高比例
 | 
					
						
							|  |  |  |                         // ),
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         // 背景横线
 | 
					
						
							|  |  |  |                         _buildBackgroundLines(), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         // 签名画布
 | 
					
						
							|  |  |  |                         CustomPaint( | 
					
						
							|  |  |  |                           painter: SignaturePainter(points: _points), | 
					
						
							|  |  |  |                           size: Size.infinite, | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         // 提示文字
 | 
					
						
							|  |  |  |                         if (!_hasSignature) | 
					
						
							|  |  |  |                           const Center( | 
					
						
							|  |  |  |                             child: Text( | 
					
						
							|  |  |  |                               '请在此处签名', | 
					
						
							|  |  |  |                               style: TextStyle( | 
					
						
							|  |  |  |                                 fontSize: 16, | 
					
						
							|  |  |  |                                 color: Colors.grey, | 
					
						
							|  |  |  |                               ), | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |                             ), | 
					
						
							|  |  |  |                           ), | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |                       ], | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |                   ), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildBackgroundLines() { | 
					
						
							|  |  |  |     return LayoutBuilder( | 
					
						
							|  |  |  |       builder: (context, constraints) { | 
					
						
							|  |  |  |         return CustomPaint( | 
					
						
							|  |  |  |           painter: BackgroundLinesPainter(), | 
					
						
							|  |  |  |           size: Size(constraints.maxWidth, constraints.maxHeight), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildActionButtons() { | 
					
						
							|  |  |  |     return Padding( | 
					
						
							|  |  |  |       padding: const EdgeInsets.only(left: 24,right: 24,top: 10,bottom: 10), | 
					
						
							|  |  |  |       child: Row( | 
					
						
							|  |  |  |         mainAxisAlignment: MainAxisAlignment.spaceEvenly, | 
					
						
							|  |  |  |         children: [ | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |           // 重签按钮
 | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |             child: OutlinedButton( | 
					
						
							|  |  |  |               onPressed: _clearSignature, | 
					
						
							|  |  |  |               style: OutlinedButton.styleFrom( | 
					
						
							|  |  |  |                 padding: const EdgeInsets.symmetric(vertical: 8), | 
					
						
							|  |  |  |                 side: const BorderSide(color: Colors.blue), | 
					
						
							|  |  |  |                 shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |                   borderRadius: BorderRadius.circular(8), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               child: const Text( | 
					
						
							|  |  |  |                 '重签', | 
					
						
							|  |  |  |                 style: TextStyle( | 
					
						
							|  |  |  |                   fontSize: 16, | 
					
						
							|  |  |  |                   color: Colors.blue, | 
					
						
							|  |  |  |                   fontWeight: FontWeight.w500, | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |           const SizedBox(width: 20), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           // 确定按钮
 | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |             child: ElevatedButton( | 
					
						
							|  |  |  |               onPressed: _confirmSignature, | 
					
						
							|  |  |  |               style: ElevatedButton.styleFrom( | 
					
						
							|  |  |  |                 backgroundColor: Colors.blue, | 
					
						
							|  |  |  |                 padding: const EdgeInsets.symmetric(vertical: 8), | 
					
						
							|  |  |  |                 shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |                   borderRadius: BorderRadius.circular(8), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               child: const Text( | 
					
						
							|  |  |  |                 '确定', | 
					
						
							|  |  |  |                 style: TextStyle( | 
					
						
							|  |  |  |                   fontSize: 16, | 
					
						
							|  |  |  |                   color: Colors.white, | 
					
						
							|  |  |  |                   fontWeight: FontWeight.w500, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 签名绘制器
 | 
					
						
							|  |  |  | class SignaturePainter extends CustomPainter { | 
					
						
							|  |  |  |   final List<Offset?> points; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SignaturePainter({required this.points}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void paint(Canvas canvas, Size size) { | 
					
						
							|  |  |  |     Paint paint = Paint() | 
					
						
							|  |  |  |       ..color = Colors.black | 
					
						
							|  |  |  |       ..strokeCap = StrokeCap.round | 
					
						
							|  |  |  |       ..strokeWidth = 3.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < points.length - 1; i++) { | 
					
						
							|  |  |  |       if (points[i] != null && points[i + 1] != null) { | 
					
						
							|  |  |  |         canvas.drawLine(points[i]!, points[i + 1]!, paint); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |   bool shouldRepaint(SignaturePainter oldDelegate) => | 
					
						
							|  |  |  |       oldDelegate.points != points; | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 背景横线绘制器
 | 
					
						
							|  |  |  | class BackgroundLinesPainter extends CustomPainter { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void paint(Canvas canvas, Size size) { | 
					
						
							|  |  |  |     Paint paint = Paint() | 
					
						
							| 
									
										
										
										
											2025-07-29 08:50:41 +08:00
										 |  |  |     // ..color = Colors.grey[200]!
 | 
					
						
							| 
									
										
										
										
											2025-07-14 18:01:16 +08:00
										 |  |  |       ..color = Color.from(alpha: 0, red: 0, green: 0, blue: 0)! | 
					
						
							|  |  |  |       ..strokeWidth = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 绘制横线
 | 
					
						
							|  |  |  |     for (double y = 40; y < size.height; y += 40) { | 
					
						
							|  |  |  |       canvas.drawLine( | 
					
						
							|  |  |  |         Offset(0, y), | 
					
						
							|  |  |  |         Offset(size.width, y), | 
					
						
							|  |  |  |         paint, | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   bool shouldRepaint(CustomPainter oldDelegate) => false; | 
					
						
							|  |  |  | } |