はじめに
何かを作るのにはやっぱり動的なページだなということで勉強したことのまとめです。
環境
Android Studio
Flutter 1.0
StatefulWidget
Flutterは動的なWidgetを作成するために状態を持つStatefulWidget
を使用することがベースとなります。(他のWidgetを使うこともあるようなので、初期の勉強としてはです)
ただしStatefulWidget
をそのまま大元の子供としてエラーとなってしまうようです。
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MyApp();
}
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return TestState();
}
}
I/flutter (11861): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (11861): The following assertion was thrown building MyApp(state: TestState#0a289):
I/flutter (11861): MediaQuery.of() called with a context that does not contain a MediaQuery.
I/flutter (11861): No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of().
I/flutter (11861): This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce
I/flutter (11861): a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
I/flutter (11861): The context used was:
I/flutter (11861): Scaffold(dirty, state: ScaffoldState#18d75(lifecycle state: initialized, tickers: tracking 1
I/flutter (11861): ticker))
正常に動作させるためにはいくつか方法があるようですが、最初はMaterialApp
を使って覚えていくのが一番楽そうです。
WidgetApp
もいけるようですが、builder
の概念がまだよく分かってないので、いったん置いておきます。
ということでMaterialAPP
を使って何かしら表示させることができたコードが以下となります。
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: MyApp());
}
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return TestState();
}
}
class TestState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: Center(
child: Text('Stateful Demo'),
),
);
}
}
State
StatefulWidget
を継承したクラスではcreateState
を実装しState<T>
を返却する必要があります。
State
自体はWidget
を返却するため、ここで状態で変化するWidget
を作成します。
今回はButton
を作って押したらその日時が表示されるような変化を起こす実装をしてみます。
State
の作成方法としては基本はWidget
を作成するため、そこは特に違いがありません。
違いとしては状態が変更されるというトリガーがあるのと、そのトリガーを持って状態を変化させる実装する点にあります。
状態変化時の動きについてはsetState
に変化時に動作するべき関数を渡すことで実現します。setState
は引数にvoid
が戻り値となるfunctionを渡す必要があるため、この内部で何かしらの副作用を与える形となります。
setState
外部で状態を変更して渡すという訳ではないので、その点は注意が必要です。
トリガーはButton
なので、Widget CatalogにあるBasicsにあったRaisedButton
を使用します。
各種ボタンには押された際に動作するonPressed
が用意されているため、ここに引数なしかつvoid
が戻り値となるfunctionを渡すことで押下時の動作を指定することができます。
今回はここにsetState
を実装したfunctionを渡すことでトリガーとなります。
setState
とonPressed
の引数が違うため、onPressed
に直接実装できないため、何かしらでラッピングさせる必要がある点に注意ください。
そうして出来たState
が以下のコードとなります。
これを実行して右下にあるボタンを押すと真ん中のテキストが現在日時で更新されていきます。
変数の文字列展開は$変数
とすればできるので、今回は変数に日時の文字列を入れてその文字列を展開することで実現しています。
class TestState extends State<MyApp> {
String _dateText = DateTime.now().toString();
void _updateDateText() {
setState(() {
_dateText = DateTime.now().toString();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Center(
child: Text('$_dateText'),
),
floatingActionButton: RaisedButton(
onPressed: _updateDateText,
child: Text("Button"),
),
);
}
}
動的ページを作るためのまとめ
StatefulWidget
とState
の組み合わせで動的ページを作成できることが分かったので、いったんここで動的なページを作るためのまとめを書きます。
ここでは上で作ったような最小限な構成とします。
Widget構成は以下画像のようになり、実装としてはMaterialApp
配下の組み立てとなることが多くなりそうです。
状態についてはStatefulWidget
を間にかませてState<T>
配下に設定したイベントを検知して変更するロジックを組むことになります。
状態変更については副作用を与えることが前提となるため(現状わかっている範囲では)、メンバ変数の変更で対応していくことになります。