Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

こんなときどうする?Flutter でモバイルアプリ開発

こんにちは。中山です。

今年度の Google I/O 2018 で beta 3 バージョンが発表された Flutter
flutter.io

今年 6月には、もう Release Preview 1 が発表されていました。

なにかとホットな Flutter でありますが、従来の Android、iOS のネイティブアプリ開発者が Flutter にチャレンジする場合に、
「ここはどうやって実現するのだろうか?」
と疑問に思うであろう箇所を中心に、いくつか具体的な事例を交えて見ていきたいと思います。


Flutter 自体の導入と、実際に開発を始められるようにするまでの手順については、公式の情報をご参照ください。
また Codelab などもあり、はじめて Flutter でアプリ開発する、というときのための材料は、いろいろ用意されているようです。

UI の開発

画面のレイアウト、UI を作っていくには、Android ネイティブアプリですと、(コードで直接編集する方法もありますが)レイアウト定義の xml ファイルをいじったり、また iOS の場合は Storyboard を編集したりしていました。

Flutter の場合は、画面開発はすべて、UI を構築するためにあらかじめ用意されている、 Widget と呼ばれる部品を組み合わせて行う、ということのようです。
たとえば、以下は Codelab から抜粋したものですが、ここに登場する「Container」「Row」「TextField」「IconButton」などがいずれも Widget にあたります。

return new IconTheme(
  data: new IconThemeData(color: Theme.of(context).accentColor),
  child: new Container(
    margin: const EdgeInsets.symmetric(horizontal: 8.0),
    child: new Row(
      children: <Widget>[
        new Flexible(child: new TextField(
          controller: _textController,
          onSubmitted: _handleSubmitted,
          decoration: new InputDecoration.collapsed(hintText: "Send a message")
        )
      ),
      new Container(
        margin: new EdgeInsets.symmetric(horizontal: 4.0),
        child: new IconButton(icon: new Icon(Icons.send),
          onPressed: () => _handleSubmitted(_textController.text)
        )
      ) ],
    )
  )
);

これを組み合わせて、画面上の部品の配置や、ユーザが操作した時のアクションなどを、Dart 言語で、プログラムとして直接記述しています。

Android 端末(エミュレータ)でこのアプリを実行させてみますと、以下のような画面になって表示されます。(上記のコードは、正確には、画面下部の「 Send a message 」と表示されている部分のテキスト入力欄とボタンの部分が該当します。)

 f:id:IntelligentTechnology:20180725173930p:plain:w320

iOS 端末(シミュレータ)では、以下のように表示されます。

 f:id:IntelligentTechnology:20180725174515p:plain:w320

従来の Android や iOS のネイティブアプリ開発であっても、最初から Storyboard やレイアウト定義ファイルは使わずに、直接、コードで UI レイアウトを書いていく、という場合もあると思いますので、このように、コードで UI レイアウトを構築する、ということに関しては、そんなにハードルは高くないかなと思いました。

それよりも印象が大きかったのは、ひとつコードを書くだけで、Android 、iOS どちらでもすぐ動くものが作れるというのは、思った以上に生産性が高い、ということでした。
かつ、普段使い慣れている Android Studio がそのまま使える、というのも大きなポイントです。Dart 言語自体も、あまりクセがなく、わりと直感的に取り扱えると感じました。
(しかし、iOS でも動かそうと思いますと、裏で Xcode の機能を呼び出してたり、Xcode 付属のシミュレータを使ったりしますので、結局、Mac での開発になってしまいますね。)

こんな Widget が使える

Flutter で用意されている Widget は、 Widgets Catalog のページで確認することができます。
基本的には、Android のマテリアルデザインに準じた部品が用意されているようですが、

 f:id:IntelligentTechnology:20180726095541p:plain:w480

iOS っぽいデザインのものもあるようです。(しかしたとえば、OS が iOS だったらこの Widget に切り替える、ということができるのかどうか、というのは未検証です。)

 f:id:IntelligentTechnology:20180726095815p:plain:w480

Widget が用意されていないときは

Widgets Catalog を見ますと、基本的な部品はひととおり用意されているようなのですが、少し凝ったことをやろうとすると、ここにある Widget だけでは対応できないパターンが出てきます。
たとえば、カメラのプレビューを表示したり、動画を再生したり、スマートフォンのセンサーを利用する処理を実装したり。
このような場合には、Flutter の PluginPackage とも呼ばれているようです)を利用する、という方法があります。

Flutter 公式の Plugin( Package )のリストはこちら
github.com

これを見ますと、カメラ、動画プレイヤー、センサーなど、すでに主な機能については提供されていることがわかります。

 f:id:IntelligentTechnology:20180726110022p:plain:w280

また、Firebase と連携するため、であろうと思われる Plugin( Package )もいくつか見つかります。(これはまた別の機会に試したいところ。)

 f:id:IntelligentTechnology:20180726110356p:plain:w280

このような、公式の Plugin( Package )を利用する方法のほか、サードパーティ製のものがあればそれも利用したり、自身で独自の Plugin( Package )を作成したり、というのも可能であるようです。
flutter.io
flutter.io

「 camera 」Plugin( Package )を使ってみる

ここでは例として、「 camera 」Plugin( Package )を使ってみます。
github.com

なお今回は、Flutter の、古い beta 版をインストールしていた環境であったため、事前に以下をターミナルから実行して、Flutter 自体を最新化しました。

# 更新ファイル取得先を master チャンネルに変更
$ flutter channel master
# Flutter をアップグレード
$ flutter upgrade

また、iOS 用にアプリをビルドする際、Plugin( Package )のインストールに Cocoapods を利用する、とのことでしたので、以下をターミナルから実行しました。

# Cocoapods を Homebrew でインストール
$ brew install cocoapods
# Cocoapods の初期設定
$ pod setup

最初に、Android Studio から、新しく Flutter プロジェクトを作成して、プロジェクトディレクトリ内にある、「pubspec.yaml」を開きます。
「dependencies:」と記載のある部分の下に「camera: ^0.2.1」と追記して、

dependencies:
  flutter:
    sdk: flutter
  camera: ^0.2.1

画面右上に表示されている「Packages get」をクリックします。

 f:id:IntelligentTechnology:20180726144902p:plain

その後、端末のカメラ機能を利用するためのパーミッションなどの設定を行います。
プロジェクトディレクトリ内の「ios/Runner/info.plist」ファイルを開いて、「<dict>」タグ内に以下を追記します。

<dict>
  ・・・
  <key>NSCameraUsageDescription</key>
  <string>Can I use the camera please?</string>
  <key>NSMicrophoneUsageDescription</key>
  <string>Can I use the mic please?</string>
  <key>UIBackgroundModes</key>
  <array>
      <string>fetch</string>
      <string>remote-notification</string>
  </array>
</dict>

また、プロジェクトディレクトリ内の「android/app/build.gradle」ファイルを開いて、「minSdkVersion」を 21 以上に設定します。
(このあたりは Android、iOS 個別に設定しないといけないんですね。)

android {
    ・・・

    defaultConfig {
        ・・・
        minSdkVersion 21

その後、「 camera 」Plugin( Package )の「 README.md 」ページにある Example コードを、プロジェクトディレクトリ内の「lib/main.dart」ファイルに記載します。

これを実行しますと、
Android のエミュレータで、カメラプレビューを表示することができました。

 f:id:IntelligentTechnology:20180726164811p:plain:w320

iOS のシミュレータは、残念ながらカメラ機能が使えないので、アプリのビルドはできたのですが、実行時にエラーとなってしまいました。とりあえず、起動できる、というところまで確認できています。

 f:id:IntelligentTechnology:20180727204051p:plain:w320

開発スピードというメリット

Flutter、最初の頃は、結局、細かいところになると、それぞれの OS 独自の実装が必要になるのではないかと思い、ほんとに簡易的な用途にだけ使えるのかな、などと考えていました。
しかし実際には、思った以上に Plugin( Package )が充実してきているようで、これは実は十分使えるのではないかと思ってきたのが、ここ最近の感想です。
このあたり、OS や端末に依存するような機能を、Plugin( Package )である程度まかなえるとしますと、とにかく、1回書いたら Android でも iOS でもすぐ動く、というメリットが、大きく効いてくるかなと考えます。
もちろん、これからじっくり付き合っていくとなると、いろいろ気になる部分も出てくると思いますが、ちょっと今後に期待してもいいのかな、と思わされるプロダクトでした。