import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutterappdz/resource.dart' as res;
import 'package:path_provider/path_provider.dart';
class SignPage extends StatefulWidget {
const SignPage({Key? key}) : super(key: key);
@override
_SignPageState createState() => _SignPageState();
}
class _SignPageState extends State<SignPage> {
GlobalKey key = GlobalKey();
final GlobalKey _containerkey = GlobalKey();
PointerEvent? _event;
late List<Offset?> points = <Offset>[];
Color selectColor = Colors.red;
Uint8List? _postBytes;
File? fileN;
double y = 0;
late double bottom;
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
RenderBox box = _containerkey.currentContext!.findRenderObject() as RenderBox;
Offset offset = box.localToGlobal(Offset.zero);
y = offset.dy;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('签名'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.symmetric(vertical: 20,horizontal: 16),
child: Text('请在下方区域手写签名',style: TextStyle(fontSize: 16,fontWeight: FontWeight.w400),),
),
Stack(
children: [
Container(
width: double.infinity,
color: Colors.white,
child: Image.asset('assets/sign_image.png',fit: BoxFit.fill,),
),
Positioned(
left: 0,
top: 0,
bottom: 0,
right: 0,
child: RepaintBoundary(
key: key,
child: Stack(
children: [
Listener(
child: Container(
key: _containerkey,
alignment: Alignment.center,
color: Colors.transparent,
// width: double.infinity,
// height: double.infinity,
// child: Text(_event?.toString() ?? "",
// style: TextStyle(color: Colors.black)),
),
onPointerDown: (PointerDownEvent event) {
setState(() {
_event = event;
points.add(_event?.localPosition ?? Offset.zero);
});
},
onPointerMove: (PointerMoveEvent event) {
setState(() {
_event = event;
if (_event != null){
RenderBox box = _containerkey.currentContext!.findRenderObject() as RenderBox;
Offset offset = box.localToGlobal(Offset.zero);
var dy = offset.dy;
var dbottom = box.size.height;
if (_event!.localPosition.dy < dbottom && _event!.localPosition.dy > 0){
points.add(_event?.localPosition ?? Offset.zero);
}
}
});
},
onPointerUp: (PointerUpEvent event) {
setState(() {
_event = event;
points.add(Offset.zero);
});
},
),
Positioned(
left: (_event != null ? _event?.position.dx ?? 0 : 0),
top: (_event != null ? (_event?.position.dy ?? 0) -
y : 0),
child: Container(
width: 1,
height: 1,
color: Colors.black,
),),
CustomPaint(painter: SignaturePainter(points))
],
),
)
)
],
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: Row(
children: [
const SizedBox(
width: 24,
),
Expanded(
child: OutlinedButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(const EdgeInsets.symmetric(vertical: 10)),
side: MaterialStateProperty.all(const BorderSide(color: res.Colors.majorColor)),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
onPressed: () {
setState(() {
points = [];
});
},
child: const Text('重置'),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: ElevatedButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(const EdgeInsets.symmetric(vertical: 10)),
side: MaterialStateProperty.all(const BorderSide(color: res.Colors.majorColor)),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
onPressed: () {
_saveSignPic();
},
child: const Text('确定'),
),
),
const SizedBox(
width: 24,
),
],
),
),
if(fileN != null)
Container(
color: Colors.red,
constraints: const BoxConstraints(
maxHeight: 100,
maxWidth: 100,
),
child: Image(image: FileImageEx(fileN!),),
),
],
),
);
}
// 保存签名
void _saveSignPic() async{
RenderRepaintBoundary boundary = key.currentContext!.findRenderObject() as RenderRepaintBoundary;
var image = await boundary.toImage(pixelRatio: 1);
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
Directory dir = await getTemporaryDirectory();
String path = dir.path +"/"+ 'sign.png';
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;
});
}
}
}
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List<Offset?> points;
final Color paintColor = Colors.black;
Paint myPaint = new Paint();
void paint(Canvas canvas, Size size) {
myPaint.strokeCap = StrokeCap.round;
myPaint.strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null &&
points[i + 1] != null &&
points[i] != Offset.zero &&
points[i + 1] != Offset.zero) {
canvas.drawLine(points[i]!, points[i + 1]!, myPaint);
}
}
}
bool shouldRepaint(SignaturePainter other) => other.points != points;
}
// ignore: must_be_immutable
class FileImageEx extends FileImage {
int? fileSize;
FileImageEx(File file, { double scale = 1.0 })
: assert(file != null),
assert(scale != null),
super(file, scale: scale) {
fileSize = file.lengthSync();
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) {
return false;
}
final FileImageEx typedOther = other;
return file.path == typedOther.file.path
&& scale == typedOther.scale && fileSize == typedOther.fileSize;
}
}