🍥

【Flutter】CustomPainterでボタンを作る

2021/11/22に公開

FlutterのMaterialライブラリにも使いやすいボタンがたくさんあるけど、ほんの少し凝ったボタンを作りたい。
そんな機会があったので、CustomPainterを使って作ってみた。

ボタン例

今回は、一番上のスラッシュが付いたボタンを例にした手順メモ。
参考:他の2つのボタンのコード
https://github.com/nasubibocchi/custom_paint_sample

1. CustomPainterで絵を描く

作りたいデザインを実際に表現してみる。

①CustomPainterをextendsしたクラスを作る

そうすると、'paint'と'shouldRepaint'をオーバーライドしてくださいねと言われる。

class RestrictedButtonPainter extends CustomPainter {

  
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
  }

  
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}

paint : どんなサイズのどんな絵を描くのかを指定する
shouldRepaint : 再描画したときに新しく情報を追加して図形を描き直すのかを指定する

②shouldRepaintの編集

例では特に図形を更新しないので下記のようにfalseを返す。


  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }

③図のサイズを指定する。

例では別ファイルにサイズの定数『restrictedButtonSize』というのを定義しておいてSize()の引数にしている。


  void paint(Canvas canvas, Size size) {
    size = Size(restrictedButtonSize.width, restrictedButtonSize.height);
    //・・・
  }

④どんな図形を描くかを定義する

Paint()では、どんな図形を描くかを指定できる。
color : 図形の色を指定
style : 塗りつぶすか塗りつぶさないかを指定(PaintingStyle.stroke / PaintingStyle.fill)
strokeWidth : 線の太さを指定

線のエッジを丸くしたりとかもできる。


  void paint(Canvas canvas, Size size) {
    size = Size(restrictedButtonSize.width, restrictedButtonSize.height);
    //↓ここ。後で使うので名前をつけておく。
    var paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 4;

    //・・・
  }

⑤線を引くための準備をする


図の中の、スラッシュみたいな部分の線が例。
線の始点と終点を定義しておく。

class RestrictedButtonPainter extends CustomPainter {
//ここから
Offset topRight = Offset(restrictedButtonSize.width * (1 - 0.14),
      restrictedButtonSize.height * 0.14);
Offset bottomLeft = Offset(restrictedButtonSize.width * 0.14,
      restrictedButtonSize.width * (1 - 0.14));
//ここまで

  
  void paint(Canvas canvas, Size size) {
    // ・・・
  }

  
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // ・・・
  }
}

今編集しているウィジェットの左上端から数えてどこか?をOffsetを使って指定する。
後でtopRight → bottomLeftに向けて引く処理をする。

⑥円を描く準備をする

円をどこに描くかを定義しておく。必要になるのは円の中心位置なので、左上端から半径分だけ進んだところを指定。

Offset center =
      Offset(restrictedButtonSize.width / 2, restrictedButtonSize.height / 2);

⑦円と線を引く

図形を描く処理は、canvasで行う。

canvas.○○で、円や線、長方形、多角形などがかける。


  void paint(Canvas canvas, Size size) {
    // ・・・
    canvas.drawCircle(center, restrictedButtonSize.width / 2, paint); //円
    canvas.drawLine(topRight, bottomLeft, paint); //線 
  }

drawCircle()は、第一引数が中心位置、第二引数が半径、第三引数がPaintとなっているので、⑥までで準備したものを入れる。

drawLine()は第一、第二引数で線の視点と終点を指定。↑の円とは別の色やスタイルの線画描きたかったら、別のPaintを作っておいて第三引数に入れる。

これで描くものの準備ができた!

2. 1.を使ったボタンウィジェットを作る

今回の例では、1.で作った図形をボタンとして使い回せるようにする。
CustomPaint()のpainter: に、1.で作ったクラスを指定する。

class RestrictedButtonWidget extends StatelessWidget {
  const RestrictedButtonWidget({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return SizedBox(
      height: restrictedButtonSize.height,
      width: restrictedButtonSize.width,
      child: InkWell(
          onTap: () {
            // print('a'); //debug
          },
          child: CustomPaint(
            painter: RestrictedButtonPainter(), //作ったやつを入れる
          )),
    );
  }
}

3. ページの使いたいとこに2.のウィジェットを配置

2.まででボタンを呼び出せるようになったので、配置したいところでRestrictedButtonWidget()を呼ぶ。

例は線と円だけのボタンだけど、IconDataや画像を組み合わせると結構幅広いデザインのボタンが作れそう。

Discussion