23. FlutterでのCustomWidgetの作り方
今回はFlutterでCustomWidgetを作成する方法について学習します。
例えば、Text('文字列')
というウィジェットを継承してカスタムなTextを作成したい場合などに使う用法ですね。
このやり方を知る前はFlutterでもTextを継承するだけだと思ったのですが、これは違うみたいです。
具体例
サンプルとして、AppBarとbodyにはColumnウィジェットが乗った簡単なレイアウトを作成しました。
Columnの中にはTextとRaisedButtonが乗っかっています。
さらに今回はStatelessWidget
を使っています。
main.dart
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { int index = 0; void increment() { index = index + 1; print(index); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Customなウィジェットの作り方'), ), body: Center( child: Column( children: [ Text('カスタムにしたいウィジェットです。$index'), RaisedButton( child: Text('普通のボタン'), onPressed: increment ), ], ), ), ), ); } }
こちらをビルドすると次のような画面が表示されます。
これをもとにしてカスタムにしたいウィジェットです
と書かれたTextウィジェットをカスタムにします。
カスタムなウィジェットを作成する
まず、lib
に新しいファイルとしてcustom_text.dart
というファイル名で新規作成します。
新しいTextウィジェットを作成しますが、今回はStatelessWidget
を使うことにします。
作成したcustom_text.dart
に次のコードを書きます。
custom_text.dart
import 'package:flutter/material.dart'; class CustomText extends StatelessWidget { @override Widget build(BuildContext context) { return Container(); } }
しばらくはStatelessWidget
のテンプレートをこれにして学習します。
これでmain.dart
にimportするとCustomText
が使えるようになります。
次にmain.dart
のColumnに乗っているText
ウィジェットをCustomText
に移動させます。
custom_text.dart
import 'package:flutter/material.dart'; class CustomText extends StatelessWidget { @override Widget build(BuildContext context) { return Text('カスタムにしたいウィジェットです。$index'); } }
これでTextウィジェットを移動させることができました。
ただし、この状態ではCustomTextにはindex
の変数が存在しないのでビルドがエラーになって失敗してしまいます。
カスタムウィジェットのコンストラクタを作成
コンストラクタとは呼び出し元でインスタンスを生成するときに使うメソッドをイメージして頂くとだいたい意味が同じになります。
前項でindex
の変数が存在しないことによるエラーを解消させるためにindexをプロパティとして持たせる必要があります。
custom_text.dart
import 'package:flutter/material.dart'; class CustomText extends StatelessWidget { /// 1. indexのプロパティを宣言する final int index; @override Widget build(BuildContext context) { return Text('カスタムにしたいウィジェットです。$index'); } }
ですが、まだコンストラクタがないのでエラーのままです。
ここからはDartの文法の話しになりますのでDartを学習する必要がありますが、 簡単に解決法を述べると次のようなコンストラクタを書けば解消できます。
CustomText(this.index);
またDartには何種類か?の書き方が存在して
/// 1. クラス() CustomText(this.index); /// 2. クラス({}) CustomText({this.index}); /// 3. クラス({必須プロパティ}) CustomText({@required this.index});
のような書き方ができるそうです。
今回は特にこだわりはありませんので1を採用します。
custom_text.dart
import 'package:flutter/material.dart'; class CustomText extends StatelessWidget { /// 1. indexのプロパティを宣言する final int index; /// 2. CustomTextのコンストラクタを宣言する CustomText(this.index); @override Widget build(BuildContext context) { return Text('カスタムにしたいウィジェットです。$index'); } }
これでintのindex
を渡せるカスタムなTextを生成できるようになりました。
それでは、main.dartに戻ります。
Textウィジェットを消してCustomText
に書き直します。
main.dart
import 'package:flutter/material.dart'; import 'package:quiz_app/custom_text.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { int index = 0; void increment() { index = index + 1; print(index); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Customなウィジェットの作り方'), ), body: Center( child: Column( children: [ CustomText(index), RaisedButton( child: Text('普通のボタン'), onPressed: increment ), ], ), ), ), ); } }
これでアプリをビルドしてみましょう。 画面表示が前回と同じであれば成功です。
これでStatelessWidgetを用いたカスタムなウィジェットの作り方が分かりました。
必須プロパティにするとどうなるか
最後にコンストラクタのときに@required
のアノテーションがありましたね。
試しにcustom_text.dart
を次のように変更してみましょう。
custom_text.dart
import 'package:flutter/material.dart'; class CustomText extends StatelessWidget { /// 1. indexのプロパティを宣言する final int index; /// 2. indexを必須プロパティに変更する CustomText({@required this.index}); @override Widget build(BuildContext context) { return Text('カスタムにしたいウィジェットです。$index'); } }
するとmain.dart
がエラーになってしまいます。
main.dartでCustomText
を生成するところを次のように変更します。
CustomText(index: index)
main.dart全体ではこのようになります。
import 'package:flutter/material.dart'; import 'package:quiz_app/custom_text.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { int index = 0; void increment() { index = index + 1; print(index); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Customなウィジェットの作り方'), ), body: Center( child: Column( children: [ CustomText(index: index), RaisedButton( child: Text('普通のボタン'), onPressed: increment ), ], ), ), ), ); } }
ということで、@required
をつけることでインスタンス生成時にそのプロパティがSwiftでいう引数ラベル
のように
扱われることになります。
これが@required
の効果でした。
で、私はSwiftに慣れているので引数ラベルがあったほうが読みやすいのですが、
それは頑張ることにします。
ということでカスタムウィジェットの作成方法については以上になります。 Flutterではできる限りウィジェットは分割して扱うのが流儀らしいのでFatなウィジェットを作らないように心がけないといけませんね。