Flutter で画像を角丸に切って『キレイに』ボーダーをつける

画像を角丸に切って『キレイに』ボーダーをつける。こう。
<img width="220" alt="スクリーンショット 2020-02-13 19.03.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/4d874b3b-2ac2-0cd9-b16d-e2f4abc75d3c.png">

これが Flutter の場合、『キレイに』つけるのが地味に難題。
※一応だがキレイに書ける方法は一番下に載せてるので飛ばしても OK。

自分が思いつく実装方法は以下の 3 つ。

1. BoxDecoration で image, border, borderRadius を指定
2. BoxDecoration で border を表示、子をさらに ClipRRect で角丸クリップ
3. CustomPaint で clip したりして頑張る

1, 2 を以下で試してみる。

## 1. BoxDecoration で image, border, borderRadius を指定

```dart
    return Container(
      width: 200,
      height: 200,
      decoration: BoxDecoration(
        image: DecorationImage(
          image: NetworkImage('https://i.picsum.photos/id/563/200/200.jpg'),
        ),
        border: Border.all(color: Colors.white, width: 3),
        borderRadius: BorderRadius.circular(10),
      ),
    );
```

結果↓
<img width="220" alt="スクリーンショット 2020-02-13 18.50.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/56065038-aa14-27c7-d142-ee6a34705a2d.png">

なんだかんたんに出来たじゃん……ん？
<img width="290" alt="スクリーンショット 2020-02-13 18.50.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/f70befe5-fb53-914e-f507-b4ef6e4190b3.png">

外側が若干汚い。どうやら **画像の丸角部分が少しはみ出してしまう** ようだ。背景色やボーダーの色によってはごまかせるかもしれないが、場合によっては非常に目立ってしまう。

## 2. BoxDecoration で border を表示、子をさらに ClipRRect で角丸クリップ

```dart
    return Container(
      width: 200,
      height: 200,
      decoration: BoxDecoration( 
        border: Border.all(color: Colors.white, width: 3),
        borderRadius: BorderRadius.circular(10),
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(10),
        clipBehavior: Clip.hardEdge,
        child: Image.network('https://i.picsum.photos/id/563/200/200.jpg'),
      ),
    );
```

結果↓
<img width="220" alt="スクリーンショット 2020-02-13 18.55.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/a389096c-ec8e-d809-7f38-301f2f1f1bc8.png">

お、今度こそやったか……ん？
<img width="243" alt="スクリーンショット 2020-02-13 18.55.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/23f29232-bd2c-4ec1-1b4d-99d9cc6b4403.png">

今度は内側に緑の隙間が出来てしまった。これは **border 分 child が小さくなった結果、角丸が合わなくなった** ため。

## 2-2. 上記のコードを foregroundDecoration に変える

foregroundDecoration に設定すれば、その名の通り foreground にボーダーが書かれて、 child が縮まなくなる。

```dart
    return Container(
      width: 200,
      height: 200,
      foregroundDecoration: BoxDecoration( 
```

結果↓
<img width="290" alt="スクリーンショット 2020-02-13 18.50.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/4acb38d0-52d8-c340-75df-0ca617de4f44.png">

やっぱり汚くなる。 1 は結果的にこれと同じことをしているのであろう。

## 2-3. 若干の padding を入れる（現状一番キレイ）

画像を若干内側に引っ込ませればいいんじゃないの？　という発想で padding を加えてみる。

```dart
    return Container(
      width: 200,
      height: 200,
      foregroundDecoration: BoxDecoration(
        border: Border.all(color: Colors.white, width: 3),
        borderRadius: BorderRadius.circular(10),
      ),
      // 魔法の padding
      padding: const EdgeInsets.all(0.5),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(10),
        clipBehavior: Clip.hardEdge,
        child: Image.network('https://i.picsum.photos/id/563/200/200.jpg'),
      ),
    );
```

結果↓
<img width="220" alt="スクリーンショット 2020-02-13 19.03.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/4d874b3b-2ac2-0cd9-b16d-e2f4abc75d3c.png">
<img width="219" alt="スクリーンショット 2020-02-13 19.03.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17722/86ee5721-abff-d02d-3ca4-2fbd79b92291.png">

うまくいった！
うまくいった……けどさ……うーん。

他にきれいなやり方が見つからなかったら後で Issue に投げてみる。

補足: わかりやすいように border width: 3 にしてるけど、↑のやり方は width: 1 でも一応キレイに表示できる。
補足2: ClipRRect の `clipBehavior: Clip.hardEdge` は単に処理速度をちょっとでも上げたいという思いで設定している。デフォルトの antialias にしていても、 padding 入れないとやっぱりキレイにはならない。