maesblog

Flutterの開発環境をMac + Android Studio + Xcode + VS Codeで構築する

2018年12月4日にFlutterの初のメジャーバージョンとなる「Flutter 1.0」がリリースされました。FlutterはiOSとAndroidのネイティブアプリを開発するためのオープンソースのSDKです。Flutterにはプログラミング言語の「Dart」が含まれていて、Flutterでの開発もDartを使用します。Flutter、Dart共にGoogleが中心となって開発しています。私の周りでは意外と評判が良いので、ちょうどv1.0がリリースされたところだし、どんなものか試してみることにしました。まずは開発環境を構築して、デモアプリをシミュレーターで表示するところまで試してみました。内容をまとめたので、ぜひ参考にしてみてください。

はじめに

今回の記事では、Flutterの開発環境の構築から、実際にプロジェクトを作成し、デモアプリを表示するまでの方法を紹介します。ツールとしては、Mac(MacOS Mojave 10.14)とVS Code(v1.29.1)を使用します。内容に関しては、やはり公式ドキュメントの方が詳しく書いてあると思いますので、当記事は参考程度に見てもらい、わかりにく部分やもっと詳細を知りたい部分は公式ドキュメントを見ていただくのが良いかと思います。

Flutter SDKをインストールする

まずは、FlutterのサイトからFlutter SDKをダウンロードします。SDKはzipファイルになっていて、Windows版、Mac版、Linux版が用意されています。ちなみに、以下はMac版のものです。

SDKをダウンロードしたら、Flutter SDKの置き場所を決めます(置き場所は任意です)。置き場所が決まったら、そこに移動して、ダウンロードしたFlutter SDKを解凍(unzip)します。

$ cd ~/Sites
$ unzip ~/Downloads/flutter_macos_v1.0.0-stable.zip

PATHを登録する

次に、flutterコマンドをどこからでも使えるように、PATHの登録を行います。$HOME/.bash_profileファイルをviなどで開いて(もしくは作成して)、以下を追記します(上記でFlutterを解凍したディレクトリのパスを指定します)。

export PATH=/Users/maechabin/Slites/flutter/bin:$PATH

PATHの登録を行ったら、sourceコマンドで現在の画面に反映させます。

$ source $HOME/.bash_profile

以下のコマンドを実行すると、flutter/binディレクトリが現在のPATHにあることを確認することができます。

$ echo $PATH

また以下のコマンドでバージョンが表示されれば、インストールとPATHが通ったということが確認できます。

$ flutter --version
Flutter 1.0.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 5391447fae (2 weeks ago) • 2018-11-29 19:41:26 -0800
Engine • revision 7375a0f414
Tools • Dart 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

これでFlutterが使えるようになりました。

設定に必要なツールを確認する

Flutterをインストールしたら、Flutterアプリを開発するための設定を行っていきます。その際にflutter doctorを実行すると、設定に必要な依存関係をチェックしてくれます。何をすべきかを詳しく教えてくれるので大変便利です。

flutter doctorコマンドを実行します。

$ flutter doctor

以下のようなチェック結果が表示されます。Andoriod StudioもXcodeもインストールしていない環境で実行したので、Android toolchainやiOS toolchainが「✗」と出ました。

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.0.0, on Mac OS X 10.12.6 16G29, locale ja-JP)
[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.io/setup/#android-setup for detailed instructions).
      If Android SDK has been installed to a custom location, set $ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

[✗] iOS toolchain - develop for iOS devices
    ✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
      Download at: https://developer.apple.com/xcode/download/
      Or install Xcode via the App Store.
      Once installed, run:
        sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
    ✗ libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:
        brew update
        brew install --HEAD usbmuxd
        brew link usbmuxd
        brew install --HEAD libimobiledevice
        brew install ideviceinstaller
    ✗ ios-deploy not installed. To install with Brew:
        brew install ios-deploy
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To install:
        brew install cocoapods
        pod setup
[!] Android Studio (not installed)
[!] IntelliJ IDEA Community Edition (version 2018.1.6)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] VS Code (version 1.29.1)
[!] Connected device
    ! No devices available

! Doctor found issues in 5 categories.

必要な操作をコマンドベースで教えてくれるのがとても良いですね。さらに詳しい情報で結果を知りたい場合は、flutter doctor -vを実行します。

Android Studioの設定

FlutterでAndroidアプリを作成する場合は、以下のAndroid Studioの設定が必要です。Android StudioはAndroidアプリを開発するための統合開発環境です。

Android Studioをインストールする

Android Studioをインストールするには、以下よりdmgファイルをダウンロードしてインストールします(私がダウンロードしたdmgファイルは988MBありました)。

dmgファイルをダウンロードしたら、ファイルを開いてインストールします。この時点でflutter docterを実行すると、「Android licenses not accepted. To resolve this, run: flutter doctor –android-licenses」と表示されます。以下のコマンドを実行してライセンスに同意します。

$ flutter doctor --android-licenses

Flutter、Dartプラグインをインストールする

さらに、この時点でflutter docterを実行すると、以下が表示されます。FlutterプラグインとDartプラグインがAndroid Studioにインストールされていないという内容です。

✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.

Android Studioを開いてこれらのプラグインをインストールします。Android Studioを起動すると以下のスタート画面が表示されます。画面右下から「Plugins」を選択します。

Android Studio: プラグインを選択

Plugins画面の検索フォームに「Flutter」と入力して検索ボタンを押します。Flutterという文字列を含むプラグインがAndroid Studioにインストールされていれば検索結果として表示されます。新たにプラグインをインストールする場合は、「Search in repositories」をクリックして外部のリポジトリから検索します。

Android Studio: プラグインの検索

外部のリポジトリにあるFlutterという文字列を含むプラグインが検索結果として表示されます。検索結果の「Flutter」を選択し、画面右側の「Install」ボタンを押すとFlutterプラグインがインストールされます。

Android Studio: Flutterプラグインのインストール

Flutterプラグインをインストールする時に、依存関係も一緒にインストールするか聞かれるので、「OK」を選択すると、同時にDartプラグインもインストールされます。プラグインをインストールしたら、Android Studioを「restart」するとプラグインが反映されます。

仮想端末を作成する

Androidは数多くの端末が存在しています。それらの端末を使ってアプリの挙動を検証できるように、Android Studio上でさまざまな端末を仮想端末として定義できるようになっています。Flutterプラグインをインストールすると、Flutterプロジェクトを作成できるようになります。何でも良いので、Flutterプロジェクトの作成画面を開くと、仮想端末を作成できるようになっています。上記のメニューから「Tools > AVD Manager」と選択すると、仮想端末の作成画面が表示されます。

仮想端末は直感的に作ることができるかと思いますが、詳細については以下のAndroid Studioの公式ドキュメントをご参照ください。 

Xcodeの設定

FlutterでiOSアプリを作成する場合は、Xcodeの設定が必要です。Xcodeは、iOSアプリをはじめとする各ソフトウェアを開発するためのアップルの統合開発環境です。

Xcodeをインストールする

Xcodeをインストールするには、App Storeまたは公式サイトからダウンロードしてインストールします(時間がかかります)。なお、Xcodeはv9.0以降のものが必要です。

Xcodeのインストールが完了したら、Xcodeを起動してライセンスに同意します。そうすると、コンポーネントのインストールが始まります(これも少々時間がかかります)。 または以下のコマンドでもライセンスに同意することもできます。

$ sudo xcodebuild -license

それから、以下のコマンドを実行して、インストールしたXcodeのバージョンを使用するようにコマンドラインツールを設定します。

$ sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

iOSのシミュレータが起動するかは、以下のコマンドで確認することができます。

$ open -a Simulator

iOSデバイスにデプロイするための設定

こちらの設定はデプロイするための設定なので、デモアプリを確認するだけであれば最初は必要ありませんが、一応紹介しておきます。

Homebrewをインストールし、Homebrewを最新の状態に更新します。

$ brew update

Homebrewを更新したら、以下のコマンドを上からひとつずつ実行して、FlutterアプリケーションをiOSデバイスにデプロイするためのツールをインストールします(pod setupが時間かかります)。

$ brew install --HEAD usbmuxd
$ brew link usbmuxd
$ brew install --HEAD libimobiledevice
$ brew install ideviceinstaller ios-deploy cocoapods
$ pod setup

Flutterプロジェクトを作成する

Flutterプロジェクトを作成します。flutter createコマンドの後ろに「プロジェクト名(パッケージ名)」を指定してコマンドを実行するとプロジェクトが作成されます。 

$ flutter create flutter_sample

なお、プロジェクト名は、「小文字」「アンダースコア」のみ許容されています。ハイフンは使えないので注意が必要です。

プロジェクトの作成が完了すると、以下のようなメッセージが表示されます。

All done!
[✓] Flutter is fully installed. (Channel stable, v1.0.0, on Mac OS X 10.14 18A391, locale ja-JP)
[✗] Android toolchain - develop for Android devices is not installed.
[✓] iOS toolchain - develop for iOS devices is fully installed. (Xcode 10.1)
[!] Android Studio is not available. (not installed)
[✓] VS Code is fully installed. (version 1.29.1)
[!] Connected device is not available.

Flutterアプリを起動する

プロジェクトが作成されると、その中にはMaterial Componentsを使ったシンプルなデモが含まれています。さっそくプロジェクトディレクトリに移動して、flutter runコマンドでそのデモを起動してみましょう。

$ cd flutter_sample
$ flutter run

なお、flutter runを実行する際に、あらかじめAndroidかiOSのシミュレータを起動しておく必要があります。シミュレータを起動せずに、flutter runを実行すると、「No connected devices.」と怒られます。

VS Codeからシミュレータを起動する

VS Codeに機能拡張「flutter」をインストールしておくと、VS Codeからシミュレータを起動することができるようになります。

shift + command + pでコマンドパレットを開き、flutter: Launch Emulatorコマンドを実行すると、以下のようにシミュレータが選択できるようになっています。任意のシミュレータを選択すると起動します。

VS Code: flutter: Launch Emulator

シミュレータを起動した状態で、flutter runコマンドを実行すると以下のようにシミュレータにFlutterアプリが反映されます。

 シミュレータにFlutterアプリを反映させる

VS Codeでソースコードを確認する

Flutterアプリをシミュレータで確認できたので、今度は実際にソースコードをVS Codeで開いて確認してみます。flutter_sampleディレクトリ内で以下のコマンドを実行して、VS Codeで起動します。

$ code .

上記でも紹介しましたが、FlutterアプリをVS Codeで扱う場合は、機能拡張「flutter」をインストールしておくと便利です。Dartにも対応し、さらにFlutterアプリのデバッグなどもできるようになります。

デモのソースコードは以下のようになっています。詳細はコメントを参照してください。 

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // このウィジェットはアプリケーションのルートです。
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // これはアプリケーションのテーマです。
        //
        // "flutter run"でアプリケーションを起動してみましょう。
        // 青いツールバーを持ったアプリが開きます。
        // それから、アプリを終了することなく、
        // 下記のprimarySwatchをColors.greenに変更してみると、
        // "hot reload"が呼び出されます("flutter run"を起動したコンソールで"r"を押すか、
        // Flutter IDE内の"hot reload"に対して変更を保存するかだけでもOKです)。
        // カウンターはゼロにリセットされていないということに注目してください。
        // アプリケーションは再起動されていません。
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // このウィジェットはアプリケーションのホームページです。ステートフルであり、
  // (以下で定義されている)Stateオブジェクトを持っていることを意味しています。
  // このStateオブジェクトには、見た目に影響を及ぼすフィールドを含んでいます。

  // このクラスはstateのための設定を行います。親によって提供され(今回のケースではtitle)、
  // Stateのbuildメソッドによって使われる(今回のケースではApp widget)値を保持します。
  // Widgetサブクラスのフィールドは常に"final"でマークされます。
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      // このsetStateの呼び出しは、FlutterフレームワークにこのStateの中で
      // 何かが変更されたということを伝えます。
      // これにより、以下のbuildメソッドがreturnされ、更新された値が反映されます。
      // もし、setState()を呼び出さずに、_counterを変更したとしたら、
      // buildメソッドは再び呼ばれることはなく、何も起こらないでしょう。
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // このメソッドはsetStateが呼ばれるたびにreturnされます。
    // たとえば、上記の_incrementCounterメソッドによってreturnされるように。
    //
    // Flutterフレームワークはbuildメソッドが速くreturnするように最適化してきました。
    // そのためWidgetのインスタンスを個別に変更する必要はなく、
    // 更新が必要なものだけを再buildできます。
    return Scaffold(
      appBar: AppBar(
        // ここではApp.buildメソッドによって作成されたMyHomePageオブジェクトから値を取得し、
        // appbarのtitleを設定するために使用します。
        title: Text(widget.title),
      ),
      body: Center(
        // Centerはレイアウト用のwidgetです。単一の子を取り、親の中央に配置します。
        child: Column(
          // Columnもまたレイアウト用のwidgetです。子のリストを取り、それらを垂直に並べます。
          // デフォルトでは、子のサイズに合わせてサイズが変更され、親の高さになります。
          //
          // 各ウィジェットのワイヤフレームを表示するために"debug painting"を起動します。
          //(コンソールで"p"を押すか、
          // Android StudioであればFlutterインスペクターから
          // "Toggle Debug Paint"アクションを選択するか、
          // Visual Studio Codeであれば"Toggle Debug Paint"コマンドを実行します。)
          //
          // Columnは、自分自身の大きさや子の配置方法を制御するための様々なプロパティを持ちます。
          // ここでは、子を垂直方向にセンタリングするためにmainAxisAlignmentを使用しています。
          // ここでの主軸は垂直軸となります。それは列が垂直(交差軸は水平)であるからです。
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // この末尾のカンマは、buildメソッドのための自動フォーマットをより良くします。
    );
  }
}
main.dart

Widgetのテストコードは以下のようになっています。ファイルはtestディレクトリ内にあります。こちらも詳細は、ソースコード内のコメントを見てください。

// これは基本的なwidgetのテストです。
//
// テストでウィジェットとのインタラクションを実行するために、
// Flutterが提供するWidgetTesterユーティリティを使用します。
// たとえば、タップとスクロールのジェスチャを送信することなどができます。
// さらに、WidgetTesterを使用して、widgetのツリー内の子widgetを検索し、
// テキストを読み取り、widgetのプロパティの値が正しいことを検証することもできます。

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:flutter_sample/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // アプリをbuildしてフレームをトリガーします。
    await tester.pumpWidget(MyApp());

    // カウンターが0からスタートするか検証します。
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // '+'アイコンをタップし、フレームをトリガーします。
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // カウンターが増えるか検証します。
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}
widget_test.dart

ちなみに、テストの実行はflutter testコマンドを使います。以下のようにファイルを指定してもOKです。

$ flutter test test/counter_test.dart

Flutterのユニットテストについての公式ドキュメントは以下となります。詳細はこちらをご参照ください。

Dartの構文紹介

上記のデモはDartで書かれています。私にとって現時点でDartはまだそこまで馴染みのないプログラミング言語です。上記のデモのソースコード内で調べておく必要があるなと思った構文をピックアップしました。以下に紹介します。

final修飾子

変更が必要のない変数につける修飾子です。final修飾子をつけた変数は、初期化時にのみ1回だけ代入でき、それ以降は変更できなくなります。varの代わりとなり、final修飾子の後に型注釈をつけるのはOKです。

final name = 'Bob'; // 型注釈なし
final String nickname = 'Bobby';

name = 'Alice'; // => Error
nickname = 'Allie' // => Error

上記のデモのソースコードの中でステートフルWidgetとして定義しているMyHomePageクラスで、コンストラクタで受け取った値を受け取る変数にfinal修飾子をつけています。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

@overrideアノテーション

サブクラスで、インスタンスメソッド、ゲッター、セッターをオーバーライドすることができます。@overrideアノテーションは、メンバーを意図的にオーバーライドしていることを示すために使用します。

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

上記のデモのソースコードの中では、build()関数のところなどで@overrideアノテーションが使われています。

@override
Widget build(BuildContext context) {
...
}

コンストラクタ関数

Dartのコンストラクタ関数は、クラス名と同じものを使用して定義します。

class Point {
  num x, y;

// コンストラクタ関数
  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

上記のデモのソースコードの中でステートフルWidgetとして定義しているMyHomePageクラスでコンストラクタ関数がMyHomePage({Key key, this.title})と定義されています。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

super()関数

extendsしたスーパークラスのコンストラクタ関数を呼び出す方法としてsuper()関数を使用します。使用方法としては、以下のようにサブクラスのコンストラクタ関数の後ろに:を挟んでsuper()関数を呼び出します。

abstract class Foo {
  String foo;
  Foo (String this.foo); // コンストラクタ関数
}

class Bar extends Foo {
  Bar() : super('foo') { // コンストラクタ関数
    print('bar');
  }
}

void main() {
  var bar = new Bar(); // => 'bar'.
  print(bar.foo); // => 'foo'.
}

上記のデモのソースコードでもMyHomePageクラスのコンストラクタ関数の定義のところでsuper()関数を使用しています。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

Optional named parameters

Dartでは、関数の引数を定義する時に{}または[]で囲うと任意の引数として定義することができます。{}で囲ったものがOptional named parametersとなります。

たとえば、以下のようにOptional named parametersを使って関数を定義した場合、引数名を明記することで関数の呼び出し時に第二引数にだけ値を渡せるようになります。

// 関数定義
void enableFlags({bool bold, bool hidden}) {...}

// 関数の呼び出し
enableFlags(hidden: false);

上記のデモのソースコード内にも以下のように引数名を明記した状態で引数が指定されている箇所が多く見られます。

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(...),
  ...
);

なお、[]で囲った場合は、Optional positional parametersと言って、こちらの場合は引数名を明記する必要はありませんが、引数の指定する順番を気にする必要があります。

まずはこの辺の構文を押さえておけば、デモのソースコードは理解できるんじゃないかと思います。これを皮切りに少しずつDartの書き方を覚えていければいいんじゃないかと思います。

まとめ

いかがでしょうか。Android Studio、Xcodeの設定などやることがたくさんありますが、これでFlutterアプリの開発環境をMacで構築できるようになるはずです。「はじめに」でも書きましたが、必要最小限のことしか書いていないので、詳細についてはFlutter公式のドキュメントを見てもらえると良いでしょう。

FlutterはDartで書く部分がネックとなりますが、2019年はFlutterがそこそこ来るのではないかと漠然と思っています。GitHubのスター数が現時点ですでに約46,000もあります。React Nativeが約72,000ほどです。後発の割にはだいぶ健闘しているのではと思っています。

私自身まだFlutterについてはよく理解していないので、これから少しずつ覚えていこうと思っています。次は実際に自分でToDoアプリなどのサンプルを作って、理解を深めたいと思っています。

ありがたいことにFlutterの入門書も出版されています。最後に紹介しておきます。

Android/iOSクロス開発フレームワーク Flutter入門Android/iOSクロス開発フレームワーク Flutter入門
  • 『Android/iOSクロス開発フレームワーク Flutter入門』
  • 著者: 掌田津耶乃
  • 出版社: 秀和システム
  • 発売日: 2018年9月14日

関連記事

コメント

  • 必須

コメント