はじめに
FlutterやReactにおいて、大きな関心ごとの一つに状態管理がある。
TextFieldはその性質上、自ら状態を持つ設計になりやすい。
しかし、TextFieldの入力情報をcallbackで返し、TextFieldの引数に現在値を与えることでTextFieldが状態を持たないようにできる。
ユースケースはProvider
などの状態管理ツールを入れているプロジェクトに限定されると思うが、私が欲しいと思ったので、今回作成した。
ついでに勉強がてら公開してみた。
メリット
- TextFiledの状態を自前で管理できるようになる(Provider, redux, Reverpod, BLoc, etc...)
- 他のwidgetの状態と連動させやすい
デメリット
- 自前で状態管理するコードを書く必要がある
- rebuildのタイミングを把握していないと、文字列入力中にwidgetがrebuildされ、文字が消えることがある
先行記事
Flutterにおける状態管理は様々な手法がある。こちらの記事が非常によくまとまっていると感じた。
この記事では状態管理方法については言及せず、外側で状態管理をしやすいTextFieldについて記述する。
こちらの記事と全く目的が同じである。
しかし、こちらの記事では状態管理を完全に外だししているわけではなく、Stateに状態を持たせてしまっているため、外からTextFieldに表示したい文字列を与えても変化しない課題がある。
WidgetとStateの関係についてはこちらの記事がとても参考になった。
使い方
こちらのリポジトリに公開している。
また、pub.devにも公開している。
exampleを見てもらうのが一番早いが、こちらにもコードを載せておく。
githubやpub.devが最新版となるため、そこはご了承を...
class _MyHomePageState extends State<MyHomePage> {
String _message = "";
void _setMessage(String str) =>
setState(() => _message = str);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StatelessTextField(
initialValue: _message,
style: Theme.of(context).textTheme.headline4,
decoration: InputDecoration(hintText: "first message field"),
onSubmitted: _setMessage,
),
StatelessTextField(
initialValue: _message,
style: Theme.of(context).textTheme.headline6,
decoration: InputDecoration(hintText: "second message field"),
onSubmitted: _setMessage,
)
],
),
),
);
}
}
簡単に説明すると、縦に並ぶ二つのStatelessTextField
のinitialVlaue
に_message
を与えて、onSubmitted
で_message
を更新してrebuildするexampleである。
このようにStatelessTextField
は外側で状態管理をしたいときに有効である。
実装(使うだけなら知らなくてもOK)
TextFieldに初期値を与えるためには、TextEditController
を保持しなければならないため、やむなくStatefulWidgetをextendsして作成した。
が、使用感はStatelessWidgetなので、stateless_textfield
と命名した。
exampleのような構成ではkeyを正しく与えないとWidgetとStateの組み合わせがずれてしまう場合があると思うが、Stateに状態を持たない構成なので、ずれてしまっても問題ない。
なんとなく気持ち悪いと思うが、悪影響はなさそうなので、このような実装にした。
TextEditControllerが曲者
TextFieldに初期値を与えるためにはTextEditController
を用意しなければならない。
また、これはちゃんとdispose()
を呼ばないとメモリリークする危険物であるため、そのハンドリングのためにStateで保持している。
逆に言えば、Stateで保持しているのはこれだけである。(たまにちゃんとdisposeしていないサンプルを見かけるが、大丈夫なのだろうか...)
最後に
Flutterの勉強を初めて半月くらいですが、WidgetとElementの関係や、公式の提供するWidgetの豊富さが面白く、「Flutter凄いー」となっています。
フロントエンドを例えに出すとReact + Material-UIといった感じですね。サクサク作れます。
Flutterの勉強がてら、自分用のflutter_templateを作っていたところ、状態を持たないTextFieldが欲しくなったので作りました。