Step1 の概要
Step1 では、Flutter アプリのテンプレートを作成するところから、アプリを開発し始めるための準備事項について説明します。
本章で学べること
- Flutter を開発するための準備
- Flutter テンプレートの中身と Widget の基本
Flutter の準備
何はともあれ、Flutter をインストールしてください。
基本的に行う作業は以下の通りです。
- SDK をダウンロードする
- パスを通す
-
flutter doctor
の結果を見て随時セットアップする
環境構築に関しては数多くの記事が出ておりますので、ここで長々と説明するのは避けます。
とりあえず、flutter create myapp
が動作し、flutter run
で Android エミュレーターでも、iOS シミュレーターでも、Web でも、USB 接続した実機でも、お好きな環境でテンプレートのカウンターアプリが動作するところまでは頑張ってください。
リリースを考慮すると、
flutter create --org orgname myapp
の方が良いです。
サンプルリポジトリの使い方
本書で取り扱うアプリのサンプルリポジトリを用意しました。
タグを設定してありますので、Step1 の完成形を見たい場合は
git clone https://github.com/sugitlab/pokemon_flutter
に加えて
git checkout -b step1 refs/tags/step1
とタグのポイントでブランチを作成&チェックアウトして使用してください。
このサンプルは 「1. はじめに」に記載のバージョンの Flutter を使用しております。
リポジトリには .fvm ファイルが保存されていますので、FVM を活用しても大丈夫です。
FVM について詳しく知りたい場合は別途発行の別冊版(準備中...)を参照してください。
カウンターアプリの大掃除
Flutter の初期状態ではカウンターアプリが用意されています。ほとんどの要素は使いませんので、大掃除しておこうと思います。
まず、その初期状態のカウンターアプリが動作することを確認しましょう。iOS でも Android でも Web でも良いので、何かの環境を用意して以下のコマンドでアプリを起動してください。
flutter run
カウンターアプリのもろもろの要素は不要なので掃除します。
- コメント類を消す
- FAB(Floating Action Button)を消す
- AppBar を消す
- StatefulWidget を消して StatelessWidget へ変更する
- 画面中央に HelloWorld を表示する
上記内容、何も説明されなくともできますよ!という方はこの章の最後までジャンプしてください。
以下は上記の大掃除をしながら、カウンターアプリの構成について触れていきます。
Flutter のディレクトリ構成
tree コマンドを使って Flutter プロジェクトのディレクトリ構成を出力しました。一部説明には不要なものはカットしてあります。
.
├── .dart_tool
├── .gitignore
├── .idea
├── .metadata
├── .packages
├── README.md
├── analysis_options.yaml
├── android
├── build
├── flutter_pokemon_trial.iml
├── ios
├── lib
│ └── main.dart
├── pubspec.lock
├── pubspec.yaml
├── test
│ └── widget_test.dart
└── web
18 directories, 28 files
上から順に簡単に説明します。
- .gitignore
- git リポジトリとして取り扱うときにバージョン管理不要なファイルを指定します
- 基本そのままで OK です
- README.md
- git リポジトリにはお馴染みのファイルです。リポジトリの説明を記載するものです。
- flutter create したファイルそのままだとカッコ悪いので早めに書き換えましょう。
- analysis_options.yaml
- Linter の設定ファイルです。Linter については後述します。
- android
- Android 向けのビルドに必要なファイル群です
- build
- ビルド成果物です
- ios
- iOS 向けのビルドに必要なファイル群です
- lib
- Flutter の実装本体です
- pubspec.lock
- 依存するパッケージの情報を詳細に保持します
- pubspec.yaml
- 依存するパッケージに関する設定です
- test
- テスト用のサンプルです。
- web
- Web 向けのビルドに必要なファイル群です
- 記述がないファイル/フォルダ
- 無視して OK です
Flutter の開発において基本的に編集するのは lib フォルダの中身です。
次点で、pubspec.yaml はパッケージの追加やリリースのバージョン管理など、アプリケーション全体の設定ファイルですので、比較的よく触ります。
それ以外のファイルについてはゴリゴリと編集するようなシチュエーションはほぼありません。パッケージの導入時に指示された設定を加える程度です。
test もテストを書かなければ不要です。本書のようなアプリ作りの練習であれば不要です。リリースを目指した際に Flutter のテストはどうあるべきかを考える必要が出てくるので、そのときまでそっとしておいてください。
main.dart を紐解く
lib フォルダ下にある main.dart が Flutter アプリケーションを開発するためのスタート地点です。
main.dart をエディタで開いてみましょう。VSCode でも良いですし、Android Studio でも結構です。私は NeoVim を使っています。
NeoVim でも設定すれば Linter や intellicense のようなことはできます。ただやはり、デバッグが煩わしかったり、Widget のベースのコード(ボイラープレート)をサクッと導入してくれる機能などは用意していないので、VSCode や Android Studio のほうがおすすめです。私はエディタとしてサクサク動く NeoVim を初期の段階では使ってゴリゴリ記述し、そこそこアプリが出来上がってきてちょとデバッグでも、という段階で VSCode に切り替えています。
main.dart には冒頭に以下の記述があります。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
import 文
JavaScript に慣れている方であれば特に違和感なく感じると思いますが、import
文ではそのプロジェクトで利用可能なパッケージを各ファイルで参照可能にするために使用します。パッケージだけではなく、別のファイルに記述した実装を参照するためにも使用します。
例えば、上記実装において MyApp
が別のファイル myapp.dart にあるとします。(main.dart 同様、lib フォルダ直下と仮定します)
その場合は以下のように記述します。
import 'package:flutter/material.dart';
import './myapp.dart';
void main() {
runApp(const MyApp());
}
もしくは以下のようにも記述できます。
import 'package:flutter/material.dart';
import 'package:pokemon_flutter/myapp.dart';
void main() {
runApp(const MyApp());
}
これについてはどちらが良いというわけではありませんが、記述方法が 2 つもあるとチームで開発する際にある人は相対パス(1 つ目の書き方)で記述し、またある人はパッケージのパス(2 つ目の書き方)で記述するというぐちゃぐちゃコードが誕生してしまいます。
こういうのはあまり良くないので、チームで統一しておきましょう。
チームでの統一を "このプロジェクトでは相対パス禁止です〜" とアナウンスする時代はもう終わりました(これはこれで大事です)。これからは Linter に任せましょう。パッケージのパスの設定についても Linter を使ってルールの自動チェックを行うことができます。 Linter について詳しく知りたい場合は別途発行の別冊版(準備中...)を参照してください。
main メソッドと runApp メソッド
main
メソッドは Flutter の実行時にプログラムのエントリーポイントとして扱われるもので、全ての処理(実装した分)についてはmain
メソッドを起点に動き始めます。Flutter アプリではrunApp()
で最初に展開して欲しい Widget を指定します。通常は MaterialApp を持ってきます。iOS スタイルで開発する場合は CupertinoApp も同様に使用できます。もちろん、その他の Widget でも配置可能です。
runApp()
がなければ、それはただの Dart プログラムとして動作します。
Flutter アプリ開発においては、とりあえず "main から始まって、runApp を呼んだところから Widget が必要になるんだね" ということを覚えておいてください。
MyApp Widget
Flutter では全てが Widget であると言われます。これは Flutter が公式に言っています。Widget という言葉についてはおいおい理解するとして、とりあえず部品があって、それを組み合わせて作るんだと思っておいてください。レゴブロックのようなものです。
テンプレートの MyApp Widget について見てみましょう。
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: title,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: title),
);
}
}
まず class
として MyApp が定義されています。オブジェクト指向な言語に触れていればクラスという言葉については一切抵抗なくスタートできると思います。ここでオブジェクト指向についてあれこれ説明するつもりはないので、ぜひオブジェクト指向については理解を深めてみてください。オブジェクト指向といえば以下の書籍が良本として有名です。
さて、Flutter は全てが Widget だと言いましたが、Widget もクラスです。Widget というなんだか新しい概念が出てきた感がありましたが、ただのクラスです。Flutter を動かす上で必要な機能がモリモリのったクラスです。クラスならよく扱うぜ、という方ならもう怖くないですね。
次に StatelessWidget
ですね。これはちょっと落ち着いて説明したいので別冊(準備中)に詳細を記載します。
ここではとりあえず概要だけ触れようと思いますが、もう少し後で StatefulWidget
という対になるような Widget が出てきます。そのタイミングで説明します。とりあえず、よく使う Widget のひとつだと覚えておいてください。
次は const MyApp({Key? key}) : super(key: key);
ですね。
これの理解をキチンとするにはオブジェクト指向について理解し、クラスの親子関係について知っている必要があります。こちらも概要だけ説明すると、これはコンストラクタです。コンストラクタとはクラスが実体として生成される際に呼び出される処理を指します。さらに後半に付属している :super(key: key)
はコンストラクタのリダイレクトと呼ばれる Dart 独特の書き方です。リダイレクトの名の通り、コンストラクタが呼び出されたときにまた別のコンストラクタを呼び出すための紐付けです。super
は親を指すので、元を辿れば Widget
クラスのコンストラクタを呼び出していることになります。
詳細はこちらの Dart Language Tour の "Redirecting constructors" を参照してください。
ここでは加えて、Flutter における Key とはなんなのか、の理解が必要ですね。
Key もとても重要な仕組みですのでこちらについても別冊にて詳細を説明します。
簡単に言えば、Flutterにおいて画面に表示されている要素を特定することができるキーです。
次は @override
ですね。これはそのままオーバーライドです。これもオブジェクト指向あるあるですね。親のクラスにあるメソッドに対してオーバーライドを定義してあげると、親のクラスのメソッドが呼び出されそうなタイミングで代わりにこちらが呼び出されます。処理を奪い取るような感じがしますが、どちらかというと親のクラスは子クラスで実装することを想定して書き方だけ提供しているような感じです。実際、StatelessWidget
では build メソッドが定義されていますが、中身がありません。Flutter のフレームワークとして、各 Widget の build メソッドを順次呼び出しますよ、というのが決まっているので、各 Widget はそれに備えて build メソッドをオーバーライドして実装しておく、というものです。
この build メソッドが順次実行されるという仕組みが Flutter の根幹をなす仕組みのひとつです。これについては Flutter のライフサイクルについての理解が必要です。このあたりは別冊で説明する予定です。
やっと到達しました、MaterialApp
です。これは Flutter が提供する基本ライブラリである material の仕組みをアプリケーション全体に提供するために一番根っこに定義するものです。MaterialApp の定義を見るとわかるのですが、かなりの数のパラメーターを指定することができます。ほとんどのパラメーターはデフォルト値を持つので、指定しなくてもアプリケーションは動作します。より詳細なカスタマイズが必要になった段階で変更を検討すれば良いですが、そうそうに触ることになるのが Theme
です。テーマについては実際にポケモンアプリを作る過程で使用します。
以上で MyApp の確認が終わりました。
最後に home に設定されている MyHomePage
について触れます。
MyHomePage はStatefulWidget
で作られています。先程後回しにした、StatelessWidget
と対をなす Widget です。StatefulWidget
はそれ単体ではあまり意味がありません。別途 State<T>
クラスを実装する必要があります。StatefulWidget
とState<T>
には紐付けが行われています。それが createState
です。State<T>
は状態を保持するクラスです。つまり、状態を保持するクラスと連携して動作するのが、StatefulWidget
で、単独で動作しつつ保持する値は何も変わらない StatelessWidget
とはペアということになります。
なんだかいろいろな要素が詰まっていてよくわからなかったかもしれません。ほとんどの説明を "別途やります" としたのは、具体的なアプリを組む前にこのあたりを理解するのは現実的ではないですし、あまりメリットもないと思っているからです。
この説明で覚えておいていただきたいのは以下のポイントです。
- Flutter は Widget でできている
- Widget は Dart のクラスである
- 基本の Widget に StatelessWidget と StatefulWidget がある
では、予定通りアプリの大掃除をしましょう。項目は以下の通りです(再掲)。
- コメント類を消す
- FAB(Floating Action Button)を消す
- AppBar を消す
- StatefulWidget を消して StatelessWidget へ変更する
- 画面中央に HelloWorld を表示する
大掃除が完了したなら、以下のサンプルから抜粋して確認してみましょう。
git checkout -b step1 refs/tags/step1