365 lines
9.0 KiB
Dart
365 lines
9.0 KiB
Dart
|
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;
|
||
|
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<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
|
||
|
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;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
}
|