#はじめに
こちらの記事はユアマイスターアドベントカレンダー2021、8日目の記事です
(投稿が遅れ、申し訳ありません!!)。
業務で手書き署名機能を実装する可能性がでたので、実際にどのように作るのか試してみました。
手書き署名で調べてましたが、実際にはペイント機能を作ればよさそうでした。
#参考
下記を参考にさせてもらいました。
https://www.egao-inc.co.jp/programming/flutter-paint/
#実装
後に説明するSignerとクリア用ボタンを設置しておきます。
import 'package:flutter/material.dart';
import 'package:test/signer.dart';
class SignPage extends StatefulWidget {
const SignPage({Key? key}) : super(key: key);
@override
_SignPageState createState() => _SignPageState();
}
class _SignPageState extends State<SignPage> {
final SignController _controller = SignController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('署名'),
centerTitle: true,
),
body: Signer(
paintController: _controller,
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: "clear",
onPressed: () => _controller.clear(),
child: const Text('クリア'),
),
],
),
);
}
}
GestureDetectorでタッチイベントを検出します。
https://api.flutter.dev/flutter/widgets/GestureDetector-class.html
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/sign_history.dart';
class Signer extends StatefulWidget {
final SignController paintController;
Signer({required this.paintController})
: super(key: ValueKey<SignController>(paintController));
@override
_SignerState createState() => _SignerState();
}
class _SignerState extends State<Signer> {
@override
Widget build(BuildContext context) {
return SizedBox(
child: GestureDetector(
child: CustomPaint(
willChange: true,
painter: _CustomPainter(
widget.paintController._paintHistory,
repaint: widget.paintController,
),
),
onPanStart: _onPaintStart,
onPanUpdate: _onPaintUpdate,
onPanEnd: _onPaintEnd,
),
width: double.infinity,
height: double.infinity,
);
}
void _onPaintStart(DragStartDetails start) {
widget.paintController._paintHistory
.addPaint(_getGlobalToLocalPosition(start.globalPosition));
widget.paintController._notifyListeners();
}
void _onPaintUpdate(DragUpdateDetails update) {
widget.paintController._paintHistory
.updatePaint(_getGlobalToLocalPosition(update.globalPosition));
widget.paintController._notifyListeners();
}
void _onPaintEnd(DragEndDetails end) {
widget.paintController._paintHistory.endPaint();
widget.paintController._notifyListeners();
}
Offset _getGlobalToLocalPosition(Offset global) {
return (context.findRenderObject() as RenderBox).globalToLocal(global);
}
}
class _CustomPainter extends CustomPainter {
final SignHistory _paintHistory;
_CustomPainter(this._paintHistory, {required Listenable repaint})
: super(repaint: repaint);
@override
void paint(Canvas canvas, Size size) {
_paintHistory.draw(canvas, size);
}
@override
bool shouldRepaint(_CustomPainter oldDelegate) => true;
}
class SignController extends ChangeNotifier {
final SignHistory _paintHistory = SignHistory();
final Color _drawColor = Colors.black;
final double _thickness = 2.0;
final Color _backgroundColor = Colors.white;
SignController() : super() {
Paint paint = Paint();
paint.color = _drawColor;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = _thickness;
_paintHistory.currentPaint = paint;
_paintHistory.backgroundColor = _backgroundColor;
}
void _notifyListeners() {
notifyListeners();
}
void clear() {
_paintHistory.clear();
notifyListeners();
}
}
描画の履歴を管理するwidgetです。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class _PaintData {
_PaintData({
required this.path,
}) : super();
Path path;
}
class SignHistory {
final List<MapEntry<_PaintData, Paint>> _paintList =
<MapEntry<_PaintData, Paint>>[];
final Paint _backgroundPaint = Paint();
bool _inDrag = false;
late Paint currentPaint;
set backgroundColor(color) => _backgroundPaint.color = color;
void clear() {
if (!_inDrag) {
_paintList.clear();
}
}
void addPaint(Offset startPoint) {
if (!_inDrag) {
_inDrag = true;
Path path = Path();
path.moveTo(startPoint.dx, startPoint.dy);
_PaintData data = _PaintData(path: path);
_paintList.add(MapEntry<_PaintData, Paint>(data, currentPaint));
}
}
void updatePaint(Offset nextPoint) {
if (_inDrag) {
_PaintData data = _paintList.last.key;
Path path = data.path;
path.lineTo(nextPoint.dx, nextPoint.dy);
}
}
void endPaint() {
_inDrag = false;
}
void draw(Canvas canvas, Size size) {
canvas.drawRect(
Rect.fromLTWH(
0.0,
0.0,
size.width,
size.height,
),
_backgroundPaint,
);
for (MapEntry<_PaintData, Paint> data in _paintList) {
canvas.drawPath(data.key.path, data.value);
}
}
}