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()?
型との互換性がないために発生します。
元のコード例:
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
を使用することです:
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とは?
VoidCallback
はvoid Function()
の型エイリアスです。パラメータを受け取らず、何も返さない関数を表します。Flutterのボタン系Widgetはこの型を期待しています。
2. 明示的な関数型の指定
VoidCallback
の代わりに、明示的に関数型を指定することもできます:
final void Function()? onPressed; // 明示的な型指定
3. 関数の呼び出し方法の変更
関数を渡す際の構文も重要です:
// 正しい呼び出し方法
DrawerItem(
text: "ホーム",
onPressed: () => myFunction(), // 単一関数の場合
)
// または
DrawerItem(
text: "ホーム",
onPressed: () {
function1();
function2(); // 複数関数を実行する場合
},
)
避けるべき方法
型安全性が損なわれるため、以下の方法は推奨できません:
// 非推奨:型安全性がない
final dynamic onPressed;
// 非推奨:具体的な型情報がない
final Function? onPressed;
詳細な解説
null安全の影響
Dart 2.12で導入されたnull安全は、型システムをより嚴格にしました。従来のFunction
型は、任意の関数を表すことができましたが、null安全ではより具体的な関数型が必要になりました。
さまざまなWidgetでの適用例
このパターンはさまざまなFlutter Widgetで適用できます:
// Checkboxの場合
final void Function(bool?)? onChanged;
// カスタムコールバックの場合
final void Function(String)? onTextSubmitted;
// パラメータがある場合
final void Function(int index)? onItemSelected;
コードグループの例
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,
);
}
}
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のイベントコールバックを適切に処理できます。
この変更により、実行時エラーを防ぎ、コードの意図を明確にし、メンテナンス性を向上させることができます。