Flutterを布教してみる

f:id:oyachi-milk:20211224040000p:plain
本記事はTUT Advent Calendar 2021 24日目の記事です。

はじめに

はじめまして。 TUT 3系 B3のoyachiです。 世間はクリスマスなるもので賑わっているようですが、私は相変わらず課題やレポートに勤しんでおります。平常運転ですね。 折角のAdvent Calendarですので、ちょっとした技術的なお話をしてみたいと思います。といっても私も勉強中の身なので、そこまで高度な内容ではないです。

前置き

皆さんは何か趣味をお持ちでしょうか?技科大生は基本的に忙しいですので、夢中になって打ち込める趣味の一つや二つは持っておきたいですよね。休みにずっと寝ていたりTwitterYouTubeばかり見ているのはちょっともったいない。で、私から開発をおすすめしたいわけです。開発といっても電子工作やゲーム開発など色々ありますが、今回はFlutterによるモバイルアプリ開発について取り上げてみます。アプリ開発はとてもクリエイティブなものですし、PC1台あれば始められます。そう考えると非常にコスパに優れている趣味と言えるでしょう。

Flutterについて

Flutterとはなんぞや?という方にむけてご紹介。 Flutterは所謂モバイルアプリケーション用フレームワークです。開発元は天下のGoogle様ですね。 フレームワークとは文字通りアプリケーションと枠組みとなるソフトウェアのことです。モバイルアプリに限らず、大体のアプリはフレームワーク上でUIや機能を実装することで作られています。ゼロから作る必要がないので、開発工程を大幅に短縮できるわけです。 Flutterな主な特徴は

等々。中でも大きいのはクロスプラットフォーム開発に対応しているということです。基本的にモバイルアプリは、iOSならSwiftやObjective-CAndroidならKotlinやJavaなどOSごとに別々の言語で開発する必要があります。しかし、Flutterのようなクロスプラットフォーム開発に対応しているフレームワークを使うと、1つの言語で両OSのアプリ開発ができます。ちなみに他にもReact NativeやXamarinなどがクロスプラットフォーム対応です。
Flutterの詳細は公式ドキュメントを覗いてみてください。

アプリ開発に触れてみる

軽くFlutterについて紹介したところで実際にどのようにアプリを作っていくのかを見てみましょう。

※以下コードを掲載している箇所がありますが、それについての詳しい説明はしません。また、開発環境構築や仕様等に関する話も省略しています。あくまで「Flutterというものがあって、こんな感じでアプリを作っていくんだな」という雰囲気をお伝えできればと思います。

Widget

FlutterではWidgetを用いてUIを作っていきます。WidgetとはUIを構築するパーツのようなものです。例えばアプリ内にボタンを配置したいとしましょう。そのときはFlutterからButtonというWidgetを呼び出せばいいです。以下のようなコードを書きます。

ElevatedButton(
   child: const Text('Button'),
   onPressed: () {},
),

f:id:oyachi-milk:20211223170038p:plain:h500

これだけです.簡単ですね.でもこれだけでは味気ないのでデザインを変えてみましょう。

ElevatedButton(
    child: const Text('Button'),
    style: ElevatedButton.styleFrom(
         primary: Colors.orange,
         onPrimary: Colors.white,
         shape: RoundedRectangleBorder(
           borderRadius: BorderRadius.circular(10),
          ),
     ),
     onPressed: () {},
 ),

f:id:oyachi-milk:20211223170918p:plain:h500

ボタンの色をオレンジ色にして、枠を少し丸めてみました。このようにWidgetのプロパティに色々と指定することでデザインやボタンが押されたときのイベント処理などを変更することができます。FlutterではこのようなWidgetが数多く用意されており、開発者はこれを組み合わせることで複雑なUIを簡単に構築できます。このWidgetの多さと公式ドキュメントの充実さが、私がFlutterを推す理由の1つです。

作ったアプリ

Widgetを使ってどのようにUIを作っていくのでしょうか。以下のサンプルアプリを例に見てみます。
f:id:oyachi-milk:20211223174341p:plain:h400 f:id:oyachi-milk:20211223174533p:plain:h400
某大手ECサイトのものを参考にして作ったオンラインショッピングアプリです(著作権的にまずい商品画像は黒塗りにしています)。ちなみに作ったのはUI周りだけなので実際に購入できるわけではありません。
一見複雑そうなUIですが、Widgetを複数組み合わせることでこのように実装できます。大まかにUIを分解すると以下のようになります。

f:id:oyachi-milk:20211223184217p:plain:h400

f:id:oyachi-milk:20211223184237p:plain:h400

Navigation Bar

iOSアプリでよく利用されるコンポーネントです。バックボタンやドロワーを表示させるためのボタンが配置されることが多いですね。AppBar Widgetで作っています。

AppBar(
        backgroundColor: Colors.white,
        leading: Padding(
          padding: EdgeInsets.only(left: 20.0),
          child: InkResponse(
            onTap: () => print('Menu'),
            child: Icon(
              Icons.menu,
              size: 30.0,
              color: Colors.black,
            ),
          ),
        ),
        actions: [
          Stack(
            children: [
              Padding(
                padding: EdgeInsets.only(top: 12.0, right: 20.0),
                child: InkResponse(
                  onTap: () => print('Cart'),
                  child: Icon(
                      Icons.shopping_cart,
                      size: 30.0,
                      color: Colors.black
                  ),
                ),
              ),
              Positioned(
                bottom: 8.0,
                right: 16.0,
                child: Container(
                  height: 20.0,
                  width: 20.0,
                  decoration: BoxDecoration(
                    color: Colors.orange,
                    borderRadius: BorderRadius.circular(10.0),
                  ),
                  child: Center(
                    child: Text(
                      '3',
                      style: TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ],
      ),

Search Bar

よくある検索するためのコンポネートです。TextFormFieldをstyleを色々カスタマイズして実装しています。

Container(
      margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
      child: Stack(
        alignment: Alignment.centerRight,
        children: [
          TextFormField(
            decoration: InputDecoration(
              hintText: 'Search',
              fillColor: Colors.white,
              prefixIcon: Icon(
                Icons.search,
                size: 30.0,
                color: Colors.black,
              ),
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(16.0),
                borderSide: BorderSide.none,
              ),
              filled: true,
            ),
          ),
          Padding(
            padding: EdgeInsets.only(right: 20.0),
            child: InkResponse(
              onTap: () => print('Microphone'),
              child: Icon(
                Icons.mic,
                size: 30.0,
                color: Colors.black38,
              ),
            ),
          ),
        ],
      ),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(16.0),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.1),
            blurRadius: 30,
            offset: const Offset(0, 14),
          )
        ],
      ),
    )

Banner

広告などを表示するアレです。正式名称はわかりません。Flutterは画像を挿入するのも簡単ですね。

 Stack(
      alignment: Alignment.bottomCenter,
      children: [
        Image(
          image: AssetImage('assets/images/sale_banner.jpg'),
        ),
        Container(
          margin: EdgeInsets.only(bottom: 10.0),
          padding: EdgeInsets.all(10.0),
          height: 65.0,
          width: MediaQuery.of(context).size.width * 0.7,
          color: Colors.white,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Text(
                'Bargain Detail',
                style: TextStyle(
                  fontSize: 18.0,
                  fontWeight: FontWeight.bold,
                ),
              ),
              SizedBox(width: 20.0,),
              Container(
                width: 60.0,
                child: FlatButton(
                  padding: EdgeInsets.all(10.0),
                  onPressed: () => print('Sale details'),
                  color: Colors.orange,
                  child: Icon(
                    Icons.arrow_forward,
                    size: 25.0,
                    color: Colors.white,
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    )

Carousel

一覧画面など同じ要素を繰り返すシンプルなコンポーネントをリストと呼びますが、その中でも写真などを横スクロールしていくものをカルーセルタイプと呼ぶそうです。

Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
          child: Text(
            title,
            style: TextStyle(
              fontSize: 22.0,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
        Container(
          height: 250.0,
          child: ListView.builder(
            padding: EdgeInsets.symmetric(horizontal: 10.0),
            scrollDirection: Axis.horizontal,
            itemCount: products.length,
            itemBuilder: (BuildContext context, int index) {
              return _buildProductCard(index);
            },
          ),
        ),
      ],
    )

Card

関連するコンテンツをまとめて並べるコンポーネントです。FlutterにはCardというWidgetが用意されており、簡単に実装できます。

Card(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const ListTile(
            title: Text(
              'Recommended for you',
              style: TextStyle(
                fontSize: 25,
              ),
            ),
          ),
          Image(
            image: AssetImage('assets/images/monitor.jpg'),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              TextButton(
                child: const Text('More'),
                onPressed: () => print('more'),
              ),
              const SizedBox(width: 8),
            ],
          ),
        ],
      ),
    )

どのコンポーネントもあるWidgetを呼び出し、その子要素に別のWidgetを割り当てて...といった形で書かれているのがなんとなくわかるかと思います。Flutterの基本はこれを繰り返すだけです。あとはユーザーからの操作があったときの処理を記述していくことで簡単なアプリが作れてしまいます。
コードの全貌は私のGithubに載せておきます。

最後に

いかがだったでしょうか。
駆け足になってしまいましたが、ちょっとでもアプリ開発の雰囲気が伝われば幸いです。一見コード量が多く複雑に見えるかもしれませんが、高度なことはやっていません。ちょっと勉強すれば動くものが作れてしまいます。実際のアプリの多くはサーバーサイドとの通信が行われるのでバックエンドの知識が必要です。しかし、GoogleにはFirebaseというBaaS形態のサービスがあり、これを利用することでバックエンドで動くサービスの構築・運用が楽になります。FlutterとFirebaseを組み合わせることで個人開発でもある程度のクオリティのものが作れます。
私も前々から個人開発を進めているのでなんとか完成してリリースまで持っていきたいですね。やはり何かを作るのは楽しいものです。皆さんも是非、Flutter含めアプリ開発をやってみてください!