Flutter中实现签名功能

语言: CN / TW / HK

我们可以使用GestureDetector记录触摸,使用CustomPaint 在屏幕上绘制线条从而实现签名功能

参考步骤

  • 使用 RenderBox.globalToLocal 将  GestureDetector.onPanUpdate提供的DragUpdateDetails  转换为相对坐标
  • 使用GestureDetector.onPanEnd 手势处理程序记录笔画之间的中断。
  • Mutating the same List won't automatically trigger a repaint because the CustomPainter constructor arguments are the same. You can trigger a repaint by creating a new List each time a new point is provided.
  • 改变相同的 List 不会自动触发重绘,因为 CustomPainter 构造函数参数是相同的。每次提供新点时,您都可以通过创建一个新的“列表”来触发重绘。
  • 使用 Canvas.drawLine 在签名的每个记录点之间画一条线。

参考代码

```Dart

import 'package:flutter/material.dart';

class SignaturePainter extends CustomPainter { SignaturePainter(this.points);

final List points;

void paint(Canvas canvas, Size size) { Paint paint = new Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.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); } }

bool shouldRepaint(SignaturePainter other) => other.points != points; }

class Signature extends StatefulWidget { SignatureState createState() => new SignatureState(); }

class SignatureState extends State { List _points = [];

Widget build(BuildContext context) { return new Stack( children: [ GestureDetector( onPanUpdate: (DragUpdateDetails details) { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition);

        setState(() {
          _points = new List.from(_points)..add(localPosition);
        });
      },
      onPanEnd: (DragEndDetails details) => _points.add(null),
    ),
    CustomPaint(painter: SignaturePainter(_points), size: Size.infinite),
  ],
);

} }

class DemoApp extends StatelessWidget { Widget build(BuildContext context) => new Scaffold(body: new Signature()); }

void main() => runApp(new MaterialApp(home: new DemoApp())); ``` 签名参考文章

截图保存签名图层

使用RepaintBoundary 在State中实现如下代码

```dart class _MyHomePageState extends State { GlobalKey rootWidgetKey = GlobalKey(); ...

@override Widget build(BuildContext context) { return RepaintBoundary( key: rootWidgetKey, child: Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( ..... ), ), ); } }

``` 通过rootWidgetKey可以拿到RenderRepaintBoundary的引用,进来拿到内部组件的截图:

```dart class _MyHomePageState extends State { GlobalKey rootWidgetKey = GlobalKey();

Future _capturePng() async { try { RenderRepaintBoundary boundary = rootWidgetKey.currentContext.findRenderObject(); var image = await boundary.toImage(pixelRatio: 3.0); ByteData byteData = await image.toByteData(format: ImageByteFormat.png); Uint8List pngBytes = byteData.buffer.asUint8List(); return pngBytes;//这个对象就是图片数据 } catch (e) { print(e); } return null; } ... }

``` 截图功能参考文章