123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- import 'dart:convert';
- import 'dart:io';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:get/get.dart';
- import 'package:image_picker/image_picker.dart';
- import 'package:uuid/uuid.dart';
- import 'package:vitalapp/architecture/storage/storage.dart';
- import 'package:vitalapp/components/appbar.dart';
- import 'package:fis_common/logger/logger.dart';
- import 'package:vitalapp/rpc.dart';
- import 'dart:ui' as ui;
- import 'package:universal_html/html.dart' as html;
- import 'controller.dart';
- /// 签字板
- class SignatureBoardPage extends GetView<SignatureBoardController> {
- SignatureBoardPage({super.key});
- final canvasController = _SignatureBoardController();
- @override
- Widget build(BuildContext context) {
- final params = Get.parameters;
- final String title = params["title"] ?? "设置签名";
- return WillPopScope(
- onWillPop: () async {
- return false;
- },
- child: Scaffold(
- appBar: VAppBar(
- context: context,
- titleText: title,
- ),
- body: Stack(
- children: [
- _SignatureBoard(controller: canvasController),
- Positioned(
- right: 32,
- bottom: 0,
- top: 0,
- child: _buildFloatActions(context),
- ),
- ],
- ),
- ),
- );
- }
- Widget _buildFloatActions(BuildContext context) {
- return Column(
- mainAxisSize: MainAxisSize.min,
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- SizedBox(
- width: 90,
- height: 90,
- child: ElevatedButton(
- style: ButtonStyle(
- foregroundColor: const MaterialStatePropertyAll(Colors.white),
- backgroundColor: const MaterialStatePropertyAll(Colors.grey),
- shape: MaterialStatePropertyAll(
- RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(45),
- side: BorderSide.none,
- ),
- ),
- ),
- onPressed: () {
- canvasController.revocation();
- },
- child: const Text(
- "撤销",
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
- ),
- ),
- ),
- const SizedBox(height: 32),
- SizedBox(
- width: 90,
- height: 90,
- child: ElevatedButton(
- style: ButtonStyle(
- foregroundColor: const MaterialStatePropertyAll(Colors.white),
- backgroundColor: const MaterialStatePropertyAll(Colors.red),
- shape: MaterialStatePropertyAll(
- RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(45),
- side: BorderSide.none,
- ),
- ),
- ),
- onPressed: () {
- canvasController.clear();
- },
- child: const Text(
- "清除",
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
- ),
- ),
- ),
- const SizedBox(height: 32),
- SizedBox(
- width: 90,
- height: 90,
- child: ElevatedButton(
- style: ButtonStyle(
- foregroundColor: const MaterialStatePropertyAll(Colors.white),
- backgroundColor:
- MaterialStatePropertyAll(Theme.of(context).primaryColor),
- shape: MaterialStatePropertyAll(
- RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(45),
- side: BorderSide.none,
- ),
- ),
- ),
- onPressed: () async {
- final result = await canvasController.getImageUrl();
- Get.back(result: result);
- },
- child: const Text(
- "保存",
- style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
- ),
- ),
- ),
- ],
- );
- }
- }
- class _SignatureBoardController extends ChangeNotifier {
- List<Offset?> points = [];
- SignaturePainter? painter;
- Size? size;
- /// 清除
- void clear() {
- points = [];
- notifyListeners();
- }
- /// 撤销
- void revocation() {
- int index = points.lastIndexOf(null);
- if (index == points.length - 1) {
- points = points.take(index).toList();
- index = points.lastIndexOf(null);
- }
- if (index < 0) {
- points = [];
- } else {
- points = points.take(index).toList();
- }
- notifyListeners();
- }
- XFile? convertBase64ToXFile(String base64Image) {
- try {
- final bytes = base64Decode(base64Image);
- final tempDir = Directory.systemTemp;
- final tempPath = tempDir.path;
- final imageId = const Uuid().v4().replaceAll('-', '');
- final filePath = '$tempPath/$imageId';
- File(filePath).writeAsBytesSync(bytes);
- return XFile(filePath);
- } catch (e) {
- print('Error converting base64 to XFile: $e');
- return null;
- }
- }
- html.File? convertBase64ToFile(String base64Image) {
- try {
- final bytes = base64Decode(base64Image);
- const mimeType = 'image/jpeg'; // 替换为您的图片类型
- final blob = html.Blob([bytes], mimeType);
- final file = html.File([blob], 'image.jpg'); // 替换为您的文件名
- return file;
- } catch (e) {
- print('Error converting base64 to File: $e');
- return null;
- }
- }
- Future<String?> getImageUrl() async {
- final imageBase64 = await getImageBase64();
- String? url;
- if (kIsWeb) {
- final file = convertBase64ToFile(imageBase64!);
- url = await rpc.storage.webUpload(file!);
- } else {
- final xFile = convertBase64ToXFile(imageBase64!);
- url = await rpc.storage.upload(xFile!);
- }
- return url;
- }
- /// 获取图片base64字符串
- Future<String?> getImageBase64() async {
- try {
- final recorder = ui.PictureRecorder();
- final canvas = Canvas(recorder);
- painter!.paint(canvas, size!);
- // Paint paint = Paint()
- // ..color = Colors.black
- // ..strokeCap = StrokeCap.round
- // ..strokeWidth = 8.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);
- // }
- // }
- // 将绘制的内容转换为图像
- final picture = recorder.endRecording();
- final image =
- await picture.toImage(size!.width.toInt(), size!.height.toInt());
- // 将图像保存到文件
- final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
- if (byteData != null) {
- String base64Image = base64Encode(byteData.buffer.asUint8List());
- return base64Image;
- }
- } catch (e) {
- logger.e("_SignatureBoardController getImageBase64 error.", e);
- }
- return null;
- }
- }
- class _SignatureBoard extends StatefulWidget {
- final _SignatureBoardController controller;
- const _SignatureBoard({required this.controller});
- @override
- _SignatureBoardState createState() => _SignatureBoardState();
- }
- class _SignatureBoardState extends State<_SignatureBoard> {
- List<Offset?> get _points => widget.controller.points;
- @override
- void initState() {
- widget.controller.addListener(_onUpdate);
- super.initState();
- }
- @override
- void dispose() {
- widget.controller.removeListener(_onUpdate);
- super.dispose();
- }
- void _onUpdate() {
- setState(() {});
- }
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- onPanUpdate: (DragUpdateDetails details) {
- setState(() {
- RenderBox renderBox = context.findRenderObject() as RenderBox;
- Offset localPosition =
- renderBox.globalToLocal(details.globalPosition);
- widget.controller.points = List.from(_points)..add(localPosition);
- });
- },
- onPanEnd: (DragEndDetails details) {
- widget.controller.points.add(null);
- },
- child: LayoutBuilder(builder: (context, c) {
- final size = Size(c.maxWidth, c.maxHeight);
- final painter = SignaturePainter(points: _points);
- widget.controller.size = size;
- widget.controller.painter = painter;
- return CustomPaint(
- painter: painter,
- size: size,
- );
- }),
- );
- }
- }
- class SignaturePainter extends CustomPainter {
- List<Offset?> points;
- SignaturePainter({required this.points});
- @override
- void paint(Canvas canvas, Size size) {
- Paint paint = Paint()
- ..color = Colors.black
- ..strokeCap = StrokeCap.round
- ..strokeWidth = 8.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) => true;
- }
|