1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

flutterで手書き機能を作成する

Last updated at Posted at 2025-10-09

はじめに

3年ほどFlutterを触らせてもらっているのにも関わらずモバイルの手書き機能を実装したことがなかったため今回の記事を作成しました。
今回はそこで使用したCustomPainterを紹介します。

CustomPainterとは

CustomPainterは、Flutterで独自の描画(カスタムペイント)を行うためのクラスです。
Widgetツリーで通常のWidgetではできない自由なグラフィックス(図形・線・テキスト・画像など)をCanvas上に描画できます。

主に以下の用途で使われます:

  • お絵描き機能
  • グラフやチャートの描画
  • アニメーションのカスタム描画
  • ゲームや特殊UI

今回はこの中のお絵描き機能の方を実装してみました。

実装

実装方法基本構造としては以下になると思います。

  1. CustomPainterクラスを継承したクラスを作る
  2. paint(Canvas canvas, Size size)メソッド内で描画処理を書く
  3. shouldRepaintで再描画の必要性を返す
  4. CustomPaintウィジェットにPainterを渡して利用

上記を加味して手書きだけするコードを作成しました。

import 'package:flutter/material.dart';

void main() => runApp(const DrawApp());

class DrawApp extends StatelessWidget {
  const DrawApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(body: DrawingBoard()),
    );
  }
}

class DrawingBoard extends StatefulWidget {
  const DrawingBoard({super.key});

  @override
  _DrawingBoardState createState() => _DrawingBoardState();
}

class _DrawingBoardState extends State<DrawingBoard> {
  List<Offset?> points = [];

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          points.add(details.localPosition);
        });
      },
      onPanEnd: (details) {
        setState(() {
          points.add(null); // 線の区切り
        });
      },
      child: CustomPaint(
        painter: MyPainter(points),
        size: Size.infinite,
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  final List<Offset?> points;
  MyPainter(this.points);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 4.0
      ..strokeCap = StrokeCap.round;

    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(MyPainter oldDelegate) => true;
}

上記コードで以下画像のように手書き機能が実装できます!
スクリーンショット 2025-10-09 13.50.15.png

解説

DrawingBoard(手書きボード)

class DrawingBoard extends StatefulWidget {
  const DrawingBoard({super.key});

  @override
  _DrawingBoardState createState() => _DrawingBoardState();
}

手書きの状態(座標や線)を持つのでStatefulWidgetを使用しています。

class _DrawingBoardState extends State<DrawingBoard> {
  List<Offset?> points = [];

pointsは指やペンが動いた位置(座標)を記録するリストです。
Offsetは2D座標(x, y)を表します。
nullは線の区切り(指を離したタイミング)を示します。

GestureDetector(ジェスチャー検知)

return GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          points.add(details.localPosition);
        });
      },
      onPanEnd: (details) {
        setState(() {
          points.add(null); // 線の区切り
        });
      },
      child: CustomPaint(
        painter: MyPainter(points),
        size: Size.infinite,
      ),
    );
  • GestureDetectorで指の動きを検知
    • onPanUpdate: 画面上を指やペンが動くたび、その座標をpointsに追加
    • onPanEnd: 指やペンが離れたとき、nullを追加して線の区切りを明示
  • CustomPaintにMyPainterを渡して、pointsに従って描画します

MyPainter(カスタムペインター)

class MyPainter extends CustomPainter {
  final List<Offset?> points;
  MyPainter(this.points);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 4.0
      ..strokeCap = StrokeCap.round;

    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(MyPainter oldDelegate) => true;
}
  • paintメソッドで、pointsの座標同士を線で結びながら描画
  • paintの設定で線の色や太さを指定
  • shouldRepaintは毎回再描画するためtrueを返す

以上になります。

最後に

これで手書きができるようになりました!
ただこのコードだと手書きができるだけなので、消しゴム、色変更、保存などはできないです。別途追加コードが必要になります。
私のように手書き機能やったことないなって人の参考程度になればなと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?