ロコガイド テックブログ

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの社員がいろいろな記事を書いています。

「地域のくらしを、かしこく、たのしく」する、株式会社ロコガイドの社員がいろいろな記事を書いています。

Androidエンジニアがサービスのプロト開発にFlutterを採用したら最高だった話

f:id:yokomii:20190404171026p:plain

こんにちは、技術部の横山です。
普段はトクバイアプリのAndroid版の開発をしているモバイルエンジニアです。

今回はトクバイ技術部内で取り組んでいる「部活」制度についてと、
その中で経験した「Flutter」を用いた、モバイルアプリのプロトタイプ開発について紹介させて頂きます!

部活とは

社員の技術力向上を目的とした
通常業務では手の届かない領域や、専門分野外の開発に果敢にチャレンジする取り組みです💪

大体週1回ペースでお酒🍺を飲んだりピザを食べながらワイワイ🙌チーム開発をしています。
(飲食費用は会社負担となっております✨✨)

f:id:yokomii:20190404171336j:plain こないだはマック食べた

私が部長を務めている「プロト部」では、トクバイに新しいサービス価値を生み出すことを目標に
イケてる新プロダクトのプロト開発を粛々とおこなっています🙇‍

現在は「Flutter」フレームワークを用いて、
お店の人向けの投稿アプリ(と、それに伴うWebAPI)のプロトを開発しています。

なぜFlutter?

f:id:yokomii:20190404172109p:plain Flutter

  • Google製のモバイルアプリ開発用のフレームワークです。
  • いわゆるクロスプラットフォーム開発用のフレームワークで、 単一のコードでiOS&Androidの両OSに対応したアプリを作ることができます
  • 開発言語はDartというオブジェクト指向の言語です

今回、ネイティブアプリ開発ではなくFlutterを採用するに至った動機は以下の通りです。

1. 短い時間で効率よく開発できそう

通常業務ほど開発に時間をかけられないので、なるべく効率的に開発をおこなうために、
クロスプラットフォーム開発に挑戦することにしました。

将来的にサービスを正式運用したときのメンテコストを抑えたい狙いもあります。

Webフロント開発と親和性が高い

  • Widgets tree構造がHTML DOMに類似している
  • ReactiveフレームワークなのでWebフロントエンジニアは親しみやすい

などの特徴から、モバイルアプリ開発の経験がなくとも、
Webフロントの経験があれば参画しやすいと考えました。

3. Google I/Oの発表とかで勢いを感じるし、何より楽しそう!

色々理由を述べましたが、結局のところ自分達の興味関心のあることをやりたい!
というのが一番の理由だったりします。

また当時、社内にFlutterに知見のあるエンジニアがいなかったため
足並みを揃えて開発をスタートできるのが部活動らしくて良いなと思いました🏃‍💨

Flutterのここが最高!

実際にFlutterでプロト開発に取り組んでみて、良かった点や気づきを紹介します。

👑最高ポイント1: チュートリアルが充実

丁寧なチュートリアル複数種類のCoadLabs
が提供されているので、初学者の方は最初にこれらを学ぶのがオススメです

ちなみに私は
Write Your First Flutter App, part 1
Write Your First Flutter App, part 2 から
Flutterの世界に入りました🌍

👑最高ポイント2: Android Studioで開発できる

Flutterの推奨IDEはAndroid Studio(IntelliJ)Visual Studio Codeです。
普段からAndroidを書かれてる方であれば、使い慣れているIDEのまま開発することができます。

👑最高ポイント3: シンプルで書きやすいDart

Javaによく似た静的型付け言語なので、Java/KotlinやSwiftの経験があれば
学習コストはそれほど高く無いかなと思いました。

  • 型がある
String hoge = "hoge";
var hogege = "hogege"; // 型推論もしてくれる
  • lambdaを書ける
  var printer = (String s) => print("s");
  printer("hoge");
  • Null SafetyではないがNullチェックに便利なオペレータがある
String hoge = null;
var length = hoge?.length ?? 0; // nullの時??の右辺を代入

コーディング記法などで迷ったときはDart language-tourを読みましょう

👑最高ポイント4: Hot Reload

Hot Reload

リロードのスピードがとにかく高速で
大規模なUIの差し替えを行ったとしても、下記のようにすぐに実機確認することができます。

f:id:yokomii:20190404182447g:plain

これにより、トライ&エラーのサイクルが超高速で回せるので
短い時間内で開発をおこなう部活動との相性が最高でした🙌

👑最高ポイント5: デザインパーツが豊富

ネイティブアプリでいうところのViewはFlutterではWidgetと呼ばれ
あらかじめ豊富な種類のWidgetが提供されています。

Widget catalog
Material Components widgets

プロト開発程度の規模であれば、用意されている基本Widget群で事足りると思います。
デザイナーがいなくても、そこそこ見栄えのするアプリが作れるのも嬉しいポイントです。

ネイティブアプリ開発(Android)との違いと代表的なアーキテクチャについて

通常のAndroidのネイティブ開発ではView/LayoutをXMLに記述し、
ビジネスロジックをJava(Kotlin)で記述しますが
FlutterはどちらもDartコードで書きます。

下記コードはAndroidStudioで新しくFlutterプロジェクトを作成したときに
自動生成されるコード(カウンター)を一部抜粋したものです。

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) { // Widgetを生成する関数
    return Scaffold( // ルートWidget
      appBar: AppBar( // AppBar
        title: Text(widget.title),
      ),
      body: Center( // 子Widgetを中央揃えにするWidget
        child: Text( // Text(カウント数)を描画するWidget
          '$_counter',
          style: Theme.of(context).textTheme.display1,
        ),
      ),
      floatingActionButton: FloatingActionButton( // FabButton
        onPressed: () { // クリックリスナーをセット
        setState(() { // Widgetをルートから再描画
            _counter++; // クリックするとカウントアップする
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

f:id:yokomii:20190404180450p:plain

このようにビジネスロジックとUI生成処理が共存してコードが煩雑になりがちです。

この問題を解決する方法として、BLoC(Business Logic Componentの略)と呼ばれる
アーキテクチャパターンが主流となっています。

ビジネスロジックを管理するクラスに
Widgetの状態を示すStream変数(RxのSubjectのようなストリームソース)を定義し
WidgetはそのStreamを監視して自身の振る舞いを変更します。

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    final bloc = CounterBloc();
    return Scaffold( // ルートWidget
      appBar: AppBar( // AppBar
        title: Text(widget.title),
      ),
      body: Center( // 子Widgetを中央揃えにするWidget
          child: StreamBuilder<int>( // Streamを監視できるWidget
              initialData: 0,
              stream: bloc.count, // BLoCクラスのStream変数を監視
              builder: (context, snap) { // Streamの値の変更通知を受けてTextを更新
                return Text(
                  snap.data.toString(),
                  style: Theme.of(context).textTheme.display1,
                );
              })),
      floatingActionButton: FloatingActionButton(
        onPressed: () { // クリックリスナーをセット
          bloc.countUp(); // blocのcountUp()関数を呼び出す
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class CounterBloc {
  final _countObservable = BehaviorSubject<int>();
  Stream<int> get count => _countObservable.stream;

  void countUp() {
    var old = _countObservable.value ?? 0;
    _countObservable.add(++old);
  }
}

(BehaviorSubjectはRxDartライブラリのクラスです)

上記のコードはざっくりとした例で、
実際のBLoCパターンではWidget内でBLoCクラスのインスタンスの生成は行いません。
(無関係のWidgetからBLoCを隠蔽する仕組みがあります。)

BLoCパターンを用いるとsetState()の呼び出しが不要になり、必要最低限のWidgetのみを再描画するのでパフォーマンスの改善にも繋がります。

チュートリアルやCodeLabである程度Flutterに対する理解を深めたら
下記のGoogle I/O 2018のセッション動画で
BLoCパターンなどの代表的なアーキテクチャパターンを学習することをオススメします。

Build reactive mobile apps with Flutter (Google I/O '18)

動画内に出てくるサンプルコード: https://github.com/filiph/state_experiments

あとがき

Flutterは、プロト開発がしたい!手っ取り早くアプリを作りたい!
という時の選択肢として非常に有用だと思いますので
この機会にぜひチャレンジしてみてはいかがでしょうか?

特に、ネイティブアプリ開発の経験者であれば、
Hot Reloadで変更が爆速で反映される体験は感動間違いなしです😭

この記事を読んで、Flutter興味ある!部活動楽しそう!と思ったそこのあなた👇
トクバイで共にエモいプロトタイプ&サービス開発をしましょう〜〜

応募はこちらから↓↓

RECRUIT | 株式会社ロコガイド