はじめに
iOSメインのスマホアプリエンジニアです。
iOSエンジニア視点からFlutterのコールバックの利用タイミングなどを自分なりにまとめました。
Dartの文化に反していたり、好みや等があるかもしれませんがご了承ください。
その他ご意見ございましたらご指摘ください。
VoidCallback
最もシンプルでFlutter callbackと検索すると真っ先に引っかかるコールバック方法です。
その名の通り、「空っぽのコールバック」
宣言
VoidCallbackを受ける側の実装。
voidCallback = (){ 
   print("voidCallback");
};
呼び出し
VoidCallbackを呼び出す
voidCallback.call();
// 以下でも可能。
voidCallback();
利用用途
明示的に「このコールバックには引数がないですよー」と表したいときに利用する。
ただ、後述のFunctionで事足りるため、チーム内で統一するために使わないようにするのも良いと思いました。
Function
コールバックを呼び出すタイミングで何かしらパラメータを渡したいことが多いと思います。
そのときに登場するのがこちらのFunctionになります。
引数なし
宣言
受ける側の実装。
// 引数なし。VoidCallbackと同じ。
Function function1;
// 受ける側の実装。
function1 = (){
  print("voidCallBack");
};
呼び出し
Functionを呼び出す
function1();
引数あり
宣言
受ける側の実装。
// 引数ありのコールバック
Function(String) function1;
// 受ける側の実装。
function1 = (args){
  print("function1 args:$args");
};
呼び出し
呼び出し側の実装。
function1("このパラメータが渡されます。");
引数あり応用編
宣言
// 引数が複数のコールバック。
Function(int, String) function2;
function2 = (args1 , args2){
  print("function2 args1:$args1 args2:$args2");
};
// 引数にクラスを渡す。
class Hoge {
  final String name;
  final int age;
  Hoge({this.name, this.age});
}
Function(Hoge) function3;
function3 = (hoge){
  print("function3 name:${hoge.name} age:${hoge.age}");
};
呼び出し
// 引数が複数のコールバックを呼び出す。
function2(111 , "function2の引数が渡されます。");
// 引数がクラスのコールバックを呼び出す
function3(Hoge(name: "name" ,age: 20));
利用用途
iOSでいうBlockやClosuresに似ており、単一のコールバックを返すに適していると思います。
複数のコールバックを返したい場合に、メンバー変数等に宣言すると冗長になります。
Delegateみたいなこと出来ないかな。と探しましたところありました!
mixinを使ったDelegateパターン
mixinを使ったDelegateパターンを使って実装してみましたところ、iOSエンジニアから見ると非常に馴染み深い書き方になります。
今回はStatefulWidgetに記載する例を記載しました。
宣言
// iOSで言うところのprotocolを宣言。
mixin HogeDelegate {
  void hoge();
  void hogeHoge1(String);
  void hogeHoge2(String args1, String args2);
  String hogehoge4();
}
// クラスにDelegateを作成。
class Hoge {
  HogeDelegate delegate;
  void run(){
    delegate.hoge();
  }
}
// Stateクラスに受け取れるように実装します。
// with HogeDelegate追加。
class _MyHomePageState extends State<MyHomePage> with HogeDelegate {
  // Hogeクラスを宣言。
  Hoge hogehoge;
  void hoge() {
    print("delegate hoge");
  }
  void hogeHoge1(String) {
    print("delegate hogeHoge1");
  }
  void hogeHoge2(String args1, String args2) {
    print("delegate hogeHoge2 args1$args1 $args2");
  }
  String hogehoge4(){
    return "hogehoge4を返却する。";
  }
  @override
  void initState() {
    super.initState();
   // initでクラスとdelegateを宣言しておく。
    hogehoge = Hoge();
    hogehoge.delegate = this;
  }
}
呼び出し
hogehoge.run();
まとめ
個人的には以下のように使い分けてみようかと思います。
| 概要 | iOSでは? | 使いみち | 
|---|---|---|
| VoidCallback | iOSではない | 明示的にパラメータ等を返さないと宣言したい時に利用する。 | 
| Function | Block/Closures | 単一のコールバックを返すに適している。複数のコールバックをFunctionで実装すると冗長になる可能性がある。 | 
| mixin | Delegate | TableViewDelegateのように、複数のコールバックを返却したいときに適している。 | 
その他のコールバックを見つけたら追記していきたいと思います。