アプリ開発初心者が Flutter に入門して 1 か月でアプリをリリースした話

## はじめに

最近 Flutter に入門しました。無事に一つアプリをリリースすることができたのですが、ネイティブアプリの開発が全くの素人で苦労した点があったので、同じく入門する初心者の方に記録として残していこうと思います。

また、事情があり家の PC でなかなか開発が進められないため社内の PC で業務終了後に開発を進める形になりました。社内環境では Proxy にネットワーク通信が阻まれてしまうため、よくある入門の記事通りに行かないことがとにかく多くあり・・・(というかむしろここが一番苦労したと思います。) 
同じく Proxy 環境での開発を余儀なくされている方の助けになれば幸いでございます。

なお、Flutter やその周辺等知識が甘い部分が多いと自負しているため誤りやアドバイス等あればコメントしていただけると幸いです。ただちに確認致します。

## リリースしたアプリの紹介

### BundleApps

こちらのアプリです ⇒ [BundleApps](https://play.google.com/store/apps/details?id=com.johnworks.BundleApps)

### BundleApps とは

インストールしてあるアプリを一覧に表示して使いたいアプリだけを選んで表示することができるアプリです。Web アプリだと[Station](https://getstation.com/)や[Stack](https://getstation.com/)など高機能なものがありますが、ネイティブアプリを管理するツールは現在ないのかなと思いました。（調査が足らないだけでもしかしたらあるかもしれない）比較するのもおこがましいほど簡素な作りですがまずは一つアプリをリリースしてみようと思った次第です。

本当は Firebase とバリバリ連携するアプリを作りたかったのですが後述する Proxy の弊害のせいでうまくいかなかったため、ローカルでこじんまりとデータを管理するつくりになっています・・・宜しければインストールして使ってみてください。

## 開発の際に参考にした書籍・サイトなど

### 書籍

下記二冊を購入しました。

- [Flutter モバイルアプリ開発バイブル](https://www.amazon.co.jp/Flutter-%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E3%83%90%E3%82%A4%E3%83%96%E3%83%AB-%E5%8D%97%E9%87%8C%E5%8B%87%E6%B0%97/dp/4839970874/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=flutter&qid=1576626530&sr=8-1)

  ⇒ 全くの初心者がガンガン読み進めていくのは難しいかなあという印象を受けました。アプリ開発の知見があって Flutter だとどのように書いていくかを知りたい人向けみたいな。まだ読み終えてません。

- [Flutter×Firebase で始めるモバイルアプリ開発 (技術の泉シリーズ（NextPublishing）) Kindle 版](https://www.amazon.co.jp/Flutter%C3%97Firebase%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA-%E6%8A%80%E8%A1%93%E3%81%AE%E6%B3%89%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E4%B8%8B%E7%95%91-%E7%BF%94-ebook/dp/B07LBPRHQD/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=flutter&qid=1576626530&sr=8-4)

  ⇒ 情報が古いからか、Proxy のせいかもわかりませんが Firebase 連携の部分から動かなくて躓きました・・・説明はわりと丁寧かなと思うのですが Flutter は新しめで更新も早いので環境によっては動かないという人も多いと思います。

### サイト

色々なところで言われていますが、入門より少し込み入ったことをやろうとすると英語のリソースが多めだと感じました。これ以外は直接パッケージの GitHub リポジトリを見に行ったり、やりたいことでキーワード検索をしたりして開発を進めていきました。

- ★ [Flutter 公式](https://flutter.dev/?gclid=CjwKCAiAluLvBRASEiwAAbX3Ge-7PYsVZDMd62aI4Xrr8d68YUKneYhGS_bxHT7VpVX0VXSZC4gTvhoC9tEQAvD_BwE)
  ⇒ 公式が素晴らしいです。英語ですが確かに動きますし、チュートリアルが充実しています。環境構築からサンプルアプリの作成まで解説されているのでこれをみておけば入門としては間違いないと思います。

- ★ [Flutter はじめの一歩](https://medium.com/flutter-jp/first-step-9b7f2c74fb08)

    ⇒ Flutter で自動生成される初期サンプルを非常に丁寧に説明されています。初心者への配慮があり、理解の助けになりました。これを読んでから圧倒的に開発のスピードが上がった気がします。

- [Udacity](https://www.udacity.com/)

    ⇒ まだ見始めた段階ですが、字幕付きの動画で電車などで学習を進める際に活用しています。

## 開発期間内訳

### 【1~2 週目】 Proxy 設定との戦い

#### 問題発生

冒頭で申し上げた通り Proxy に阻まれ苦労しました・・・

Android Studio に抵抗があり、Visual Studio Code で開発を進めようと思い環境構築を行ったのですがシミュレータとの接続がうまくいかない。初期サンプルアプリが立ち上がらない。

Android Studio やコマンドラインからの flutter run だと無事に起動するのでこれは Visual Studio Code の問題だと判断したのですが検索しても有用な情報がなかなか見つからず悩みに悩んでいました。

#### 光明差す?

Proxy 関連の設定だとあたりを付けてよくよく考えてみるとエミュレータと Visual Studio Code 間の通信はローカル通信ではないか？つまり逆に Proxy 設定ありで通信しようとしているからダメなのではないか？と考えて Visual Studio Code の no proxy 設定を検索しだします。

しかし、探せど探せどそれっぽい情報は見つからず・・・
付けるのは Setting.json の編集でいけるのにそこに no proxy の設定が見つかりませんでした。

#### 解決?

結論から言うと Visual Studio Code から proxy の設定を外すことで解決しました。

Visual Studio Code では setting.json に Proxy 設定を記入するとそちらを優先してしまうようなのですが、消した場合はシステム環境変数のほうを参照するような仕組みになっているようです。元々環境変数に

| 変数        | 値                          |
| ----------- | --------------------------- |
| http_proxy  | http://proxy-hogehoge.co.jp |
| https_proxy | http://proxy-hogehoge.co.jp |
| no_proxy    | localhost,127.0.0.1         |

といった記載があったため、そちらを参照してくれるようになり無事にサンプルアプリが動作するようになりました！

#### さらば Firebase

サンプルアプリは立ち上がったものの書籍通りに進めても Build に成功はするものの Firestore から一切データが取得できないという事態に陥りました。

いくつもサンプルを写経して試したのにダメだったのでこれもやはり Proxy の弊害なのかなと。

アプリ側に Proxy を超えて通信する設定をする必要があるのかと考え調べたのですが良い情報が見つからず、結果一旦 Firebase との連携は諦めてできる範囲でアプリ開発をすることとしました。

※良い情報をお持ちの方がいらっしゃいましたら教えていただけるとありがたいです・・・

### アプリ制作開始【3 週目】

#### 躓いた点 ①

チュートリアルに毛が生えた程度のアプリなので、基本的には書籍や解説サイトを参考にしあげることができました。

ただそれでもいくつか躓いた点がありまして・・・まず Dart の非同期処理である async / await に関して理解が甘く、想定通りの動作にならず苦労しました。

具体的には

```
.
.
.
    //アプリ起動時に一度だけ実行される
    @override
    void initState() {
        super.initState();
        .
        .
        .
    }
.
.
.
```

というウィジェット作成のタイミングで処理を行うことができる部分で、アプリ一覧を取得 ⇒ 表示という処理を行おうと思ったのですが

```
.
.
.
    // ローカルからアプリケーションのリストを取得する処理
    _getLocalData() {
        SharedPreferences pref = await SharedPreferences.getInstance();
        List<String> apk = pref.getStringList("apk") ?? new List<String>();
    }

    // アプリケーション一覧を取得する処理
    _createAppList() async {
        .
        .
        //ローカルの情報を元にアプリ情報を取得、リストに追加
        apk.forEach((pn) async {
          ApplicationWithIcon app = await DeviceApps.getApp(pn, true);
          setState(() {
            _iconApps.add(app);
          });
        });
    }

    //アプリ起動時に一度だけ実行される
    @override
    void initState() {
        super.initState();
        _getLocalData();
        _createAppList();
        .
        .
    }
.
.
.
```

と書いたところ一向にアプリケーションが格納されているはずのリストが空で半日程度悩んでいました。

結論として、

```
.
.
.
    // アプリケーション一覧を取得する処理
    createAppList() async {

        // ここを中にいれる！
        // ローカルからアプリケーションのリストを取得する処理
        SharedPreferences pref = await SharedPreferences.getInstance();
        List<String> apk = pref.getStringList("apk") ?? new List<String>();

        //ローカルの情報を元にアプリ情報を取得、リストに追加
        apk.forEach((pn) async {
          ApplicationWithIcon app = await DeviceApps.getApp(pn, true);
          setState(() {
            _iconApps.add(app);
          });
        });
    }

    //アプリ起動時に一度だけ実行される
    @override
    void initState() {
        super.initState();
        _createAppList();
        .
        .
    }
.
.
.
```

このようにしたら無事にアプリのリストが取得できるようなりました。
認識に誤りがあったら訂正していただきたいのですが、async の内側で処理の完了を待つひとくくりということでしょうか？ そもそも非同期処理の理解を深める必要があるなあと痛感した事例です・・・

#### 躓いた点 ②

package についてです。

[device_apps](https://github.com/g123k/flutter_plugin_device_apps)という package を利用させていただいているのですが、README.md には

```
Image.memory(app.icon);
```

でアプリのアイコンが取得できると書いてあるのにそのように書くと icon などという情報はないとエラーが出てしまい悩みました。

これも半日程度悩み、アイコン出せないんじゃ・・・と諦めかけたのですが GitHub ソースを読み issue を参照した結果何とか解決することができました。

issue には

' Cannot call "icon" method #20 '

とまさに問題の issue があり、その回答が

'you need to cast to ApplicationWithIcon to use.'

となっていて、なるほど！とソースを読みだした次第です。

```
.
.

  static Future<Application> getApp(String packageName,
      [bool includeAppIcon = false]) async {
    if (packageName.isEmpty) {
      throw Exception('The package name can not be empty');
    }

    return _channel.invokeMethod('getApp', {
      'package_name': packageName,
      'include_app_icon': includeAppIcon
    }).then((app) {
      if (app != null && app is Map) {
        return Application(app);
      } else {
        return null;
      }
    }).catchError((err) {
      print(err);
      return null;
    });
  }

.
.
.

class ApplicationWithIcon extends Application {
  final String _icon;

  ApplicationWithIcon._fromMap(Map map)
      : assert(map['app_icon'] != null),
        _icon = map['app_icon'],
        super._fromMap(map);

  get icon => base64.decode(_icon);
}
```

となっていて、includeAppIcon のフラグを立てたうえでキャストしなきゃいけないのかな？と気づくことができました。

開発経験も浅く、Package のソースを見るということをしてこなかったのですが良い解決策を得ることができると学べたので今後は積極的にソースを参照する癖を付けたいと思います。

### 結論と今後の展望

- Flutter(Dart)の仕様理解が甘い部分が多いので一旦書籍や動画で理解を深めることから始めたいと思います。
- コードを読むと得られるモノが多かったので Package を利用したい場合はその内側まで踏み込んで利用したいと思います。

今後は付き合いのある店舗のポイントカードアプリを作成してみようかと考えているのですが、流石に高機能になるのかなと・・・

Firebase との連携も必要でしょうし、おすすめのリソースがあればコメントいただけるとありがたいです。特に切実に Proxy 関連（　＾ ω ＾）・・・

### 最後に

ここまで読んでいただきありがとうございました！

よろしければアプリを使ってみてもらえたら嬉しいです。
[BundleApps](https://play.google.com/store/apps/details?id=com.johnworks.BundleApps)