FlutterでAndroid/iOS両対応アプリを作ってみる
Flutter というGoogle製フレームワークが最近β版になったそうです。
Flutter は Dart という言語を使い、同じコードで Android/iOS 両方のアプリを作れることができるフレームワーク。
Dart は Google 製の言語で、最初見たとき「Javaっぽいな」という印象でした。
ある程度型付けもできるようですし、他のクロスプラットフォーム環境より入っていきやすそうだったので触ってみました。
導入の方法、公式のチュートリアルをやった感想などをメモしておきます。
Flutter の始め方
Flutter公式 の GET STARTED に詳しく説明があるのでこれに従っていけば OK です。
Flutter 本体のセットアップ
まずは Flutter 本体をダウンロードし、開発環境を整えていきます。
Flutter のリポジトリを clone して PATH を通すという流れです。
1 2 3 |
$ git clone -b beta https://github.com/flutter/flutter.git $ export PATH=`pwd`/flutter/bin:$PATH |
これで flutter というコマンドが使えるようになるので、flutter doctor
でちゃんとインストールできてるか確認しておきましょう
1 2 |
$ flutter doctor |
私の環境では下記のような結果になりました
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel beta, v0.1.5, on Mac OS X 10.13.3 17D47, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK 26.0.2) [!] iOS toolchain - develop for iOS devices (Xcode 9.2) ✗ libimobiledevice and ideviceinstaller are not installed. To install, run: brew install --HEAD libimobiledevice brew install ideviceinstaller ✗ ios-deploy not installed. To install: brew install ios-deploy [✓] Android Studio [✓] Android Studio (version 3.0) [!] IntelliJ IDEA Ultimate Edition (version 2017.2.6) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] IntelliJ IDEA Community Edition (version 2017.1.3) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] VS Code (version 1.19.2) [✓] Connected devices (2 available) |
IntelliJ や VS Code などの開発環境で、必要なプラグインが入っているかもチェックしてくれてるみたいですね。私は IntelliJ 使うつもりだったので、
- iOS tool chain で入ってないものを brew でインストール
- IntelliJ で Flutter と Dart プラグインをインストール
だけ追加でセットアップしました。
一応再度 flutter doctor
。こんな感じで使う環境は問題無さげな結果になりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel beta, v0.1.5, on Mac OS X 10.13.3 17D47, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK 26.0.2) [✓] iOS toolchain - develop for iOS devices (Xcode 9.2) [✓] Android Studio [✓] Android Studio (version 3.0) [✓] IntelliJ IDEA Ultimate Edition (version 2017.2.6) [!] IntelliJ IDEA Community Edition (version 2017.1.3) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] VS Code (version 1.20.1) [✓] Connected devices (2 available) |
なお、flutter コマンドは今のセッションでだけ有効となるような PATH の通し方をしましたが、 .bash_profile 等で設定しておいて常に使えるようにした方が良さそうです。
その辺のことも上記リンクに書いてあるので一読しておきましょう。
プロジェクトの作成
上記 GET STARTED のドキュメントでは、ここから Android、iOS の各デバイス向けのビルド・実行の説明が続きますが、その前にプロジェクトを作っておきましょう。その方が確認がスムーズにできるかと思います。
flutter create
でプロジェクトを作成できます。
1 2 |
$ flutter create project_name |
なお、IntteliJ などの IDE からでもプロジェクト作成は行えるようです。詳しくはこちらのリンクを参照。
プロジェクトが作成できたら実行環境のセットアップに戻ります。
実行環境のセットアップ
IDE から実行する場合
IntelliJ を使う場合であれば、先程作成したプロジェクトを IntelliJ で開き、下図のように右上で実行する環境を選択し、 Run を選択すれば実行できます。
結構簡単にそれぞれの環境で実行できました。
ちなみに、Debug で実行すれば、Dart のソースコード上でブレークポイント置けます。
このあたりの環境構築が簡単なのはとてもありがたいですね。
コマンドラインから実行する場合
コマンドラインで実行する場合はそれぞれの環境向けに設定が必要ですが、基本は `flutter run` コマンドを実行すればよいです。
シミュレータで実行する場合は先にシミュレータの方を起動しておく必要があります。
iOS のシミュレータであれば、open -a Simulator
コマンドでシミュレータを起動できます。
もし、実行環境の候補が複数ある場合は -d <deviceId>
オプションを付けることで、実行環境を指定できます。
1 2 |
$ flutter run -d "iPhone X" |
deviceId は、実際に実行環境が複数ある状態で flutter run
を実行すると下記のように候補が表示されますので、そこから選んで指定すれば OK です。
1 2 3 4 5 |
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices. ASUS Z017DA • G9AZCY070023P3A • android-arm64 • Android 7.0 (API 24) iPhone X • 89A028AA-F22B-49CA-8145-5EDD20F95A8A • ios • iOS 11.2 (simulator) |
iPhoneの実機で実行する場合は先に Xcode でプロジェクトファイルを開いて Development Team を選択するなどの手順が必要になります。公式ページに詳しくやり方が書いてあるので、参照してください。
個人的には IDE 使った方法の方が簡単かなと思いますので、以降は IntelliJ を使って開発進めていくことにします。
Flutter 試し書き
環境が整ったら Dart を書いてアプリを作っていきます。
公式にチュートリアル的な物があったので、やってみました。
チュートリアルなぞっただけですが、GitHubにもコード上げてます。
以下、補足と感想などが続きます。公式チュートリアルも合わせてご確認ください。
Flutter で Hello World
(公式の STEP1)
まずはお約束ですが、Flutter で Hello World してみたいと思います。
公式ドキュメントに従って、下記のコードを main.dart に記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { 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('Hello World'), ), ), ); } } |
実行すると画面中央に Hello World が表示されます。
画面のレイアウトも Dart で書いていく感じですかね。
- MaterialApp でマテリアルデザインのアプリケーション
void main() => runApp(new MyApp());
の=>
は一行で関数を定義する書き方らしい- UIのパーツは全て Widget と呼ばれるようです。Scafford でデフォルトのタイトルバーや本体を含んだ Widget が構築できるみたいです。
外部ライブラリを使ってみる
(公式の STEP2)
外部ライブラリの追加は pubspec.yaml という YAML 形式のファイルで行います。
このファイルは Node環境でいう package.json、Gradle 環境で言う build.gradle みたいなものかと思います。
pubspec.yaml の depenencies という項目に、english_words というライブラリを追加してみます。
1 2 3 4 5 |
dependencies: ... cupertino_icons: ^0.1.0 english_words: ^3.1.0 |
pubspec.yaml を開くと下図のように右上にヒントが表示されるので、Packages get を選択して必要なライブラリをダウンロードしてきます。
main.dart に戻って、main.dart を書き換えます。
公式ドキュメントそのままのコードなので Gist で貼り付けます。
差分としては、
- import で english_words パッケージの参照を追加
- wordPair でランダムな単語ペアを取得
- Text として取得した単語ペアを画面表示
という感じですね。
上記のように書き換えるわけですが、既にデバッグ実行中の場合、ファイル保存したタイミングで即座に反映されます。
また、右上のイナズマっぽいボタン(Flutter Hot Reload) でも更新されます。
即座に確認できて便利!!
リスト表示
(公式の STEP3, 4)
大概のツール系スマホアプリでは実装するであろう、リスト表示を試してみます。
main.dart 下記のように書き換えます。
Flutter の Widget は StatelessWidget と StatefulWidget の2種類があり、今回からは StatefulWidget を使っています。
StatefulWidget はユーザ操作などの要因で画面の表示内容が動的に変わる場合に使う Widget ですが、このサンプルで StatefulWidget にしたのは、次のサンプルでユーザのボタン操作を実現するためかと思います。リスト表示するだけであれば StatelessWidget のままでも行けそうでした。
リストの作成自体は _buildSuggestions()
が該当します。 itemBuilder で行のインデックスが渡ってくるので、それを見て各行の表示内容を決めます。
Dart 言語では _
始まりの関数・フィールドは、private になります。
このコードではリストの要素数は規定していないので、無限にスクロールできるリストになっています。
Android の ListView、Adapter や iOS の UITableView とか触ってると、これだけのコードでリスト表示ができるがとてもありがたいですね。
細かいですが、このリスト、実際に動かしてみると下図の用な感じになるんですが
Android では最上部に到達すると上に行けないことがわかるマーカー、iOS ではバウンスするという、各 OSでおなじみの UI 表示になります。
Native っぽい感じの UI になるように配慮されてるんですね。
ユーザのボタン操作
(公式のSTEP5)
さきほど作ったリストにボタンを配置してみます。下記のようにコード追加します。
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 |
class RandomWordsState extends State<RandomWords> { final _suggestions = <WordPair>[]; // 追加: ふぉぼったアイテムを保存するためのフィールド final _saved = new Set<WordPair>(); final _biggerFont = const TextStyle(fontSize: 18.0); ... 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); } }, ); }, // 追加ここまで ); } } |
- trailing でリストの各行の右端にアイコンを追加。ふぁぼっぽいやつ。
- onTap でタップ時の操作を追加。タップするとふぁぼが赤くなる。
onTap での setState により、RandomWordsState#build が再度呼び出されてUI が更新される仕組みです。
StatelessWidget な MyApp の build は再度呼び出されることはありません。
再び細かいのですが、Android Studio などではおなじみ
こんな感じでアイコンや色が IDE 上にも表示されます。やはり IDE サポートはありがたいですね。
画面遷移
(公式の STEP6)
タイトルバーの右上にアイコンを配置して、タップしたら別画面に遷移するという処理を追加します。
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 |
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(), ); } ... // 追加: タップ時の画面遷移 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), ); }, ), ); } // 追加ここまで } |
この画面遷移のサンプルでは、ふぁぼったテキストのみを表示するリストを遷移先で表示するというコードになっています。
この画面遷移は、遷移先の画面で 左上のナビゲーションボタンで戻れるようになっています。
テーマの変更
(公式の STEP7)
最後ですが、少しだけ見た目を変更してみます。theme で primaryColor を白に設定するというサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Startup Name Generator', theme: new ThemeData( primaryColor: Colors.white ), home: new RandomWords(), ); } } |
タイトルバーが白くなります。同時に、タイトルバーの文字も黒から白に変わります。
さいごに
Flutter、なかなか良い感じです。
Dart言語自体が Java と近い雰囲気もあって、スマホアプリを普段作っている人であれば割とすんなり入っていけるのではないでしょうか。
見た目が Native っぽくなるのも好きです。
気になった点としては、使えるライブラリが揃っているのかという点でしょうか。(主にUI以外の機能に関して)
実は JavaScript のライブラリが使えるのかなと思っていたのですが、Flutter 環境で呼び出す手段が無いように思っています。このあたりの方法、ご存じの方がいらっしゃいましたら教えていただけるとありがたいです。
作りたいアプリが Dart で提供されているライブラリで実現できそうであれば十分検討して良い環境かと思います。
2018/07/30 追記
Flutter でカメラ使うのとかどうやるのかなーと思って試してみました。
2018/07/30 追記ここまで
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする