iOS アプリと Android アプリを同時に開発!Flutter とは??

iOS アプリと Android アプリを同時に開発!Flutter とは??

エンジニアブログ

投稿日:2021/09/22 | 最終更新日:2021/10/20

初めまして!今年4月より新卒で入社した Yuya です!
本日は最近人気が急上昇中のフレームワーク「Flutter」に関してご紹介します!

iOS アプリなら言語は Swift か Objective-C, Android アプリなら Kotlin や Java を用いて開発しますが 1 つのアプリを開発するにしても2 つの言語を使用し開発しなければならないということは非常に非効率的です。
しかし、Flutter は iOS アプリと Android アプリを同時に開発できるクロスプラットフォームなのです!以下で詳細を述べます。

Flutter とは?

Flutter とは 2018 年にGoogleが開発したiOS, Android, Web を同時に作成できるクロスプラットフォームです。「Dart」という言語をベースにしたフレームワークであり、Swift や Kotlin にはない多くのメリットがあります!以下で具体的なメリット、そして実際に Flutter で開発されているアプリなどを紹介します!

Flutter を導入するメリット

その1:iOS と Android の同時開発の可能
何度も述べるようですが、やはりここが最大の特徴ではないでしょうか。従来は別々に開発していたものが一つのコードのみで開発できるのは作業効率の効率化、人員の削減。さらにコスト削減も可能になります。

その2:ホットリロードにより開発効率が上がる
ホットリロードとは即座にプログラムを上書きし UI や処理に変更を適用させる機能です。
やり方は非常に簡単でショートカットキー(Command + s )で保存するだけです。数秒も待つことなく変更を適用してくれます!
ネイティブの開発でリビルドに時間がかかってしまう経験をされた方々にはこのメリットは非常に大きいかもしれませんね!

その3:ドキュメントが充実している
Flutter はとにかく公式ドキュメントが充実していることが特徴です。サンプルコードも多く掲載されており、YouTube などにも Googleが出したFlutter に関する動画がアップロードされていたりと開発者にとってはかなりありがたいと思われます。
最近ではQiita などの技術記事にも Flutter の記事を多く目にしますね!

Flutterで開発されているアプリ

Flutter の公式サイトにいくつか紹介されていますが、BMW やアリババ、さらにebayなどのアプリケーションは Flutter で開発されています!有名企業に所属されているエンジニアの方々の Flutter に関する記事も最近多く見かけるようになったので今後も 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(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: ‘Flutter Demo Home Page’),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
  ),
      body: Center(
        child: Column(
          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),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

こちらは Flutter プロジェクトを作成した際に初めに表示されるサンプルプログラムです。
さて、みていきましょう!

import ‘package:flutter/material.dart’;

まずは必要なライブラリのインポートからですね。

この material.dart 内に Flutter で開発する上で必要不可欠になる StatelessWidget などが定義されています。インポートは必要不可欠といっても過言ではないですね。

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

続いてメイン関数です。こちらはプロジェクトのエントリーポイントになっていてここからアプリの処理が始まります。
今回はアロー関数を用いて MyApp() を呼び出しています。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: ‘Flutter Demo’,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: ‘Flutter Demo Home Page’),
    );
  }
}

続いては StatelessWidget を継承した MyApp クラスです。Flutter はウィジェットを複数組み合わせて UI を作成します。
ここでは StatelessWidget で定義されている build メソッドをオーバーライドし任意の UI を作成します。
Flutter は StatelessWidget と StatefulWidget の二つが用意されており、何か UI が変化するようなものは
StatefulWidget 、変化しない静的なものは StatelessWidgetを使用します。
build メソッド内で返されている MaterialApp はアプリのタイトルや色を定義しているため変化がない、よってS tatelessWidget が継承されています。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

続いて MaterialApp 内のプロパティ home から呼び出される StatefulWidget を継承した MyHomePage クラスです。
StatefulWidget はこれを継承したクラスと State を継承したクラスの二つから構成されます。
フィールドを追加した為にコンストラクタが作成されていますね。
そしてこのクラス内で StatefulWidget 内の createState メソッドをオーバーライドし任意の State を作成していきます。

class _MyHomePageState extends State {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          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),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

state クラスを継承した _MyHomePageState を作成します。
StatefulWidget を用いた場合はこの State 内で UI を構築していきます。

クラス内に定義されている int _counter = 0; はカウンターの現在の数字を保持してます。
こちらを変化させることでカウンターを可能にしています。

 void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

state クラス内に定義されている関数は SetState メソッドを呼び出すものです。
こちらが呼ばれることにより setState 内の処理が UI に反映されます。

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          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),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );

続いて Build メソッド内の解説です。
返されている Scaffold はレイアウトを構成する為に使用されるウィジェットです。アプリの AppBar などが使用可能になります。

Scaffold 内の appBar プロパティにアップバーのタイトルなどを記述、body 内に画面のレイアウトのウィジェットを組み合わせていきます。

Flutter のウィジェットには親ウィジェットと子ウィジェットがあり、どんどんウィジェットを重ねて配置します。

Center というウィジェットは子ウィジェットを真ん中寄せにします(これを使用しないとウィジェットは左上寄せになります。)

続いて Column というウィジェットですがこちらは縦にウィジェットを並べて行くため、複数の子ウィジェットを持ちます。
上のコードでいうところのTextウィジェット二つですね。

floatingActionButton プロパティは Scaffold 内のプロパティです。

今カウントアップをする関数をこのボタンをタップすることにより呼び出し、カウンターを作成することができました。

状態管理に関して

LINE のテーマを変更したり、twitter をダークモードにしたりと UI はユーザの操作により変更されます。
こちらの UI の変更の反映を管理するのが状態管理です。

状態管理を行わなければアプリはリロードされずに静的な画面となってしまう為、動きがある(カウンターアプリならば数字が変化する)ようなアプリには状態が変更された時に検知される必要があります。
以下で代表的な状態管理の方法をご紹介します。

StatefulWidget

こちらは Flutter の公式ドキュメントでもこちらを用いての状態管理が使用されています。StatelesWidget で扱われる値は全て不変ですがStatefulWidget は State クラスを作成することによりクラス内で値を変更・保持します。
以下が Statefulwidget を用いたサンプルコードになります。

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          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),
      ), // This trailing comma makes auto-formatting nicer for build methods.
   );
 }
}

こちらは StatefulWidget を用いたカウンターの例です。
MyHomePage は StatefulWidget を継承したクラスと State を継承したクラスの二つで構成されており、値の変更を適用するには SetState メソッドを使用します。
一見良さそうなコードに見えますが下記の問題が存在しています。

  • 一つのクラス内にベタ書きな為に可読性に欠けている
  • 状態を切り出しにくい為テストも困難になっている
  • SetState メソッドを用いて UIのリビルドが行われるが build 関数内が全てリビルドされるため、パフォーマンス性に欠ける

実務では StatefulWidget が使用されづらいのは上記のような理由があるためです。

Provider

これらの問題を解決するために開発されたパッケージが Provider です。
このパッケージは StatefulWidget の泣きどころでもある SetState で build 関数内全て書き換えてしまうといった点も変更反映箇所を指定できるので無駄がなく、パフォーマンス性が高いと言えます。
さらに表示部分と処理部分を分離できるため、可読性も向上しテストも行いやすくなりました。
以下が provider を用いたカウンターアプリのサンプルコードになります。

import ‘package:flutter/material.dart’;
import ‘package:provider/provider.dart’;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  return MaterialApp(
  title: ‘Flutter Demo’,
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: ChangeNotifierProvider(
       create: (context) => Counter(),
       child: MyHomePage(),
     ),
   );
 }
}
class Counter with ChangeNotifier {
 int value = 0;
 _increment() {
   value++;
   notifyListeners();
 }
}
class MyHomePage extends StatelessWidget {
 MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
   final counter = Provider.of(context);
   return Scaffold(
     appBar: AppBar(
       title: Text(“sample”),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
           Text(‘You have pushed the button this many times:’),
           Consumer(
             builder: (context, counter, _) {
               return Text(
                 ’${counter.value}’,
               );
             },
           ),
         ],
       ),
     ),
     floatingActionButton: FloatingActionButton(
       child: Icon(Icons.add),
       onPressed: () => counter._increment(),
     ),
   );
 }
}

StatefulWidget にて状態管理を行ったとこよりもコード数は少なく、さらに可読性も向上しましたね!

その他の状態管理に関して

上記2つ意外にもProviderをより使いやすくした「riverpod」やimmutableなクラスをより使いやすくした「freezed」など多くの状態管理の手法があります。
最近では「riverpod」+「freezed」+「hooks」を組み合わせた状態管理が流行っているようです!

Flutter の今後

Flutter がどれほど注目されているかを明確にするために「Google trends」を用いてReact NativeやXamarinなどと比較して人気の動向を調査しました。

以下の結果になり、どれだけ Flutter の注目度が高いかわかりますね!

altタグ代替えテキスト

有名な企業に在籍するエンジニアの方々の個人ブログでも Flutter の記事を多く目にするようになったので今後とも Flutter の需要は高まってくのではないかと予想されます。

いつか Swift や Kotlin などのネイティブの言語に取って変わる存在になれば良いなと願っています!!

執筆者:Yuya


弊社トップゲートでは、 Google Cloud (GCP) 、または 20ID以上のGoogle Workspace(旧G Suite) 導入をご検討をされているお客様へ「Google Meet で無料個別相談会」を実施いたします。導入前に懸念点を解決したい方、そもそも導入した方がいいのかをお聞きしたい方はお気軽にお申し込みください!

トップゲート経由でGoogle Cloud (GCP) をご契約いただけるとGoogle Cloud (GCP) の利用料金はずっと3%オフとお得になります!

お申込みはこちら

記事を探す

GCP のメリットを最大限に活用しよう!

Google Cloud・Google Workspace のご相談・
お見積り依頼はお気軽に
お問合せフォーム