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; } ... }

``` 截圖功能參考文章