[Flutter入門(5)] 公式チュートリアルのステップ2:Adding interactivity(略)をやってみる
Flutterの公式サイトに チュートリアル として紹介されているコンテンツを順にやってみようと思います。
今回は2つ目の Adding interactivity to your Flutter app です。
前回作成したアプリは現在下図のような見た目になっています。
今回はこの画面の赤い★アイコンをタップできるようにしていきます。下図のように、タップするたびに「お気に入り追加済み」と「お気に入り未追加」の状態がトグルするような動作を実現します。
StatefulウィジェットとStatelessウィジェット
実際にコードを触っていく前に、StatefulウィジェットとStatelessウィジェットについておさらいしておきましょう。
ウィジェットには2種類あり、それが Stateful
と Stateless
です。
Statefulウィジェットは状態を持つウィジェットで、ユーザーの入力などに応じて何かしら状態が変化することがあるなら、それはStatefulウィジェットです。
Statelessウィジェットは状態を持ちません。つまり、生成されてから破棄されるまでの間、保有している値が一切変化しません。 Icon
IconButton
Text
などがStatelessウィジェットの一例です。Statelessウィジェットは、 StatelessWidget
クラスのサブクラスとして実装されています。
Statefulウィジェットの例としては、 Checkbox
Radio
Slider
InkWell
Form
TextField
など があります。Statefulウィジェットは StatefulWidget
クラスのサブクラスとして実装されています。
ウィジェットの状態は、 State
オブジェクトによって保持されます。ウィジェットの表現と状態は別々のクラスによって管理されているということです。
State
オブジェクトは、状態を表す何かしら可変な値を持っています。例えばスライダーにおける「現在の値」や、チェックボックスにおける「チェックされているかどうか」などの情報がそれに当たります。
ウィジェットの状態が変更されると、 State
オブジェクトは setState()
というメソッドを呼び出すことによって、フレームワークにウィジェットの再描画を依頼します。
Step 1:ウィジェットの状態をどう管理するかを決める
実は、ウィジェットの状態を管理する方法には選択肢がいくつかあります。
今回のサンプルアプリでは FavoriteWidget
というウィジェットを作って、自分自身に状態の管理をさせることにします。
ウィジェットと状態の分離についてより詳細に学びたい場合は、公式サイトの Managing state というコンテンツを参照するとよいでしょう。(本ブログでの解説記事は こちら)
StatefulWidget
を継承したウィジェットを作る
Step 2: まずは、状態を持つStatefulウィジェットのクラスを作ります。 lib/main.dart
の末尾に以下のコードを書き足しましょう。
class FavoriteWidget extends StatefulWidget {
_FavoriteWidgetState createState() => _FavoriteWidgetState();
}
FavoriteWidget
クラスは自分の状態を自分で管理します。そのために、 State
オブジェクトを生成する createState()
メソッドをオーバーライドしています。
createState()
メソッドは、ウィジェットがビルドされるタイミングでフレームワークによって実行されます。この例では、 createState()
メソッドは _FavoriteWidgetState
クラスのインスタンスを返しています。これは次のステップで実装しましょう。
State
を継承した状態クラスを作る
Step 3: FavoriteWidget
ウィジェットの状態を保有するのが _FavoriteWidgetState
クラスです。
今回は、アプリが起動したときには
- 自分はすでにお気に入りに追加している状態(というイメージ)
- 表示内容としては、赤色で塗り潰された★アイコンと、
41
という数字(お気に入りに追加されている数を表しているイメージ)
という状態にしたいので、 lib/main.dart
の末尾にひとまず以下のようなクラス定義を追記しましょう。
class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 41;
}
さらに、このクラスに build()
メソッドを実装します。このメソッドで FavoriteWidget
の実際の中身を構築します。
class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 41;
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ padding: EdgeInsets.all(0),
+ child: IconButton(
+ icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)),
+ color: Colors.red[500],
+ onPressed: _toggleFavorite,
+ ),
+ ),
+ SizedBox(
+ width: 18,
+ child: Container(
+ child: Text('$_favoriteCount'),
+ ),
+ ),
+ ],
+ );
+ }
}
Icon
ではなく IconButton
を使っている点に注意してください。 IconButton
は onPressed
というプロパティを持っていて、タップされたときに実行したい処理をコールバックとして渡すことができます。
ここでは _toggleFavorite
というメソッドを渡しています。では、次にこのメソッドを実装しましょう。
class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 41;
+ void _toggleFavorite() {
+ setState(() {
+ if (_isFavorited) {
+ _favoriteCount -= 1;
+ _isFavorited = false;
+ } else {
+ _favoriteCount += 1;
+ _isFavorited = true;
+ }
+ });
+ }
+
@override
Widget build(BuildContext context) {
// ...
_toggleFavorite()
メソッドは、 setState()
メソッドを呼び出しています。
setState()
を呼び出すという行為には特別な意味があります。先述したとおり、これによってフレームワークにウィジェットの状態が変わったことを通知し、ウィジェットの再描画を依頼しているのです。
setState()
に引数として渡しているクロージャーの中で、 _isFavorited
および _favoriteCount
という変数の値を更新しています。
こうしてウィジェットが再描画されると、Stateクラスの build()
メソッドが実行され、
icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)),
この部分の条件分岐によってウィジェットの見た目が変化するというわけです。
Step 4:作ったStatefulウィジェットを画面にはめ込む
あとは作ったウィジェットを実際に画面にはめ込むだけです。
titleSection
を組み立てているコードの以下の箇所を差し替えましょう。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget titleSection = Container(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
'Oeschinen Lake Campground',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Text(
'Kandersteg, Switzerland',
style: TextStyle(
color: Colors.grey[500],
),
),
],
),
),
- Icon(
- Icons.star,
- color: Colors.red[500],
- ),
- Text('41'),
+ FavoriteWidget(),
],
),
);
// ...
これで、以下のようにタップすると状態が切り替わるようなUIが実装できました👍
Discussion