Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use SignatureController instead of a GlobalKey #40

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,31 @@ This is a very simple widget that allow drawing by finger on a widget and be abl
For very basic usage, please check the example file [here](https://github.com/kiwi-bop/flutter_signature_pad/blob/master/example/lib/main.dart)

```dart
Signature(
color: Colors.black,// Color of the drawing path
strokeWidth: 5.0, // with
backgroundPainter: null, // Additional custom painter to draw stuff like watermark
onSign: null, // Callback called on user pan drawing
key: null, // key that allow you to provide a GlobalKey that'll let you retrieve the image once user has signed
);
Widget build(BuildContext context) {
// Controller is used
final controller = SignatureController(
backgroundPainter: null, // Additional custom painter to draw stuff like watermark
);
return Column(children: [
Signature(
controller: controller
color: Colors.black, // Color of the drawing path
strokeWidth: 5.0, // width of the drawing path
onSign: null, // Callback called on user pan drawing
),
ElevatedButton(
onPressed: () => controller.getData().then((image) => /* process image */),
child: Text("Save")),
ElevatedButton(
onPressed: () => controller.clear(),
child: Text("Clear"))
]);
}
```

## API

Once you retrieved the SignatureState from your GlobalKey, you'll have access to this API:
The controller exposes the following methods:

`clear`: allow you to clear the area to start again

Expand Down
19 changes: 8 additions & 11 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
Expand Down Expand Up @@ -60,7 +59,8 @@ class _MyHomePageState extends State<MyHomePage> {
ByteData _img = ByteData(0);
var color = Colors.red;
var strokeWidth = 5.0;
final _sign = GlobalKey<SignatureState>();
final controller =
SignatureController(backgroundPainter: _WatermarkPaint("2.0", "2.0"));

@override
Widget build(BuildContext context) {
Expand All @@ -72,13 +72,12 @@ class _MyHomePageState extends State<MyHomePage> {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Signature(
controller: controller,
color: color,
key: _sign,
onSign: () {
final sign = _sign.currentState;
debugPrint('${sign.points.length} points in the signature');
debugPrint(
'${controller.points.length} points in the signature');
},
backgroundPainter: _WatermarkPaint("2.0", "2.0"),
strokeWidth: strokeWidth,
),
),
Expand All @@ -94,11 +93,10 @@ class _MyHomePageState extends State<MyHomePage> {
MaterialButton(
color: Colors.green,
onPressed: () async {
final sign = _sign.currentState;
//retrieve image data, do whatever you want with it (send to server, save locally...)
final image = await sign.getData();
final image = await controller.getData();
var data = await image.toByteData(format: ui.ImageByteFormat.png);
sign.clear();
controller.clear();
final encoded = base64.encode(data.buffer.asUint8List());
setState(() {
_img = data;
Expand All @@ -109,8 +107,7 @@ class _MyHomePageState extends State<MyHomePage> {
MaterialButton(
color: Colors.grey,
onPressed: () {
final sign = _sign.currentState;
sign.clear();
controller.clear();
setState(() {
_img = ByteData(0);
});
Expand Down
101 changes: 59 additions & 42 deletions lib/flutter_signature_pad.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
class SignatureController {
final CustomPainter? backgroundPainter;
List<Offset?> points = <Offset?>[];
void Function()? _onChange;
_SignaturePainter? _painter;
late Size _lastSize;

SignatureController({this.backgroundPainter});

bool get hasPoints => points.isNotEmpty;

Future<ui.Image> getData() {
var recorder = ui.PictureRecorder();
var origin = const Offset(0.0, 0.0);
var paintBounds = Rect.fromPoints(
_lastSize.topLeft(origin), _lastSize.bottomRight(origin));
var canvas = Canvas(recorder, paintBounds);
if (backgroundPainter != null) {
backgroundPainter!.paint(canvas, _lastSize);
}
_painter!.paint(canvas, _lastSize);
var picture = recorder.endRecording();
return picture.toImage(_lastSize.width.round(), _lastSize.height.round());
}

void clear() {
points = [];
if (_onChange != null) {
_onChange!();
}
}
}

class Signature extends StatefulWidget {
final SignatureController controller;
final Color color;
final double strokeWidth;
final CustomPainter? backgroundPainter;
final Function? onSign;

Signature({
const Signature({
required this.controller,
this.color = Colors.black,
this.strokeWidth = 5.0,
this.backgroundPainter,
this.onSign,
Key? key,
}) : super(key: key);

SignatureState createState() => SignatureState();
@override
State<Signature> createState() => SignatureState();

static SignatureState? of(BuildContext context) {
return context.findAncestorStateOfType<SignatureState>();
Expand Down Expand Up @@ -49,20 +82,24 @@ class _SignaturePainter extends CustomPainter {
}

class SignatureState extends State<Signature> {
List<Offset?> _points = <Offset?>[];
_SignaturePainter? _painter;
late Size _lastSize;

SignatureState();

@override
void initState() {
super.initState();
widget.controller._onChange = () {
setState(() {});
};
}

@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => afterFirstLayout(context));
_painter = _SignaturePainter(points: _points, strokeColor: widget.color, strokeWidth: widget.strokeWidth);
widget.controller._painter = _SignaturePainter(points: widget.controller.points, strokeColor: widget.color, strokeWidth: widget.strokeWidth);
return ClipRect(
child: CustomPaint(
painter: widget.backgroundPainter,
foregroundPainter: _painter,
painter: widget.controller.backgroundPainter,
foregroundPainter: widget.controller._painter,
child: GestureDetector(
onVerticalDragStart: _onDragStart,
onVerticalDragUpdate: _onDragUpdate,
Expand All @@ -81,27 +118,30 @@ class SignatureState extends State<Signature> {
RenderBox referenceBox = context.findRenderObject() as RenderBox;
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
setState(() {
_points = List.from(_points)..add(localPosition)..add(localPosition);
widget.controller.points = List.from(widget.controller.points)
..add(localPosition)
..add(localPosition);
});
}

void _onTapUp(TapUpDetails details) {
RenderBox referenceBox = context.findRenderObject() as RenderBox;
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
setState(() {
_points = List.from(_points)..add(localPosition);
widget.controller.points = List.from(widget.controller.points)
..add(localPosition);
if (widget.onSign != null) {
widget.onSign!();
}
});
_points.add(null);
widget.controller.points.add(null);
}

void _onDragStart(DragStartDetails details) {
RenderBox referenceBox = context.findRenderObject() as RenderBox;
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);
setState(() {
_points = List.from(_points)..add(localPosition)..add(localPosition);
widget.controller.points = List.from(widget.controller.points)..add(localPosition)..add(localPosition);
});
}

Expand All @@ -110,39 +150,16 @@ class SignatureState extends State<Signature> {
Offset localPosition = referenceBox.globalToLocal(details.globalPosition);

setState(() {
_points = List.from(_points)..add(localPosition);
widget.controller.points = List.from(widget.controller.points)..add(localPosition);
if (widget.onSign != null) {
widget.onSign!();
}
});
}

void _onDragEnd(DragEndDetails details) => _points.add(null);

Future<ui.Image> getData() {
var recorder = ui.PictureRecorder();
var origin = Offset(0.0, 0.0);
var paintBounds = Rect.fromPoints(_lastSize.topLeft(origin), _lastSize.bottomRight(origin));
var canvas = Canvas(recorder, paintBounds);
if (widget.backgroundPainter != null) {
widget.backgroundPainter!.paint(canvas, _lastSize);
}
_painter!.paint(canvas, _lastSize);
var picture = recorder.endRecording();
return picture.toImage(_lastSize.width.round(), _lastSize.height.round());
}

void clear() {
setState(() {
_points = [];
});
}

bool get hasPoints => _points.length > 0;

List<Offset?> get points => _points;
void _onDragEnd(DragEndDetails details) => widget.controller.points.add(null);

afterFirstLayout(BuildContext context) {
_lastSize = context.size!;
widget.controller._lastSize = context.size!;
}
}
2 changes: 1 addition & 1 deletion test/flutter_signature_pad_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart';

void main() {
test('adds one to input values', () {
final sign = Signature();
final sign = Signature(controller: SignatureController());
expect(sign, sign);
});
}