0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初心者の壁⑮ Flutter(Dart)のStateとクラスについて学ぶ その3

0
Last updated at Posted at 2026-06-13

さて、FlutterのStateとクラスについての学習記録
その3です。

今回はついにStateを導入したいと思います。

しかし、
前回までの記事で、なんとなく
Stateの役割は分かったのですが

本当に、Stateがないとダメなの?

って、まだちょっと疑っています。

そんなわけで、まずは
Stateがないとどうなるのか
実験してみることにしました。

Step1 Stateなしの場合

実験台にするWidgetはTextFieldです。

まずは、簡単なtextFieldDefinitionを用意しました。

※◯◯Definitionというのは
私が開発中のアプリWidget Labで
各Widgetごとに作成しているファイルです。
Touchモードで触れるパラメータ
リアルタイムプレビューに表示されるWidget
などについての設定が書かれています。

コードはこちら

final textFieldDefinition = WidgetDefinition(
  id: 'textField',
  title: 'TextField',
  category: 'Input',
  parentId: 'StatefulWidget',
  description: 'ユーザーがテキストを入力できるWidget',

  params: [],

  touchParams: [
    TouchParam(
      key: 'text',
      uiType: TouchUiType.text,
      label: 'Text',
      initialValue: '',
    ),

    TouchParam(
      key: 'hintText',
      uiType: TouchUiType.text,
      label: 'Hint Text',
      initialValue: '',
    ),

    TouchParam(
      key: 'width',
      uiType: TouchUiType.slider,
      label: 'Width',
      initialValue: 240.0,
      min: 100,
      max: 400,
    ),

    TouchParam(
      key: 'enabled',
      uiType: TouchUiType.switchButton,
      label: 'Enabled',
      initialValue: true,
    ),

    TouchParam(
      key: 'readOnly',
      uiType: TouchUiType.switchButton,
      label: 'Read Only',
      initialValue: false,
    ),
  ],

  previewBuilder: (values) {
    final width =
        (values['width'] ?? 240.0).toDouble();

    final text =
        values['text'] ?? '';

    final hintText =
        values['hintText'] ?? '';

    final enabled =
        values['enabled'] ?? true;

    final readOnly =
        values['readOnly'] ?? false;

    return SizedBox(
      width: width,
      child: TextField(
        controller: TextEditingController(
          text: text,
        ),
        decoration: InputDecoration(
          hintText: hintText,
        ),
        enabled: enabled,
        readOnly: readOnly,
      ),
    );
  },

  miniPreviewBuilder: () {
    return const SizedBox(
      width: 120,
      child: TextField(
        decoration: InputDecoration(
          hintText: 'TextField',
        ),
      ),
    );
  },

  codeBuilder: (values) {
    final hintText =
        values['hintText'] ?? '';

    final enabled =
        values['enabled'] ?? true;

    final readOnly =
        values['readOnly'] ?? false;

    return '''
TextField(
  decoration: InputDecoration(
    hintText: '$hintText',
  ),
  enabled: $enabled,
  readOnly: $readOnly,
)
''';
  },
);

previewBuilderという関数が
プレビューに表示されるWidgetを返します。
ユーザーが調整した設定値を
valuesで渡しています。

今まで作成したStatelessWidgetのページは
この形式で作っていて
何も問題はありませんでした。

さて、StatefulWidgetであるTextFieldでは
どんな問題が起こるのでしょうか?

実行結果がこちらです。

テキストボックス内に文字を入力して
パラメータをいじると
入力した文字が消えてしまいます。

なるほど、ChatGPTの言っていたとおりです。

これでは

長い文字を入力した時の挙動を試してみよう

なんて使い方ができません。

Stateを導入する方法

さて、Stateの必要性は理解できました。
どうやったらStateを導入できるのでしょうか?

ChatGPTに、必要な作業をチェックボックス形式で出してもらうと
こうなりました。

PreviewStateを作る

  • PreviewStateクラスを新規作成
  • TextEditingControllerを持たせる

TouchPageを修正する

  • PreviewStateを保持する変数を追加
  • initState()PreviewStateを生成する

WidgetDefinitionを修正する

  • previewBuilderの引数を変更する

    • previewBuilder(values)
    • previewBuilder(values, state)

呼び出し側を修正する

  • previewBuilder(values)を呼んでいる箇所をすべて修正

これを見て、私は思いました。

TextFieldを1つ追加したいだけなのに、結構大工事だな……。

特に最後のところ。
今までに作ったWidgetのページはできればいじりたくない。
もっと楽な方法ないのかな?

そこで思いついたのが、
StateをValuesの中に入れてしまう
という方法でした。

ChatGPTに聞いたところ、
それでも一応動くよ、とのことなので
試してみることにしました。

Step2 StateをVvaluesに入れた場合

こんな感じになりました。
意外とスッキリしているし、
変更箇所もここだけで済みそうです。

コードはこちら
previewBuilder: (values) {
  final width =
      (values['width'] ?? 240.0).toDouble();

  final hintText =
      values['hintText'] ?? '';

  final enabled =
      values['enabled'] ?? true;

  final readOnly =
      values['readOnly'] ?? false;

  // ----------------------------
  // StateをValuesの中に保存
  // ----------------------------
  values['_state'] ??= {
    'controller': TextEditingController(
      text: values['text'] ?? '',
    ),
  };

  final state =
      values['_state'] as Map<String, dynamic>;

  final controller =
      state['controller']
          as TextEditingController;

  return SizedBox(
    width: width,
    child: TextField(
      controller: controller,
      decoration: InputDecoration(
        hintText: hintText,
      ),
      enabled: enabled,
      readOnly: readOnly,
    ),
  );
},

そして実行結果がこちら。

State→Widgetへの情報伝達や更新が
リアルタイムにできないのでは?と心配でしたが
何も問題ありませんでした。

どうやらこの方法でも
Stateは機能するみたいです。

さらに、入力後に
戻るボタンで別のページに戻ってから
再度このページを表示したら
ちゃんと最初の状態に戻っていました。

一度入力した値がずっと残ってしまうのでは?
と心配でしたが、それも問題ないようです。

Step3 Stateをしっかり実装する

さて、Step2の方法でも
用が足りたのですが

ChatGPT的には
Stateとvaluesは別々に渡すのが
断然オススメみたいです。

今後、コードプレビューを実装する場合に
Step2の方法だと

values.remove('_state');

のような除外処理を毎回書く必要があるかも…
みたいなことを言われて

これってつまり、
今、楽をするか
これから楽をするか

の違いなのだろうな、と思いました。

せっかくなので、勉強のためにも
ちゃんとした実装をしてみることにしました。

手順は上で示したとおりです。
新しく作ったのは、このファイルぐらい。

class PreviewState {
  TextEditingController? textController;
}

そして、previewBuilderはこんな感じになりました。

コードはこちら
previewBuilder: (
      values,
      previewState,
      ) {
    final width =
    (values['width'] ?? 240.0).toDouble();

    final hintText =
        values['hintText'] ?? '';

    final enabled =
        values['enabled'] ?? true;

    final readOnly =
        values['readOnly'] ?? false;

    previewState.textController ??=
        TextEditingController(
          text: values['text'] ?? '',
        );

    final controller =
    previewState.textController!;

    return SizedBox(
      width: width,
      child: TextField(
        controller: controller,
        decoration: InputDecoration(
          hintText: hintText,
        ),
        enabled: enabled,
        readOnly: readOnly,
      ),
    );
  },

意外とすんなり作業は完了しました。

そして、実行結果はStep2と全く同じでした。

まとめ

正直、Stateを扱うコードそのものについては
まだ充分に理解していません。

それでも、以前はわけがわからなくて
途方もない作業に感じた、Stateの導入が
無事達成できたことは非常にうれしかったです。

これからは、Widget Labに
StatefulWidgetのページを
どんどん追加していきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?