旧サイトから記事の移行中です

子から親Widgetへの値の渡し方:onChanged・onPressed

Flutterの画面に配置するWidgetは、まとまりごとにカスタムWidgetとして別クラスを作成することが多いです。

その際、別クラスと、その別クラスを呼び出している親となるクラスとは、何らかのデータのやり取りが必要になります。

このクラス間のデータのやり取りの仕方を、チェックボックス(Checkbox)、ボタン(ElevatedButton)を使って解説します。

*テキストフィールド(TextField)の例を、追加執筆中です。

動作確認バージョン
  • Flutter : 3.24.4
  • Dart : 3.5.4
目次

Dartのコード概要とスマホの完成画面

このアプリでは、チェックボックス・ボタン・それとテキストフィールド2つを、子Widgetとして配置し、それぞれの子Widgetで入力した結果を、親であるHomeScreenが受け取って画面表示します。

Dartコードとスマホ画面との対応図
HomeScreen()の中で、4つの子Widgetを呼び出して表示

子Widget自身には、HomeScreenと値の受け渡しをするだけで、実行する内容は記述しません。

main.dartでは、引数なしでHomeScreen()を呼んでいます。

main.dartとhome_screen.dartのDartコード。 home_screen.dartから、4つの子Widgetを呼び出す。
HomeScreen()main()home:で指定。
このコードではまだ子Widgetのクラスが未作成なのでエラーになっています。

ここで作成したDartコードは、重要なコードが装飾用のWidgetに埋もれて分かりにくくならないよう、心がけて書きます。
なので、表示画面もコードもダサいです。ご了承ください…ww

コードを書く手順

STEP

親Widget(HomeScreen)に大枠を記述

まずは、親となるHomeScreen()を作ります。

子Widgetのクラスを先に作ってもいいけれど、Flutterは小さい部品から作るよりも、大枠を先に作る方が考えやすいです。

home_screen.dartに記述した4つの子Widget(クラスファイル)はまだ未作成なので、この段階ではエラー表示されている。
まずは、子Widgetとして作成するクラス名だけを書いておく
HomeScreen()から呼び出す4つの子Widgetのクラス
  1. CheckboxArea():チェックボックスを表示する
  2. ElevatedButtonArea():ElevatedButtonを表示する
  3. TextFieldArea01():テキストフィールドを表示する
  4. TextFieldArea02():テキストフィールドを表示する

この時点では、子Widgetとして呼び出すクラスファイルがないので、赤いエラーが出ています。

STEP

子Widgetの作成

Checkbox:「受け取る値がある」とき

呼び出し元の親クラスHomeScreen()の大枠ができたので、別のDartファイルに子WidgetとしてCheckboxを作ります。

まずは、いつも通りのクラスの作成です。

return文でCheckboxを入れて、必須プロパティのvalue:onChanged:を指定します。

このプロパティには、変数を指定します。

value: と onChanged: は、Checkboxの必須プロパティ

必須プロパティに指定した変数はfinalで宣言します。
その際の「型」は、Checkbox()の定義ファイルを見て調べます。

クラスの定義ファイルは、クラス名やプロパティ名の上で[F4]キーを押せば該当行へジャンプできます。
ジャンプ先で再度[F4]を押していくと、その先の定義箇所へどんどん深くジャンプしていきます。

この定義ファイルで確認して、valueboolonChangedにはValueChangedと明記しておきます。

CheckBoxのonChangedオプションに指定する値の型を調べるには、[F4]キーで定義箇所にジャンプする。
valueboolonChangedはbool値を扱うValueChangedということがわかる

次に、ここで作った変数をコンストラクタ内に記述します。
このコンストラクタの変数を中継して、CheckboxArea()HomeScreenがつながります。

CheckBoxArea()でFinal宣言した変数は、「オプション+Enter」して、コンストラクタに自動で入力するメニューから入力する。
コンストラクタが、HomeScreenCheckboxAreaをつなぐ役割をする

コンストラクタに値を中継する変数を入れたら、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という変数名にしました。

この変数もCheckboxonChanged:のときと同じように、finalで変数宣言します。

ElevatedButtononPressed:の型も、[F4]を押して調べて入れておきます。
VoidCallbackです。

ElevatedButtonのオプションに「onPressed」という変数を指定。 それをFinalで宣言。型はVoidCallback。 Finalで宣言したら、コンストラクタに入れる。
変数宣言したらコンストラクタに記述

コンストラクタの記述ができたら、ElevatedButtonArea()の完成です。

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()は、「ボタンが押された」ということを受け取るだけです。

ValueChangedVoidCallbackも、中身はFunctionですが、この「値を持つ」「値を持たない」という点が、CheckboxArea()ではValueChangedを使い、ElevatedButtonArea()ではVoidCallbackを使う、という違いです。

「ValueChanged」「VoidCallback」にカーソルを当て[F4]を押して、その型の定義にジャンプする。
ValueChangedは「引数ありのFunction()」
VoidCallbackは「引数なしのFunction()」
STEP

親Widget(HomeScreen)に値の受け渡し処理を記述

子WidgetのCheckboxArea()ElevatedButtonArea()が完成したので、HomeScreen()で値の受け渡しを書きます。

Checkbox

まずはimportで、checkbox_area.dartをインポートしておきます。

そして、CheckboxArea()のコンストラクタに作成したcheckedonChangedの設定です。

checked:には、ここでも変数を指定します。
truefalseを、そのまま書いてはいけません。

変数名は「画面に表示する値」という意味でviewCheckValueという名前にしました。

このviewCheckValueの型は、CheckboxArea()で指定したのと同じbool
初期値はfalseとしています。

onChangedには、Checkboxの値が変わったときの処理を書きます。

HomeScreen()でCheckBoxArea()を使う設定をする。 1.コンストラクタの引数を記述。 2.受け取る値を代入する変数をFinalで宣言 3.onChanged:で、受け取った値の代入処理をsetStateの中に書く。
onChangedには、処理内容を書く
    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()onPressedVoidCallbackなので、値の受け渡しがありません。
受け取る値がないので、setState( )の中にも変数は書きません。

ボタンが押されるたびにカウントアップして画面表示する必要があるので、カウントアップ計算用にint countを宣言して、Text()ウィジェットを追加しました。

HomeScreen()でElevatedButtonArea()を使う設定をする。 1.コンストラクタの引数を記述。 2.カウントアップの変数をFinalで宣言 3.onPressed:で変数のカウントアップをsetStateの中に書く。
onPressedで、変数をカウントアップして画面の再描画を実行
    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(),
    );
  }
}
よかったらシェアしてね!
  • URLをコピーしました!
目次