SNSのシャッターボタンを再現

## はじめに

カメラで使用するシャッターボタンをSNSを参考に再現する。
今回再現するものはTwitterとInstagram
  

|Default|Twitter|Instagram|
|:--:|:--:|:--:|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/4e253613-0238-2d16-6764-5c098562b14b.png", width=100px, height=100px>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/ef3af2aa-e0bc-95ca-5722-a16771c770f4.png", width=100px, height=100px>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/56dc0371-6b12-2184-0b9f-1d7a9255df01.png", width=100px, height=100px>|

## TL;DR

- `TextButton`を使用
- `style`を指定
    - 枠線は`side`を指定
    - 形状は`shape`に`CircleBorder`を指定

## デフォルト

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/4e253613-0238-2d16-6764-5c098562b14b.png", width=100px, height=100px>
白色の枠線，塗りつぶしはなし(カメラ画像，背景色)のボタン

```dart
class ShutterButtonDefault extends StatelessWidget {
  final Function()? onPressed;

  const ShutterButtonDefault({
    super.key,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    const double size = 64.0;

    return TextButton(
      style: TextButton.styleFrom(
        fixedSize: const Size(size, size),
        side: const BorderSide(
          color: Colors.white,
          width: 4.0,
        ),
        shape: const CircleBorder(),
      ),
      onPressed: onPressed,
      child: const SizedBox(),
    );
  }
}
```

- `TextButton`は`child`が必須でありnull許容ではないため，空ウィジェット`SizedBox()`を指定
- `style`の指定する際に`TextButton.styleFrom`を使用することで変更する項目のみ指定すればよい
    - `fixedSize`: ボタンのサイズを指定
    - `side`: `CircleBorder`で枠線を指定
    - `shape`: `CircleBorder`で形状を指定

## Twitter

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/ef3af2aa-e0bc-95ca-5722-a16771c770f4.png", width=100px, height=100px>
白色の枠線，塗りつぶしは少し透過率のあるボタン

```dart
class ShutterButtonTwitter extends StatelessWidget {
  final Function()? onPressed;

  const ShutterButtonTwitter({
    super.key,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    const double size = 64.0;

    return TextButton(
      style: TextButton.styleFrom(
        backgroundColor: Colors.white70,
        fixedSize: const Size(size, size),
        side: const BorderSide(
          color: Colors.white,
          width: 4.0,
        ),
        shape: const CircleBorder(),
      ),
      onPressed: onPressed,
      child: const SizedBox(),
    );
  }
}
```

- デフォルトに`backgroundColor`を追加
    - `backgroundColor`: ボタンの色を指定
    - `Colors.white70`: 透過率30%(不透過率70%)の白色を指定

## Instagram

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595983/56dc0371-6b12-2184-0b9f-1d7a9255df01.png", width=100px, height=100px>
白色の枠線，塗りつぶしはなし(カメラ画像，背景色)の空間が少し，白色の丸の丸ボタン

```dart
class ShutterButtonInstagram extends StatelessWidget {
  final Function()? onPressed;

  const ShutterButtonInstagram({
    super.key,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    const double size = 64.0;

    return OutlinedButton(
      style: TextButton.styleFrom(
        padding: const EdgeInsets.all(0.0),
        fixedSize: const Size(size, size),
        side: const BorderSide(
          color: Colors.white,
          width: 4.0,
        ),
        shape: const CircleBorder(),
      ),
      onPressed: onPressed,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Container(
          decoration: const BoxDecoration(
            shape: BoxShape.circle,
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}
```

- `TextButton`にはデフォルトで`padding`が設定されているため`style`で`EdgeInsets.all(0.0)`を指定
- `child`に枠線の内側を作成
    - `padding`を指定して透明の空間を作成
    - `Container`に`BoxDecoration`を指定して白丸を作成

白丸の部分は`CircleAvatar`や`CustomPainter`を使用しても再現が可能

## 参考文献

- [Flutter 公式ドキュメント](https://api.flutter.dev/flutter/material/TextButton-class.html)
- [[Flutter]コピペで使える！ボタンのデザイン16種類をまとめました](https://qiita.com/coka__01/items/30716f42e4a909334c9f)
- [FlutterでiPhoneのカメラのシャッターボタンみたいなやつを作る](https://zenn.dev/shogo/articles/5a48a00aef89a5)