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 createState() => _SignatureConfirmPageState(); } class _SignatureConfirmPageState extends State { final GlobalKey _signatureKey = GlobalKey(); List _points = []; bool _hasSignature = false; File? fileN; Uint8List? _postBytes; late String imagepath=""; 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('签名已确认')), // ); //模拟保存后返回 } // 保存签名 void _saveSignPic() async{ RenderRepaintBoundary boundary = _signatureKey.currentContext!.findRenderObject() as RenderRepaintBoundary; var image = await boundary.toImage(pixelRatio: 1); ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); Directory dir = await getTemporaryDirectory(); String path = dir.path +"/"+ 'sign.png'; File file2 = File(path); // 检查文件是否存在 if (await file2.exists()) { // 文件存在,删除文件 await file2.delete(); } var file = await File(path).create(recursive: true); if(byteData != null){ file.writeAsBytesSync(byteData.buffer.asInt8List(),flush: true); setState(() { _postBytes = byteData.buffer.asUint8List(); fileN = file; imagepath = file.path; Future.delayed(const Duration(milliseconds: 500), () { Navigator.pop(context,imagepath); }); }); } } @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, 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, ), ), ), ], ), ), ), ), ], ), ), ); } 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: [ // 重签按钮 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, ), ), ), ), 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, ), ), ), ), ], ), ); } } // 签名绘制器 class SignaturePainter extends CustomPainter { final List 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 bool shouldRepaint(SignaturePainter oldDelegate) => oldDelegate.points != points; } // 背景横线绘制器 class BackgroundLinesPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { Paint paint = Paint() // ..color = Colors.grey[200]! ..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; }