Flutter公式ドキュメントで勉強した内容を要約したものです。

ジェスチャー・ハンドリング(Handling gestures)

多くのアプリはユーザーとのインタラクションが含まれる。まずは簡単にボタンをタップするアクションを検知できるようにしてみよう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: MyButton(),
        ),
      ),
    ),
  );
}

class MyButton extends StatelessWidget {
  const MyButton({super.key});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        print('MyButtonがタップされました!');
      },
      child: Container(
        height: 50,
        padding: const EdgeInsets.all(8),
        margin: const EdgeInsets.symmetric(horizontal: 8),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(5),
          color: Colors.lightGreen[500],
        ),
        child: const Center(
          child: Text('Engage'),
        ),
      ),
    );
  }
}

iOS Simulator

ボタンウィジェットではないが、ボタンぽいものをタップするとデバッグコンソルに文字列が出力される。

iOS Simulator

GestureDetectorウィジェットは視覚的に表示するのはないが、ユーザーの入力(ジェスチャー)を検出することができる。

ユーザーがタップするとonTapに指定された関数がコールバックされ、メッセージを表示している。

StatefulWidget

今までは静的ウィジェットを学んできたが、ユーザーの入力が検知できるようになったので入力によって画面の表示も変えてみたい。

こんなときに使うのがStatefulWidgetなのだ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Counter(),
        ),
      ),
    ),
  );
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Increment'),
        ),
        const SizedBox(width: 16),
        Text('Count: $_counter'),
      ],
    );
  }
}

iOS Simulator

StatelessWidgetとは少し構造が変わっていることがわかる。StatefulWidgetはStatefulWidgetを相続したクラスとステートを管理するクラスの2つで構成される。

setStateメソッドに気づいているかもしれないが、これがStatufulWidgetで状態が変わったとお知らせするものである。このお知らせを受けて画面の表示が更新される。試しにsetStateを消して_count++のみにし、printしてみるとわかるが、内部的には数字が増加しているが、画面には反映されない。

次のコードを見てみよう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import 'package:flutter/material.dart';

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Counter(),
        ),
      ),
    ),
  );
}

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        CounterIncrementor(onPressed: _increment),
        const SizedBox(width: 16),
        CounterDisplay(count: _counter),
      ],
    );
  }
}

class CounterDisplay extends StatelessWidget {
  const CounterDisplay({required this.count, super.key});

  final int count;

  @override
  Widget build(BuildContext context) {
    return Text('Count: $count');
  }
}

class CounterIncrementor extends StatelessWidget {
  const CounterIncrementor({required this.onPressed, super.key});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: const Text('Increment'),
    );
  }
}

上のコードは先ほどのカウンターと同じ結果であるが、ボタンと画面表示を子ウィジェットにしている。

Widget Inspector

VSCodeでデバッグモードで実行している場合、上のようなWidget Inspectorが表示されるので確認してほしい。

Widget Treeが表示されているので今の構造が思う通りに構成されているかどうか確認ができる。今回のコードはStatefulWidgetのCounterがあり、その子ウィジェットとしてCounterIncrementとCounterDisplayの二つのStatelessWidgetに分けている。

Tapを検知するのは子のCounterIncrementorでタップされると親の_incrementメソッドをコールバックする。そうすると数字が増加し、setSteteによってステートが変わったのが通知されるのでそのステートは子のCounterDisplayに伝達される。

このようにステート(状態)はウィジェット構造ないでStatelessWidgetには上から下へ、StatefulWidgetは下から上に流れる。

コメントする