Flutterのカスタムウィジェット(CustomPaint編)

Flutterでカスタムウィジェットを作るには主にCustomPaintウィジェットとGestureDetectorウィジェットを使う。

GestureDetectorウィジェットについてはこちら

https://qiita.com/WAsub/items/089880108be9277c852c

## CustomPaintウィジェット
色んな図形を描くウィジェット。いくつも重ねてかなり自由な形を作ることができる。

#### Containerとかと同じ感覚で適当なところに書く
``` dart:buildの中とかに
CustomPaint(
  /** 基準にするサイズ。なくてもエラーは出ないけどあった方がいい */
  size: Size(width, height),
  /** 図形を描画するクラス */
  painter: HusenPainter(),
)
```

#### 図形を描画するクラスをつくる
これはmain.dartの中に作ってもいいし、別ファイルに作ってインポートしてもいい。  

``` dart:図形を描画するクラス
class HusenPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    /** ここに図形を描画する処理を書く(下記参照) */
  }
  @override /** これは再描画が必要か。値を変化させたりするならtrueにする */
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}
```
> <details><summary><span>**<font color="#ed6d46">paint内の書き方</font>**</span></summary>
<div>

> ```dart:共通 
> // 塗り潰し
> Paint paint = Paint(); // 図形の色やらなんやらを指定できる
> paint.color = Colors.greenAccent; // 取り敢えず適当な色を指定
> // 線
> Paint paint = Paint()
>   ..color = Colors.green // 色
>   ..strokeCap = StrokeCap.round // 線の端を丸める(丸めないはbuttやsquare)
>   ..style = PaintingStyle.stroke // 線にするか(塗り潰しはfull)
>   ..strokeWidth = 2; // 線の太さ
> // paint.color = Colors.greenAccent; //みたいに指定してもいい
> ```
> ```dart:四角形
> /** 上の共通を先に書く */
> // CustomPaintのsizeで指定したサイズいっぱいの四角形を描く
> var rect = Rect.fromLTWH(0, 0, size.width, size.height);
> // 上のrectと共通のpaintで描画
> canvas.drawRect(rect, paint);
> ```
> ```dart:円
> /** 上の共通を先に書く */
> // 円の中心点を指定
> var center = Offset(size.width / 2, size.height / 2);
> // 半径を指定
> double radius = 100.0;
> // 上の二つと共通のpaintで描画
> canvas.drawCircle(center, radius, paint);
> ```
> ```dart:三角形や多角形
> /** 上の共通を先に書く */
> var path = new Path();
> // 始点へ移動()
> path.moveTo(size.width /2, 0);
> // 右下へ向かって線を描く
> path.lineTo(size.width, size.height);
> // 左へ向かって線を描く
> path.lineTo(0, size.height);
> // 始点に向かって閉じる
> path.close(); // path.lineTo(size.width /2, 0); でもいい
> 	// 上で描いたpathと共通のpaintで描画
> canvas.drawPath(path, paint);
> ```
> `path.quadraticBezier(x1,y1,x2,y2)で現在位置から(x1,y1)を中間地点として(x2, y2)までに曲線を描くこともできるらしい。`
> <font color="#ed6d46">※未検証のためちょっと違うかも</font>
> 
> ```dart:アーチ
> /** 上の共通を先に書く */
> // 基準になる四角形(正方形なら円になるし、長方形なら楕円になる)
> var rect = Rect.fromLTWH(0, 0, size.width, size.height);
> // スタート地点(円の頂点)
> var startAngle = -90 * math.pi / 180;
> // どのくらいの長さ(270度のところまで)
> var sweepAngle = 270 * math.pi / 180;
> // 上3つと、円の中心に向かって閉じるかと、共通のpaintで描画
> canvas.drawArc(rect, startAngle, sweepAngle, false, paint);
> ```
> `スタート地点とどこまで引くかはラジアンで求めるらしい(むずかしい)
> 円系を描くときは、ファイルの上部で import 'dart:math' as math; をインポートするといい`
> その他
> 線を描画するdrawLine()
> ビットマップ画像を描画するdrawImage()
> テキストを描画するdrawParagraph()
> などがある
</div>
</details>
  
### サンプルコード(未完成)
以下のサンプルコードはカスタムウィジェットとしてコンポーネント化したもの。

```dart
HusenContainer(
  height: 300,
  width: 300,
)
```
などのように使うことができる。
右下が捲れた付箋みたいなのができる。
`2021/07/05現在未完成。なぜかというと子要素を持てないから。
そのうちContainerとかみたいにchildやその他の項目を追加する`

```dart:husenContainer.dart
import 'package:flutter/material.dart';
class HusenContainer extends StatelessWidget {
  /** コンストラクタ */
  double height;
  double width;
  Color color;
  Color backSideColor;
  HusenContainer({ 
    this.height,
    this.width,
    this.color,
    this.backSideColor,
  });
  @override
  Widget build(BuildContext context) {
    if(height == null){ height = 300;}
    if(width == null){ width = 300;}
    if(color == null){ color = Colors.greenAccent;}
    if(backSideColor == null){ backSideColor = Colors.green;}
 
    return CustomPaint(
      /** 基準にするサイズ。なくてもエラーは出ないけどあった方がいい */
      size: Size(width, height),
      /** 図形を描画するクラス */
      painter: HusenPainter(
        color: this.color,
        backSideColor: this.backSideColor,
      ),
    );
  }
}

/** 図形を描画するクラス */
class HusenPainter extends CustomPainter {
  Color color;
  Color backSideColor;
  HusenPainter({ 
    this.color,
    this.backSideColor,
  });
 
  @override
  void paint(Canvas canvas, Size size) {
    /** ここに図形を描画する処理を書く */
    Paint paint = Paint();
    paint.color = color;
    var path = Path();
 
    path.lineTo(size.width, 0);
    path.lineTo(size.width, size.height / 6 * 5);
    path.lineTo(size.width / 6 * 5, size.height);
    path.lineTo(0, size.height);
    path.lineTo(0, 0);
    canvas.drawPath(path, paint);
 
    paint = new Paint();
    paint.color = backSideColor;
    path = new Path();
 
    path.moveTo(size.width / 6 * 5, size.height / 6 * 5);
    path.lineTo(size.width, size.height / 6 * 5);
    path.lineTo(size.width / 6 * 5, size.height);
    path.lineTo(size.width / 6 * 5, size.height / 6 * 5);
    canvas.drawPath(path, paint);
  }
  @override /** これは再描画が必要か。値を変化させたりするならtrueにする */
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}
```
## 参考資料
[【Flutter】CustomPainterで図形を表示させる【円、三角形、四角形】](https://www.egao-inc.co.jp/programming/flutter_custompainter_shape/#i-3)
[FlutterでフルスクラッチでWidgetを作る時に知っておきたいこと](FlutterでフルスクラッチでWidgetを作る時に知っておきたいこと)
[FlutterのCustomPaintで図形を描いて遊ぶ](FlutterのCustomPaintで図形を描いて遊ぶ)
[Flutter drawArc() method draws full circle not just arc](https://stackoverflow.com/questions/50376727/flutter-drawarc-method-draws-full-circle-not-just-arc)
[quadraticBezierToの座標【Flutter】](https://teratail.com/questions/278775)