Flutterでよく使うコールバック VoidCallback

##始めに
Flutterを3ヶ月以上触って、少しは慣れてきたかと思いきや、まだまだ日々発見や反省の繰り返しです。。

今回はコールバックに関して、流石に少しは慣れてきたので、自分のメモ的にまとめておこうと思います。
コールバックはモバイルアプリでは基礎であり、頻出だと思いますが、まだまだ苦手意識があります。逆に経験豊富なエンジニアの方はコールバックにもかなり精通していて、よく話題にも出ると思うので必ず慣れておくべき項目の一つだと思っています。


## 今回作成するもの

![スクリーンショット 2020-11-10 1.54.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479957/f0a00044-f59f-b505-a260-b1e3e17b3649.png)


ボタンが並んでいて、「コールバック」と書かれたボタンをタップすると、コンソールに「コールバックボタンが押されました」と出力され、
また緑色の大きいボタンをタップすると、コンソールに「大きい緑のボタンが押されました」と出力されるアプリです。

![スクリーンショット 2020-11-10 1.54.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479957/e6e01b32-22f3-8b94-0817-8df291348340.png)


今回のサンプルサプリのコードです。main.dartファイル内で完結しています。

```main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CallBack Sample',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('コールバックSample'),
      ),
      body: _Body(),
    );
  }
}

class _Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildVoidCallbackButton(
          () => print('コールバックボタンが押されました'),
        ),
        _buildBigGreenButton(
          () => print('大きい緑のボタンが押されました'),
        ),
      ],
    );
  }

  Widget _buildVoidCallbackButton(VoidCallback onTapped) {
    return RaisedButton(
      child: Text('コールバック'),
      onPressed: onTapped,
    );
  }

  Widget _buildBigGreenButton(VoidCallback onTapped) {
    return SizedBox(
      width: 200,
      height: 150,
      child: _GreenButton(
        onTapped: onTapped,
      ),
    );
  }
}

class _GreenButton extends StatelessWidget {
  const _GreenButton({
    this.onTapped,
  });

  final VoidCallback onTapped;

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('大きな緑ボタン'),
      color: Colors.greenAccent,
      onPressed: onTapped,
    );
  }
}

```

##VoidCallbackとは
今回はVoidCallbackを扱います。VoidCallbackは「返り値を返さず、イベントを通知してくれるもの」というイメージです。

どういう時に使うのか...

・ボタンが押されたことを通知する

ことが多いと思われます。また「返り値を返さず、」とは、ボタンがタップされたことを通知してくれますが、そのタップされたボタンが持つ情報を教えてくれたりはしないということです。

コールバックボタンを作成している部分のコードを確認すると、以下のようになっています。

```dart
Widget _buildVoidCallbackButton(VoidCallback onTapped) {
  return RaisedButton(
    child: Text('コールバック'),
    onPressed: onTapped,
  );
}
```

コールバックがタップされた時、VoidCallback型のonTappedが呼ばれています。では、この_buildVoidCallbackButtonの呼び出し側を確認すると、

```dart
_buildVoidCallbackButton(
  () => print('ボタンが押されました'),
),
```

となっており、引数onTappedのところにはボタンがタップされた時の挙動を渡しています。

##コールバックボタンでやっていること

Widgetの構造を確認してみると、以下のようになっています。

- MyHomePage　→　_Body →　RaisedButton（_buildVoidCallbackButtonメソッドで作成したボタン）

では「コールバック」ボタンをタップした時、コンソールに「コールバックボタンが押されました」と出力されるまでに何が起こっているかというと、

- RaisedButton　→（ボタンがタップされた時、VoidCallbackが_Bodyに通知する） → _Body →　_Body内のprint文で「コールバックボタンが押されました」と出力される

上で説明したように、VoidCallbackはあくまでも「返り値を返さない」ので、コード側のprint文のところも、

```dart
() => print('ボタンが押されました'),
```

となっており、```()=>```....つまり返り値の型を指定していません。

実際のプロジェクトや少しレイアウトを凝ってアプリを作成していると、ここまでシンプルな例も珍しいと思います。
なので今回もう一つ緑色の大きなボタンを用意しました。

##緑の大きいボタンをタップした時に起こっていること

コールバックボタンと同じように構造を確認します。

MyHomePage　→　Body　→　buildBigGreenButton（ボタンを作成するメソッド）　→　GreenButton　→　RaisedButton

となっています。今度はコールバックボタンの時より少し階層が増えています。緑のボタンをタップすると、

- RaisedButton →　GreenButton　→　buildBigGreenButton（ボタンを作成するメソッド）　→　Body　→　Body内のprint文で「大きい緑のボタンが押されました」と出力される

1つ目のコールバックボタンより、少しだけ複雑なレイアウトコードです。_buildの引数として、またGreenButtonのコンストラクタの引数として、VoidCall型のonTappedを順番に渡していることが分かるでしょうか。Widgetの階層が深くなっても、VoidCallbackを使ってタップイベントを検出することが出来ることが伝わると良いです。

##実際にはどのように使うか

今回はprint文を呼んでいるだけなので、あまりサンプルとして良くなかったかもしれませんが、

- 複雑なレイアウトのコードで、ボタン押された時に次の画面に遷移する
- ボタン側では知り得ないBody側のテキストフォームで入力された情報などを使って登録処理をする

などのパターンをイメージすれば、VoidCallbackの使いどころがイメージできると思います。

次の画面に渡したい引数などはボタンの方ではなく、Bodyの方に存在するものだと思いますし、

入力フォームなどの内容を使って登録処理などを行う場合、登録ボタンなどをタップされた時にBody側でしかテキストフィールドの入力内容が分からないと思います。

そのような時にVoidCallbackでボタンがタップされたことを取得すると良い場合が多いです。

##返り値を返すコールバックは？

今回は返り値を返さないコールバック、VoidCallbackのメモになります。

返り値を返すコールバックに関しても、いずれまとめたいです。書き方をよく忘れるので。。