Contents
Flutter ウィジェット カスタム 作り方:実践的な手順と設計パターン
Flutterでカスタムウィジェットを作成する際には、StatelessWidgetとStatefulWidgetの使い分けやプロパティ管理が不可欠です。本記事では、初心者〜中級者向けに、コードサンプルを交えながらカスタムウィジェットの作成手順・設計パターン・実装例を体系的に解説します。「カスタムウィジェットの再利用性」や「パフォーマンス最適化」といった実務に即したポイントも含めて、プロジェクトで使える知識を伝授します。
カスタムウィジェット作成の基本概念
FlutterアプリケーションではUI構築において、StatelessWidgetとStatefulWidgetという2つの主要なウィジェットタイプが存在します。それぞれの用途やライフサイクルを理解することで、適切な設計が可能になります。
StatelessWidgetとStatefulWidgetの使い分け
StatelessWidgetは状態を持たない静的なUIコンポーネントに適しており、例えばボタンやアイコンなどの再利用可能な部品としてよく使われます。一方で、StatefulWidgetはユーザーの操作やAPI通信などで変化するUIを扱うため、テキストフィールドやアニメーションなどが必要な場面で使用します。
| タイプ | 状態管理 | 代表的な用途 |
|---|---|---|
| StatelessWidget | なし | ボタン・アイコン |
| StatefulWidget | あり(setState) |
テキストフィールド・アニメーション |
ポイント:UIが変化しない場合は必ずStatelessWidgetを採用しましょう。これによりパフォーマンスの負荷を抑えることができます。
カスタムウィジェットのライフサイクル概要
カスタムウィジェットを実装する際には、特にStatefulWidgetにおいてinitState()やdispose()などのライフサイクルメソッドを理解することが重要です。
initState(): ワイジェットが初めて作成されたときに呼び出され、初期化処理(API通信など)を行います。dispose(): ワイジェットが破棄される直前に呼び出され、リソースの解放(例:タイマー終了、テキストコントローラーのdispose()実行)を行う必要があります。
注意点:不要な
setState()の呼び出しはパフォーマンスに悪影響を与えるため、ライフサイクルを意識した設計が必要です。
カスタムウィジェットの構築手順
カスタムウィジェットを作成するには、まずクラス定義から始めます。このセクションではStatelessWidgetとStatefulWidgetそれぞれの基本構造について解説します。
クラス定義とbuildメソッドの実装
StatelessWidgetの場合、以下のようなシンプルな構造になります:
|
1 2 3 4 5 6 7 8 9 10 |
class MyCustomButton extends StatelessWidget { @override Widget build(BuildContext context) { return ElevatedButton( onPressed: () {}, child: Text('クリック'), ); } } |
一方で、StatefulWidgetでは_MyCustomButtonStateという内部クラスを定義する必要があります:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class MyCustomTextField extends StatefulWidget { @override _MyCustomTextFieldState createState() => _MyCustomTextFieldState(); } class _MyCustomTextFieldState extends State<MyCustomTextField> { final TextEditingController _controller = TextEditingController(); @override void initState() { super.initState(); _controller.text = '初期値'; } @override void dispose() { _controller.dispose(); // リソース解放処理を明示的に記載 super.dispose(); } @override Widget build(BuildContext context) { return TextField( controller: _controller, ); } } |
プロパティ型の定義とデフォルト値設定
カスタムウィジェットにプロパティを渡す際には、コンストラクタで型を指定し、デフォルト値を設定します。以下はテキストフィールドの例です:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class CustomInput extends StatelessWidget { final String hintText; final TextEditingController controller; const CustomInput({ Key? key, this.hintText = '入力してください', required this.controller, // `required`により必須パラメータを明示 }) : super(key: key); @override Widget build(BuildContext context) { return TextField( controller: controller, decoration: InputDecoration(hintText: hintText), ); } } |
ポイント:
requiredキーワードは、必須のプロパティを明示的に指定するためのもので、コンストラクタ呼び出し時にエラーを発生させる役割があります。
複合的なUI設計技法
複数のウィジェットを組み合わせてUIを作成するには、ColumnやRowなどのレイアウトコンポーネントを活用します。ここでは具体的な実装例を通して紹介します。
レイアウト構成要素の選定
- Column: 上下に並べるレイアウト
- Row: 左右に並べるレイアウト
- Stack: 重ね合わせるレイアウト
以下は、ボタンとテキストフィールドを縦に並べた例です:
|
1 2 3 4 5 6 7 |
Column( children: [ CustomInput(controller: TextEditingController()), ElevatedButton(onPressed: () {}, child: Text('送信')), ], ) |
再利用可能なウィジェットの作成例
再利用性を高めるためには、汎用的なUIコンポーネントを作成することが重要です。例えば、ボタンや入力フィールドをカスタム化して繰り返し使用できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class CustomButton extends StatelessWidget { final String text; final Color backgroundColor; final Color textColor; final VoidCallback onPressed; const CustomButton({ Key? key, required this.text, // `required`で必須パラメータを明示 required this.backgroundColor, required this.textColor, required this.onPressed, }) : super(key: key); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, style: ButtonStyle( backgroundColor: MaterialStateProperty.all(backgroundColor), ), child: Text( text, style: TextStyle(color: textColor), ), ); } } |
このCustomButtonは、別の画面でも再利用可能です。
設計パターンとベストプラクティス
カスタムウィジェットを設計する際には、コンポジション(合成)重視の設計原則やパフォーマンス最適化手法が重要です。それらについて詳しく見ていきます。
コンポジション重視の設計原則
Flutterでは「コンポジション」(既存のウィジェットを組み合わせて新しいUIを作成する)が推奨されています。これは、再利用性の向上と可読性の改善に直結します。
- ボタンやテキストフィールドなどを個別にカスタム化し、必要に応じて組み合わせるように設計しましょう。
- 他の開発者にとっても理解しやすい構造になります。
パフォーマンス最適化手法
- constキーワードの使用:
constをつけることで、コンストラクタが一度だけ評価され、再利用時に効率的に処理されます。 StatelessWidgetでは常にconstを推奨し、状態変化がない場合に有効です。StatefulWidgetではconstは使用不可(状態を持つため)。
dart
const CustomTextWidget(text: '固定テキスト'); // StatelessWidgetでのconst利用
- StatefulWidgetの最小化:不要な状態管理は避け、できるだけ
StatelessWidgetを使用してください。
実装例とコードサンプル
ここでは具体的に、カスタムボタンや入力フィールドを作成する手順をステップバイステップで解説します。
カスタムボタンの実装
以下のコードでは、文字色・背景色をカスタマイズ可能なCustomButtonを作成しています:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class CustomButton extends StatelessWidget { final String text; final Color backgroundColor; final Color textColor; final VoidCallback onPressed; const CustomButton({ Key? key, required this.text, // `required`で必須パラメータを明示 required this.backgroundColor, required this.textColor, required this.onPressed, }) : super(key: key); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, style: ButtonStyle( backgroundColor: MaterialStateProperty.all(backgroundColor), ), child: Text( text, style: TextStyle(color: textColor), ), ); } } |
使用例:
|
1 2 3 4 5 6 7 |
CustomButton( text: '登録', backgroundColor: Colors.blue, textColor: Colors.white, onPressed: () {}, ); |
プロジェクトへの適用とまとめ
カスタムウィジェットはプロジェクトに適用することで、再利用性や保守性を劇的に向上させます。以下では、作成したカスタムウィジェットを実際に使う際の手順と今後の拡張性について述べます。
サンプルコードの再利用性検証
作成したCustomButtonやCustomInputは、プロジェクト内の他の画面でも簡単に使えるように設計されています。以下は、別の画面で使用する例です:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('再利用ページ')), body: Center( child: Column( children: [ CustomInput(controller: TextEditingController()), CustomButton( text: '確認', backgroundColor: Colors.green, textColor: Colors.white, onPressed: () {}, ), ], ), ), ); } } |
今後の拡張性について
- カスタムウィジェットの拡張性:プロパティを追加することで、UIを柔軟に変更可能です。
- 状態管理の工夫:複雑なアプリケーションでは
ProviderやRiverpodなどを使うことで、状態管理の手軽さが増します。
まとめ
- StatelessWidgetとStatefulWidgetの使い分けでパフォーマンスを最適化
- プロパティの型指定とデフォルト値設定でUIの柔軟性を高める
- コンポジション重視の設計で再利用性を向上
- constキーワードやStatefulWidgetの最小化で性能改善
実際にプロジェクトに適用してみてください。コードサンプルを参考にカスタムウィジェットを作成し、アプリの再利用性を高めてください。