目次
概要
Flutterは、 Google が開発したモバイルアプリ向けの UI フレームワークです。 Flutter を使うことで、 iOS と Android で同じコードを使用して、アプリの開発を行うことができます。 Flutter の特徴は、以下のような特徴があります。
- 高速な開発サイクル
- Hot Reload の仕組みにより、コードの編集とアプリへの反映をリアルタイムに確認することができます。
- リッチな UI
- Flutter SDK で提供されている Widget を使用することで、リッチな UI を作成することができます。
- Material Components や Cupertino という Widget を使うことで、 Android や iOS のプラットフォームネイティブな見た目のアプリを開発できます。
- モダンなフレームワーク
- ネイティブの機能のサポート
- iOS や Android のネイティブな機能にアクセスすることができます。
- また、既存の SDK を利用することもできます。
- 一貫した開発環境
- Flutter は、ビルドからデプロイまでの開発をサポートします。
- オープンソース
- Flutter のソースコードは、GitHubにホストされています。
インストール方法
Flutter のインストールは、以下の手順で行います。
- Flutter SDK Archiveから、インストールしたい Flutter のパッケージをダウンロードする。
- 以下の組み合わせから、適切なものを選択してください。
- Windows 、 macOS 、 Linux
- Beta チャンネル、 Dev チャンネル、 Master チャンネル
- 使っている OS の Beta チャンネルのものを使えば問題ないかと思います。
- 以下の組み合わせから、適切なものを選択してください。
- ダウンロードしてきたパッケージを任意の場所に展開する。
flutter
のツール群を利用できるように、パスの設定を行う。- 依存しているパッケージなどをインストールするのに、
flutter doctor
を実行する。
Windows
Windows 環境向けに、flutter_console.bat
というバッチファイルが提供されています。
このバッチファイルを開くと、flutter
にパスが通った状態で、コンソールが立ち上がります。
環境変数を更新する必要がないので、試しに使ってみたい、という場合は、こちらを利用するのが良いと思います。
本格的に Flutter を使った開発を行う際は、環境変数を更新するのが良いでしょう。
iOS アプリの開発
iOS アプリの開発は、 macOS でのみ可能です。
Xcode のインストール
- Mac App Storeから、 Xcode のインストールを行います。
- Xcode を起動し、ライセンスに同意します。
sudo xcodebuild -license
をターミナルで実行することで、 Xcode を起動せずに、ライセンスに同意することもできます。
シミュレーターの設定
open -a Simulator
をターミナル上で実行し、シミュレーターを起動させます。- シミュレーターでは、64bit 端末を使用する。
flutter run
を実行し、アプリをシミュレーターで実行する。
iOS 端末の設定
iOS 端末で、 Flutter で作成したアプリを実行する場合には、homebrewを使って、ツールをインストールすることが必要になります。
- homebrewをインストールする。
- 以下のコマンドを実行し、必要なツールのインストールを行う。
brew update brew install --HEAD libimobiledevice brew install ideviceinstaller ios-deploy cocoapods pod setup
Flutter を使用してプロジェクトを作成すると、ios/Runner.xcworkspace
というワークスペースファイルが作成されます。 Xcode でこのファイルを開き、サイニングの設定を行ってください。
アプリの実行は、シミュレーターと同様、flutter run
で行います。
Android アプリの開発
Android Studio のインストール
- Android Studioをダウンロードし、インストールを行う。
- Android Studio を起動し、
Android Studio Setup Wizard
から、以下のものをインストールする。- 最新の Android SDK
- Android SDK Platform-Tools
- Android SDK Build-Tools
Android 端末の設定
- 端末上で、開発者向けオプションを有効にし、 USB デバッグを有効にする。
- 端末を USB ケーブルで、 PC と接続し、 PC から端末にアクセスできるようにする。
- ターミナル上で、
flutter devices
を実行し、 Flutter が端末を認識していることを確認する。 flutter run
を実行し、アプリを端末で実行する。
Android エミュレーターの設定
- PC 上で、Hardware Accelerationを有効にする。
- AVD Manager を起動し、新しい仮想端末を作成する。
- 適当なハードウェアを選択し、 Next を押す。
- 任意のシステムイメージを選択し、 Next を押す。
- x86、または、 x86_64のシステムイメージが水晶となっています。
- Emulated Performance の項目を、 Hardware – GLES 2.0にし、アクセラレーションを有効にする。
- 仮想端末の検証をし、 Finish を押す。
- AVD Manager から、作成した仮想端末を起動させる。
flutter run
を実行し、アプリを仮想端末で実行する。- 接続されたデバイス名は、
Android SDK built for <platform>
となる。
- 接続されたデバイス名は、
開発環境の構築
Flutter アプリの開発では、任意のエディターと Flutter ツールチェーンを使うことになります。 公式がプラグインを提供しているエディターもあり、そのようなプラグインを使用すると、コード補完、構文ハイライトなどの便利な機能を活用できます。 公式なプラグインとしては、以下のエディター/開発環境に向けたものが提供されています。
ここでは、上記の開発環境でのプラグインの導入方法と使い方を解説します。
Android Studio/IntelliJ IDEA
プラグインのインストール
- Preferences を開きます。
- Plugins から、「 Browse repositories…」ボタンを押し、プラグイン選択ダイアログを開きます。
- Flutter プラグインをインストールします。
- Flutter プラグインと同時に、 Dart プラグインをインストールされます。
- 検索から、
Flutter
と入力すると、すぐに探せます。
- Android Studio を再起動します。
アプリの開発
- File > New > Project… を選択します。
- New Project から、 Flutter を選択します。
- プロジェクトの設定を行い、プロジェクトを作成します。
- ツールバーから、端末の選択、アプリの実行、デバッグを行います。
- 通常の Android アプリの開発と同じような流れで実行できます。
Visual Studio Code
プラグインのインストール
- Command Palette を表示します。
- アクション
Extensions: Install Extension
を選択します。Extensions
を入力して、アクションを絞り込むと、インストールアクションが探しやすいです。
- 検索フィールドに、
Dart Code
を入力し、プラグインをインストールします。 - インストール後、 Visual Studio Code のリロードを行います。
- リロード後、 Command Palette で、
Flutter: Run Flutter Doctor
を選択し、プラグインが正常にインストールできていることを確認します。
アプリの開発
- Command Palette から、アクション
Flutter: New Project
を選択します。 - プロジェクト名を入力します。
- プロジェクト名は、大文字が使えないので、注意してください。
- プロジェクトの保存先を選択します。
- プロジェクトの作成後、プロジェクトにある
lib/main.dart
が表示されます。- ファイルの作成などが行われるため、
lib/main.dart
が表示されるまで、しばらく時間がかかります。
- ファイルの作成などが行われるため、
- アプリの実行は、メニューから、デバッグの開始を選択するか、 F5キーを押して行います。
- 事前に、端末、または、エミュレーター/シミュレーターを起動しておく必要があります。
最初の Flutter アプリ開発
ここでは、簡単なアプリの開発を通して、 Flutter の使い方を解説します。
プロジェクトの作成方法
Android Studio/IntelliJ IDEA
- File > New > Project… を選択します。
- New Project から、 Flutter を選択します。
- プロジェクトの設定を行い、プロジェクトを作成します。
Visual Studio Code
- Command Palette から、アクション
Flutter: New Project
を選択します。 - プロジェクト名を入力します。
- プロジェクト名は、大文字が使えないので、注意してください。
- プロジェクトの保存先を選択します。
コマンドライン
適当なディレクトリに移動し、以下のコマンドを実行します。
flutter create project_name
flutter create
で指定できるオプションは、-h
で確認できます。
flutter create -h
project_name
で指定した名前のディレクトリが、カレントディレクトリ直下に作成され、その中に、アプリに必要なファイルやソースコードが出力されます。
アプリの実行/デバッグ方法
プロジェクトが作成された時点のアプリを実行して、開発中にアプリをテストする流れを確認しましょう。
- アプリを動作させる端末の準備
- Android であれば、
emulator
コマンドで、仮想端末を起動させます。 - iOS であれば、
open -a Simulator
を実行し、シミュレーターを起動させます。
- Android であれば、
- アプリの実行
- Android Studio/IntelliJ を使っている場合は、ツールバーから、端末の選択をし、アプリを実行します。
- Visual Studio Code を使っている場合は、
F5
を押して、デバッグの開始を行います。 - コマンドラインの場合は、
flutter run
を実行します。
- アプリの動作を確認
- 起動したアプリを動かします。
- Floating Action Button (画面右下の丸ボタン)を押すと、カウントが+1されます。
- Hot Reload
- 画面上に表示されている文字列を書き換えて、 Hot Reload で、変更が反映されることを確認します。
lib/main.dart
を開き、93行目の'You have pushed the button this many times:'
という部分を適当な文字列に書き換えて保存します。- すると、アプリの表示が、書き換えた文字列に変わることを確認します。
- Hot Reload の詳細は、Using Hot Reloadを参考にしてください。
サンプルアプリ開発
プロジェクトの作成方法、アプリの実行方法を簡単に確認したので、アプリの開発を行っていきます。 ここでは、公式サイトのWrite Your First Flutter Appにあるアプリを作ってみます。
Write Your First Flutter Appで作成するアプリは、以下のようなアプリです。
- 任意の単語を2つ繋げた単語のリストを表示します。
- 無限リストとして、単語のリストを表示します。
- 単語のリストを選択すると、お気に入りに登録されます。
- お気に入り登録済みの単語を選択すると、お気に入りから削除されます。
- ナビゲーションバーから、お気に入りに登録した単語のリストを確認できます。
以下に、ここで作成するサンプルアプリのスクリーンショットを掲載します。
プロジェクト作成
前述のやり方で、プロジェクトを作成します。
ここでは、startup_namer
という名前のプロジェクトを作成してください。
外部パッケージの追加と利用
多くのアプリの開発現場では、外部パッケージを使用して、アプリの開発を行います(もちろん、外部パッケージを全く使わない、という場合もありますが、そのようなケースは少ないでしょう)。
例えば、 iOS アプリであれば、ある機能を持った View やアプリで使用される汎用的な機能(API リクエストなど)を提供するフレームワークを、CocoapodsやCarthageで管理し、開発を行います。
ここでは、どのようなパッケージを利用するのか? また、どのバージョンを利用するのか? ということを、開発チームで、共通の環境となるようにする、という目的で用いられます。
Flutter SDK には、このようなパッケージ管理のためのツールも同梱されています。
このパッケージ管理ツールは、pubspec.yaml
に記載された情報を元に、パッケージの管理を行っています。
今回、作成するアプリでは、english_words
という英単語を扱うパッケージを使用します。
アプリが使用するパッケージは、dependencies
のセクションに以下のように記載します。
dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.0 english_words: ^3.1.0
パッケージのバージョンの指定方法などは、Pub Versioning Philosophyを参考にすると良いでしょう。
pubspec.yaml
を保存すると、利用しているエディタによっては、自動的に、パッケージの取得処理が実行されます。
パッケージの取得処理が自動的に行われない場合は、エディタから、Get Packages
コマンドを手動で実行します。
追加したパッケージをアプリから利用する場合は、import
文で、パッケージを取り込むことで利用できます。
import 'package:english_words/english_words.dart';
上記のimport
文で、english_words
を取り込むことで、english_words
が提供する機能を利用できます。
簡単に、english_words
の利用方法を確認するために、lib/main.dart
で、単語のペアを生成し、それを表示してみます。
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { // ランダムで、単語のペアを生成する final wordPair = new WordPair.random(); return new MaterialApp( title: 'Welcome to Flutter', home: new Scaffold( appBar: new AppBar( title: new Text('Welcome to Flutter'), ), body: new Center( // ランダムに生成した単語のペアをパスカルケースにして、表示する child: new Text(wordPair.asPascalCase), ), ), ); } }
単語のリストを表示
今回作成するアプリでは、単語のリストをランダムに生成するため、リストは無限の長さを持ちます。 そのため、スクロールをし、現在のリストの長さよりも先の要素が必要になった時点で、単語を作成する、というようにします。 このため、リストは、現在の状態を持ち、スクロールによって、その状態を更新しなければなりません。
Flutter では、アプリは、Widget
から構成されます。
Widget
は、状態を持たないStatelessWidget
と、状態を持つStatefulWidget
の2種類があります。
今回作成するアプリでは、前述のように、現在表示している単語のリストを状態として持ちます。
つまり、StatefulWidget
を使って、リストの表示を行います。
StatefulWidget
を使う場合は、以下のクラスを継承したクラスを用意します。
StatefulWidget
- 状態を持つ
Widget
の定義
- 状態を持つ
State
StatefulWidget
の状態とロジック
今回作成するアプリでは、それぞれ、以下のようなクラスを作成します。
RandomWords
(StatefulWidget
を継承したクラス)createState()
メソッドで、自身の状態を表すRandomWordsState
のインスタンスを返す。
RandomWordsState
(State
を継承したクラス)- 現在表示している単語のリストを保持します。
- お気に入りに登録された単語のリストを保持します。
RandomWords
として、表示する UI を構築します。- お気に入りへの登録、お気に入りからの削除、などのユーザー操作を受け付けます。
RandomWords
の実装
RandomWords
の実装は、以下の要件を満たすように行います。
StatefulWidget
を継承したクラスを定します。createState()
をオーバーライドし、RandomWordsState
のインスタンスを返す。
以下のような内容を、lib/main.dart
に追加します。
// StatefulWidget を継承した RandomWords クラスを定義 class RandomWords extends StatefulWidget { // createState()をオーバーライドし、 RandomWordsState のインスタンスを返す。 @override createState() => new RandomWordsState(); }
RandomWordsState
の実装
RandomWordsState
の実装は、以下の要件を満たすように行います。
State
を継承したクラスを定義します。StatefulWidget
を継承したRandomWords
に結びつけます。- リストとして表示する単語を保持します。
- お気に入りに登録された単語のリストを保持します。
RandomWords
の UI を定義します。- ユーザーのタップ操作により、お気に入りへの登録、お気に入りからの削除を行います。
1.と2.は、以下のようなRandomWordsState
を定義することで、実現できます。
class RandomWordsState extends State<RandomWords> { }
この定義により、State
を継承したRandomWordsState
が定義され、State<RandomWords>
という記述から、RandomWords
というStatefulWidget
と結びついていることが保証されます。
次に、表示する単語を保持するプロパティとお気に入りに登録された単語を保持するプロパティを定義します。
class RandomWordsState extends State<RandomWords> { // リストに表示する単語のリスト final _suggestions = <WordPair>[]; // お気に入りに登録された単語のセット final _saved = new Set<WordPair>(); // 単語を表示させるテキストのスタイルの設定 final _biggerFont = const TextStyle(fontSize: 18.0); }
画面上に、単語のリストなど、何かのリストを表示するときは、ListView
を使用します。
今回作るアプリでは、リストから、表示するWidget
を決めるので、ListView.builder
を使うと簡単に実装できます。
以下のようなメソッドを定義し、ListView
のインスタンスを返すメソッドを実装します。
Widget _buildSuggestions() { return new ListView.builder( // ListView の領域に、16pt の余白を作る padding: const EdgeInsets.all(16.0), // リストの i 番目の場所の要素を表示する Widget を作る itemBuilder: (context, i) { // 要素間に、分割線をいれたいので、奇数番目の場所は、分割線を返す if (i.isOdd) return new Divider(); // 偶数番目のインデックスを_suggestions の List のインデックスに変換する final index = i ~/ 2; if (index >= _suggestions.length) { // リストよりも先の要素を表示しようとしていたら、新しく単語を生成する _suggestions.addAll(generateWordPairs().take(10)); } // index 番目の単語を表示させる return _buildRow(_suggestions[index]); }, ); }
上記のメソッドは、以下のようなことを行なっています。
ListView.builder
を呼んで、ListView
のインスタンスを生成します。padding
パラメーターを指定して、ListView
の余白を作ります。itemBuilder
パラメーターに、i
番目のインデックスで表示するWidget
を返す関数/メソッドを指定します。- 今回は、要素間に、1pt の分割線をいれたいので、
i
が奇数であれば、Divider
のインスタンスを返す。 i
が偶数であれば、~/
で、2で除算を行なった結果を整数として、計算します。index
が、現在のリストの要素数以上であれば、新たに10個単語を生成して、リストに追加します。_suggestions
のindex
番目の要素を_buildRow
に渡し、単語を表示させるWidget
を生成します。
- 今回は、要素間に、1pt の分割線をいれたいので、
リスト全体を表示するメソッドの実装を行なった次は、単語を表示する箇所を実装します。
ListView
に表示する場合は、ListTile
を使います。
ListTile
は、アイコンとテキストをListView
に表示するのに便利な Widget です。
これを使って、単語を表示します。
Widget _buildRow(WordPair pair) { // お気に入りに登録されているかどうか? final alreadySaved = _saved.contains(pair); return new ListTile( // 本文として表示するテキストの設定 title: new Text( pair.asPascalCase, style: _biggerFont, ), // ListTile の右側に表示するアイコンの設定 trailing: new Icon( // アイコン画像の設定 alreadySaved ? Icons.favorite : Icons.favorite_border, // アイコンの色の設定 color: alreadySaved ? Colors.red : null, ), // ListTile をタップされた時の動作の定義 onTap: () { // 状態の更新を通知する setState(() { if (alreadySaved) { // お気に入りから削除 _saved.remove(pair); } else { // お気に入りに追加 _saved.add(pair); } }); }, ); }
上記のメソッドでは、以下のことを行なっています。
pair
が、_saved
に含まれているかどうかを判定します。ListTile
のインスタンスを生成します。- ここで、生成されたインスタンスが、単語の表示に使われます。
title
パラメーターに、表示する単語を指定します。trailing
パラメーターに、右側に表示するお気に入りアイコンを指定します。- アイコンの表示は、
alreadySaved
の値によって変わります。
- アイコンの表示は、
onTap
パラメーターに、タップされた時の処理を定義します。onTap
に指定した処理の中で、setState()
を呼び出し、状態の更新をフレームワークに通知します。setState()
の呼び出しにより、 UI の更新が行われます。
ここまでの部分で、RandomWordsState
の実装は、概ね完了したので、アプリ起動時に、RandomWords
を表示させるようにRandomWordsState
に、build()
メソッドを定義します。
build()
メソッドでは、このクラスで表示する Widget を返します。
アプリの画面に対応する場合は、画面全体を表示するための Widget のインスタンスを返します。
Material Design なアプリを実装する場合、Scaffold
を使用すると、必要な機能が、直感的に実装できます。
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Startup Name Generator'), ), body: _buildSuggestions() ); }
appBar
パラメーターには、AppBar
を設定します。- App Bar の詳細については、 Material Design の
App bar
に詳細が記載されています。
- App Bar の詳細については、 Material Design の
body
パラメーターには、メインとなるコンテンツを表示する Widget を指定します。- 今回は、
_buildSuggestions()
メソッドで、リストを表示するListView
のインスタンスを生成しているので、これを指定します。
- 今回は、
次に、アプリ全体の定義を行います。
// main 関数の定義 // runApp で、アプリ起動時に表示する画面を指定しておく void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { // build メソッドでアプリを生成する @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter App', home: new RandomWords(), ); } }
MaterialApp
のインスタンスを生成することで、 Material Design に準拠したアプリの Widget を生成します。
home
パラメーターには、アプリの起動時に表示する画面のWidget
のインスタンスを指定します。
お気に入りリストの表示
お気に入りに登録された単語を表示する画面の実装と、その画面への遷移を実装します。
App bar にボタンを追加し、そのボタンをタップされた時に、お気に入りリストの画面へ遷移させます。
画面への遷移とお気に入りリストの画面自体は、_pushSaved()
メソッドで実装し、まず、 App bar にボタンを追加します。
class RandomWordsState extends State<RandomWords> { ... @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Startup Name Generator'), actions: <Widget>[ new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved), ], ), body: _buildSuggestions() ); }
App bar にボタンを追加する場合は、単純に、actions
パラメーターに、ボタンを追加するだけです。
IconButton
のonPressed
に、後ほど作成するメソッド_pushSaved
を指定します。
Flutter での画面遷移は、Navigator
を使って行います。
Navigator
は、ナビゲーションスタックを管理し、 push/pop などのアニメーションを行っています。
お気に入りリスト画面への遷移と、お気に入りリスト画面の表示は、以下のように行います。
void _pushSaved() { // Navigator のスタックに、お気に入りリストの画面を push する Navigator.of(context).push( // route の定義を行う new MaterialPageRoute( builder: (context) { // お気に入りに登録されている単語を表示する ListTile のインスタンスを生成する final tiles = _saved.map( (pair) { return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ) ); } ); // divideTiles メソッドを使って、要素間に分割線を追加する final divided = ListTile.divideTiles( context: context, tiles: tiles, ) .toList(); // 遷移先の画面を生成する return new Scaffold( appBar: new AppBar( title: new Text('Saved Suggestions'), ), body: new ListView(children: divided), ); } ) ); }
最終的なコード
上記までの実装をまとめると、lib/main.dart
は、以下のようになります。
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Fulluter App', theme: new ThemeData(primaryColor: Colors.white), home: new RandomWords(), ); } } class RandomWords extends StatefulWidget { @override createState() => new RandomWordsState(); } class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; final _saved = new Set<WordPair>(); final _biggerFont = const TextStyle(fontSize: 18.0); @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('Startup Name Generator'), actions: <Widget>[ new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved), ], ), body: _buildSuggestions()); } Widget _buildSuggestions() { return new ListView.builder( padding: const EdgeInsets.all(16.0), itemBuilder: (context, i) { if (i.isOdd) return new Divider(); final index = i ~/ 2; if (index >= _suggestions.length) { _suggestions.addAll(generateWordPairs().take(10)); } return _buildRow(_suggestions[index]); }, ); } Widget _buildRow(WordPair pair) { final alreadySaved = _saved.contains(pair); return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), trailing: new Icon( alreadySaved ? Icons.favorite : Icons.favorite_border, color: alreadySaved ? Colors.red : null, ), onTap: () { setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, ); } void _pushSaved() { Navigator.of(context).push(new MaterialPageRoute(builder: (context) { final tiles = _saved.map((pair) { return new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, )); }); final divided = ListTile .divideTiles( context: context, tiles: tiles, ) .toList(); return new Scaffold( appBar: new AppBar( title: new Text('Saved Suggestions'), ), body: new ListView(children: divided), ); })); } }
今回のまとめ
今回は、 Android/iOS アプリを開発するための UI フレームワーク Flutter を紹介しました。 また、簡単なサンプルアプリの作成を通して、 Flutter が、どのようなものか、ということを解説しました。 サンプルアプリの開発からも分かるように、 Flutter は、全てが Widget で構成される、という特徴や、 Hot Reload など、強力な機能を持ったフレームワークです。 実際に、 Flutter を採用しているアプリが、すでに公開されており、十分に実用できるフレームワークではないかと思います。