Flutterの画面に配置するWidgetは、まとまりごとにカスタムWidgetとして別クラスを作成することが多いです。
その際、別クラスと、その別クラスを呼び出している親となるクラスとは、何らかのデータのやり取りが必要になります。
このクラス間のデータのやり取りの仕方を、チェックボックス(Checkbox)、ボタン(ElevatedButton)を使って解説します。
*テキストフィールド(TextField)の例を、追加執筆中です。
- Flutter : 3.24.4
- Dart : 3.5.4
Dartのコード概要とスマホの完成画面
このアプリでは、チェックボックス・ボタン・それとテキストフィールド2つを、子Widgetとして配置し、それぞれの子Widgetで入力した結果を、親であるHomeScreen
が受け取って画面表示します。
子Widget自身には、HomeScreen
と値の受け渡しをするだけで、実行する内容は記述しません。
main.dartでは、引数なしでHomeScreen()を呼んでいます。
コードを書く手順
親Widget(HomeScreen)に大枠を記述
まずは、親となるHomeScreen()を作ります。
子Widgetのクラスを先に作ってもいいけれど、Flutterは小さい部品から作るよりも、大枠を先に作る方が考えやすいです。
CheckboxArea()
:チェックボックスを表示するElevatedButtonArea()
:ElevatedButtonを表示するTextFieldArea01()
:テキストフィールドを表示するTextFieldArea02()
:テキストフィールドを表示する
この時点では、子Widgetとして呼び出すクラスファイルがないので、赤いエラーが出ています。
子Widgetの作成
Checkbox:「受け取る値がある」とき
呼び出し元の親クラスHomeScreen()
の大枠ができたので、別のDartファイルに子WidgetとしてCheckbox
を作ります。
まずは、いつも通りのクラスの作成です。
return
文でCheckbox
を入れて、必須プロパティのvalue:
とonChanged:
を指定します。
このプロパティには、変数を指定します。
必須プロパティに指定した変数はfinal
で宣言します。
その際の「型」は、Checkbox()
の定義ファイルを見て調べます。
クラスの定義ファイルは、クラス名やプロパティ名の上で[F4]キーを押せば該当行へジャンプできます。
ジャンプ先で再度[F4]を押していくと、その先の定義箇所へどんどん深くジャンプしていきます。
この定義ファイルで確認して、value
はbool
、onChanged
にはValueChanged
と明記しておきます。
次に、ここで作った変数をコンストラクタ内に記述します。
このコンストラクタの変数を中継して、CheckboxArea()
とHomeScreen
がつながります。
コンストラクタに値を中継する変数を入れたら、CheckboxArea()
クラスが完成です。
import 'package:flutter/material.dart';
class CheckboxArea extends StatelessWidget {
const CheckboxArea({super.key, required this.checked, required this.onChanged});
final bool checked;
final ValueChanged onChanged;
@override
Widget build(BuildContext context) {
return Checkbox(
value: checked,
onChanged: onChanged,
);
}
}
ElevatedButton:受け取る「値がない」とき
次に、ElevatedButtonArea()
を作ります。
ElevatedButton
の必須プロパティは、child:
とonPressed:
です。
child:
は、ボタンに表示する文字です。
onPressed:
には、このボタンが押されたときの処理を変数として指定します。
ここではonPressed
という変数名にしました。
この変数もCheckbox
のonChanged:
のときと同じように、final
で変数宣言します。
ElevatedButton
のonPressed:
の型も、[F4]を押して調べて入れておきます。VoidCallback
です。
import 'package:flutter/material.dart';
class ElevatedButtonArea extends StatelessWidget {
const ElevatedButtonArea({super.key, required this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: const Text('ElevatedButton'),
);
}
}
ElevatedButton
の場合は、Checkbox
のようにon/off(true
/false
)などの値を持ちません。
親WidgetのHomeScreen()
は、「ボタンが押された」ということを受け取るだけです。
ValueChanged
もVoidCallback
も、中身はFunction
ですが、この「値を持つ」「値を持たない」という点が、CheckboxArea()
ではValueChanged
を使い、ElevatedButtonArea()
ではVoidCallback
を使う、という違いです。
親Widget(HomeScreen)に値の受け渡し処理を記述
子WidgetのCheckboxArea()
とElevatedButtonArea()
が完成したので、HomeScreen()
で値の受け渡しを書きます。
Checkbox
まずはimport
で、checkbox_area.dart
をインポートしておきます。
そして、CheckboxArea()
のコンストラクタに作成したchecked
とonChanged
の設定です。
checked:
には、ここでも変数を指定します。true
/false
を、そのまま書いてはいけません。
変数名は「画面に表示する値」という意味でviewCheckValue
という名前にしました。
このviewCheckValue
の型は、CheckboxArea()
で指定したのと同じbool
。
初期値はfalse
としています。
onChanged
には、Checkbox
の値が変わったときの処理を書きます。
Text(' Checkbox : $viewCheckValue'),
CheckboxArea(
checked: viewCheckValue,
// ↓value: CheckboxArea()から返された値
onChanged: (value) {
// viewCheckValueに戻り値を代入して再描画
setState(() {
viewCheckValue = value;
});
},
),
ここでのonChanged
の役割は、CheckboxArea()
で値が変わったら、その値に応じて画面表示を更新することです。
画面の再描画はsetState
を実行すればいいので、CheckboxArea()
から受け取った値をviewCheckValue
に代入する操作をsetState
の中に書きます。
Checkbox
に変更があったらsetState
が実行され、checked: viewCheckValue,
の値でCheckboxArea()
が再度呼び出されて再描画されるというわけです。
これで、CheckboxArea()
から値を受け取ることができるようになりました。
ElevatedButton
次はElevatedButtonArea()
です。
まずは、elevated_button_area.dart
をインポートします。
そして、ElevatedButtonArea()
のコンストラクタに作成したonPressed
に、ElevatedButton
が押されときの処理を記述します。
処理内容は、「画面表示の数字をカウントアップして再描画(setState)する」です。
ElevatedButtonArea()
のonPressed
はVoidCallback
なので、値の受け渡しがありません。
受け取る値がないので、setState
の( )
の中にも変数は書きません。
ボタンが押されるたびにカウントアップして画面表示する必要があるので、カウントアップ計算用にint count
を宣言して、Text()
ウィジェットを追加しました。
Text(count.toString()),
ElevatedButtonArea(
onPressed: () {
setState(() {
count++;
});
},
),
これで、ElevatedButtonArea()
とHomeScreen()
がつながりました。
完成Dartファイル
下記が、作成したDartファイルです。
前述のWidgetだけではスマホ画面が見にくいので、文字体裁など少し装飾する設定を追加してあります。
未解説のテキストフィールド部分も入っています。
子クラスは、前述の項目「checkbox_area.dart」「elevated_button_area.dart」を見てください。
HomeScreen()
import 'package:flutter/material.dart';
import 'components/checkbox_area.dart';
import 'components/elevated_button_area.dart';
import 'components/text_field_area01.dart';
import 'components/text_field_area02.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<HomeScreen> {
bool viewCheckValue = false;
int count = 0;
String viewText01 = '入力前';
String viewText02 = '入力前';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 56.0),
Text(' Checkbox : $viewCheckValue',
style: const TextStyle(fontSize: 16.0)),
CheckboxArea(
checked: viewCheckValue,
onChanged: (value) {
setState(() {
viewCheckValue = value;
});
},
),
//
const SizedBox(height: 16.0),
Text(' ElevatedButton: $count',
style: const TextStyle(fontSize: 16.0)),
ElevatedButtonArea(
onPressed: () {
setState(() {
count++;
});
},
),
//
const SizedBox(height: 32.0),
Text(
' TextField 01 : $viewText01',
style: const TextStyle(fontSize: 18.0),
),
const Text(' (子Widgetで「メソッド渡し」で記述)'),
TextFieldArea01(
onSubmitted: (inputtedText) {
setState(() {
viewText01 = inputtedText;
});
},
),
const SizedBox(height: 16.0),
Text(
' TextField 02 : $viewText02',
style: const TextStyle(fontSize: 18.0),
),
const Text(' (子Widgetで、引数付きで記述)'),
TextFieldArea02(
onSubmitted: (inputtedText) => updateText02(inputtedText),
),
],
),
);
}
// TextFieldArea02()で実行する関数
updateText02(String inputtedText) {
setState(() {
viewText02 = inputtedText;
});
}
}
main()
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'ValueChanged Try',
home: HomeScreen(),
);
}
}