Skip to content

Dart/Flutter: Function型からvoid Function()?型への代入エラー

問題の説明

Flutterでドロワーアイテムを実装する際、以下のようなエラーに遭遇することがあります:

"The argument type 'Function' can't be assigned to the parameter type 'void Function()?'"

このエラーは、Dartのnull安全機能が導入された後に頻繁に発生するようになりました。具体的には、カスタムWidgetのonPressedプロパティをFunction型で定義している場合に、FlutterのボタンWidgetが期待するvoid Function()?型との互換性がないために発生します。

元のコード例:

dart
class DrawerItem extends StatelessWidget {
  final String text;
  final Function onPressed; // ここが問題

  const DrawerItem({Key key, this.text, this.onPressed}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      child: Text(text),
      onPressed: onPressed, // エラー発生箇所
    );
  }
}

解決策

1. VoidCallbackの使用(推奨)

最も一般的で型安全な解決策は、Functionの代わりにVoidCallbackを使用することです:

dart
class DrawerItem extends StatelessWidget {
  final String text;
  final VoidCallback? onPressed; // VoidCallbackに変更

  const DrawerItem({Key? key, required this.text, this.onPressed}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextButton(
      child: Text(text),
      onPressed: onPressed,
    );
  }
}

VoidCallbackとは?

VoidCallbackvoid Function()の型エイリアスです。パラメータを受け取らず、何も返さない関数を表します。Flutterのボタン系Widgetはこの型を期待しています。

2. 明示的な関数型の指定

VoidCallbackの代わりに、明示的に関数型を指定することもできます:

dart
final void Function()? onPressed; // 明示的な型指定

3. 関数の呼び出し方法の変更

関数を渡す際の構文も重要です:

dart
// 正しい呼び出し方法
DrawerItem(
  text: "ホーム",
  onPressed: () => myFunction(), // 単一関数の場合
)

// または
DrawerItem(
  text: "ホーム",
  onPressed: () {
    function1();
    function2(); // 複数関数を実行する場合
  },
)

避けるべき方法

型安全性が損なわれるため、以下の方法は推奨できません:

dart
// 非推奨:型安全性がない
final dynamic onPressed;

// 非推奨:具体的な型情報がない
final Function? onPressed;

詳細な解説

null安全の影響

Dart 2.12で導入されたnull安全は、型システムをより嚴格にしました。従来のFunction型は、任意の関数を表すことができましたが、null安全ではより具体的な関数型が必要になりました。

さまざまなWidgetでの適用例

このパターンはさまざまなFlutter Widgetで適用できます:

dart
// Checkboxの場合
final void Function(bool?)? onChanged;

// カスタムコールバックの場合
final void Function(String)? onTextSubmitted;

// パラメータがある場合
final void Function(int index)? onItemSelected;

コードグループの例

dart
class DrawerItem extends StatelessWidget {
  final String text;
  final VoidCallback? onPressed;
  
  const DrawerItem({
    Key? key,
    required this.text,
    this.onPressed,
  }) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(text),
      onTap: onPressed,
    );
  }
}
dart
Drawer(
  child: Column(
    children: [
      DrawerItem(
        text: "ホーム",
        onPressed: () => Navigator.pushNamed(context, '/home'),
      ),
      DrawerItem(
        text: "設定",
        onPressed: () => Navigator.pushNamed(context, '/settings'),
      ),
      DrawerItem(
        text: "ログアウト",
        onPressed: () {
          _logout();
          Navigator.pop(context);
        },
      ),
    ],
  ),
)

まとめ

Dartのnull安全導入後は、関数型の取り扱いがより厳密になりました。Functionの代わりにVoidCallbackまたはvoid Function()?を使用することで、型安全性を保ちながらFlutterのイベントコールバックを適切に処理できます。

この変更により、実行時エラーを防ぎ、コードの意図を明確にし、メンテナンス性を向上させることができます。