FlutterでUIを扱う時の基本的な概念
3Dゲームのようなアプリの場合には、どのくらいのFPS(Frame per second)を出せるハードかということが重要になります。これは、どれだけ画面がスムーズに動くかを気にするためです。
しかし、現在ではハード性能の向上により、3Dゲームのような高度な画面更新を伴うアプリケーション以外ではFPSを気にする必要はほとんどありません。特に、ビジネスアプリケーションやSNSやメディア系のアプリであれば、FPSに苦しむことはほぼないでしょう。しかし、プログラムを扱う場合には、そのような背景から生じる仕組みについては多少理解しておく必要があります。
画面更新タイミングとプログラムの関係
スムーズな画面更新を実現するためにもっとも重要なのは、不必要な画面更新をしないことです。単純なアプリケーションでは簡単でも、複雑なアプリケーションになればなるほどその管理は難しくなります。
画面更新を管理する方法にはいろいろありますが、図1はFlutterでの画面更新管理をわかりやすいよう簡略化した図です。
![図1:画面更新管理について](http://cz-cdn.shoeisha.jp/static/images/article/13329/13329_001.png)
例えば、よりスムーズな画面更新がされていると人が認識するには60fps程度の実現が望ましいとされています。つまり、0.2秒で12回画面の更新が実現できれば良いです。これは、1フレームの処理で0.016秒以下という非常に短い時間になります。
従って、毎回、画面をすべて書き直していると処理が追いつかないケースが生じます。そこで、UIを扱う場合には、どの領域を書き換える必要があるかということと、何が変わった時に書き換える必要があるかを効率的に知ることで、よりスムーズな画面更新が実現できます。
ただし、Flutterアプリはゲームのような複雑な画面表示を行うことを目的としていないので、比較的単純な管理で問題ありません。Flutterでは以下の2つの管理を行えば、実際の画面更新はFlutterのフレームワーク側で自動的に行われる仕組みになっています。
- 画面を更新する部品の定義と、更新しない部品の定義
- 画面の更新が必要なタイミングの明示的な通知
Flutterでは、この画面更新するタイミング管理を「ステート(State)」と呼んでいます。従って、プログラマーはこの「ステート」を意識してプログラミングをする必要があります。
ステートフルウィジェットとステートレスウィジェット
Flutterでは、「ステート」を管理する必要があることを「ステートフル」といい、管理する必要がないことを「ステートレス」と呼びます。そして、いずれのUI部品もこのどちらかに所属します。
また、UI部品を「ウィジェット(Widget)」と呼んでいるので、これらの基底となるUI部品は「ステートフルウィジェット(StatefulWidget)」と「ステートレスウィジェット(StatelessWidget)」に分けられます。自分でUI部品を作成する場合には、このいずれかのクラスを継承する必要があります。そして、それぞれの画面制御をする流れが図2のようになります。
![図2:StatefulWidgetとStatelessWidgetでの画面制御の違い](http://cz-cdn.shoeisha.jp/static/images/article/13329/13329_002.png)
StatelessWidgetでは、インスタンスの作成時に画面表示処理であるbuild()メソッドが実行されます。その後にその表示を更新するタイミングはありません。つまり、StatelessWidgetの表示内容を変更するためには、再作成が必要です。通常は初回表示時と親部品が更新されるタイミングで再作成されます。
StatefulWidgetでは、setState()というメソッドを通じてフレームワーク側に画面更新が必要であることを通知します。あとは、フレームワーク側でbuild()メソッドが実行されるので、プログラマーは必要な画面表示処理をbuild()メソッド内に実装します。
実際にはもう少し複雑になるので、StatefulWidgetを作成する方法については後述しますが、まずはsetStateとbuildのように2つの行程が分かれていることを覚えていただければと思います。
「ステートフル」と「ステートレス」について
ネットワークプロトコルでは頻繁に「ステートフル」と「ステートレス」という言葉が使われます。そのため、筆者はUIを扱うフレームワークでこの言葉を見た時に少々、違和感を抱きました。しかし、意味はほとんど同じです。
例えば、HTTPは「ステートレス」プロトコルと言われるようにすべてのリクエストは、前の状態を維持する前提になっていません。そのため、通常のログインが必要となるWebアプリでは「ステートフル」を実現するために「セッション」という別の概念を導入します。
このようにプログラムの世界では状態管理をするか、しないかは大変重要なことでもあるので、こういった言葉に慣れることで他の技術も理解しやすくなります。
Flutterでの基本部品
実際にアプリケーションを作成する際には多くの既存の部品を組み合わせて作成しますが、Flutterアプリでは、アプリケーション全体管理するための「MaterialApp」と画面全体を構成するための「Scaffold」という部品が用意されています。
MaterialAppとScaffold
MaterialAppは、Flutterアプリケーションの全体を管理するものです。主に全体のデザインや画面遷移をする場合の状態監視、そして、アプリケーション全体のタイトルやその全体にかかわるプロパティの管理しています。
そして、Scaffoldは「足場」という意味になりますが、モバイルアプリ画面を作る上でよく使われるメニュー表示方法を含めて画面全体を管理する部品になります。これら2つの部品は非常に密接な関係であり図3のような関係があります。
![図3:MaterialAppとScaffoldの関係](http://cz-cdn.shoeisha.jp/static/images/article/13329/13329_003.png)
Scaffoldは、モバイルアプリであればなじみ深い、表1のような部品を管理できます。また、MaterialAppは、「Material(マテリアル)」という言葉からも連想できるように、「マテリアルデザイン」をコンセプトとしてアプリを作るためのものであり、AndroidやiOS、そしてデスクトップなど多くのデバイスを対象としたものです。
Scaffoldで管理できるUI部品 | 該当するUI部品 |
---|---|
AppBar | 画面上部に表示するタイトルやアイコンなど |
BottomNavigationBar | 画面の下部に表示するメニューボタンなど |
Drawer | ドロワーメニュー。必要時に左もしくは右側から表示されるメニュー領域 |
BottomSheet | 画面下部から必要時に表示される領域 |
SnackBar | 画面下部などに表示する通知表示領域 |
FloatingActionButton | 画面下部などに表示するショートカットボタン |
MaterialApp以外にも、iOSのUI/UXデザインに特化したCupertinoAppというものもあります。CupertinoAppを使った場合には、ScaffoldもかわりにCupertinoPageScaffoldを使います。
そして、ボタンやナビゲーションバーなどさまざまな部品でもCupertinoApp用に特化したものも用意されています。これらの関係を示したものが図4です。
![図4:MaterialAppとCurpertinoAppの関係](http://cz-cdn.shoeisha.jp/static/images/article/13329/13329_004.png)
これらは共通で使えるものもあれば、それぞれのUI/UXに特化したものもあります。そのため、CupertinoAppとCupertinoPageScaffoldだけを書き換えればUI/UXデザインが切り替えられる訳ではないのでご注意ください。また、この連載で紹介するUIはMaterialApp上で利用できることを前提として説明を行います。