Flutterを使ってクソアプリを作ってみた


# これは何？

最近、若者の間で「クソアプリ」なるものを作って公開するのが話題になっているようで。いや確かに「ちゃんとしたもの」を作ろうとすると、それはそれで多大なる労力が必要になってハードルも上がるのだけれども、クソアプリでいいやってハードルを下げると「ちょっとかじってみる」程度の経験を積むにはいいし、やったことを公開することで情報の整理にもつながっていい気がするな、ということで最近気になるFlutterで負けじとクソアプリを作ってみた。

ソースコードは[コチラ](https://github.com/shionit/sample_flutter)。

## Flutterとは？

Android/iOSのマルチデバイス対応のアプリが１つのソースコードで開発できるというGoogleが作った技術。Dart言語で実装する。

https://flutter.dev/

# やったこと

## 開発環境の構築

大いにこの記事を参考にしており「開発環境の構築」は全く同じなので端折ります（手抜き）

Flutterに入門してクソアプリを作るまで
https://qiita.com/matsushou/items/f62ca1fb249670d7dbbc

ちなバージョンは

- Flutter v1.2.1 (for Mac)
- Android Studio 3.4
  - FlutterとDartのプラグインを入れる

## Dartに再入門する

筆者Google好きで、Dartが公開された直後（1.0以前）にかじったことがあったので、おさらい的に言語仕様（結構変わっている...）を見直してみる。

Language Tourをナナメ読む
https://www.dartlang.org/guides/language/language-tour

前述の参考記事のとおり Important concepts まで読んだが、Javaエンジニアとしては以下を踏まえればどうにかなりそう。

- 型はある、クラス定義も継承もある、ちゃんとオブジェクト指向
- `new` は2.x系から省略可能になった（[当時のリリースノート](https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#200---2018-08-07)）
- アンダースコア `_` 始まりの変数/関数/クラスがprivateになる
	- 可視性を示す修飾子 public/private はない

## Flutterアプリを作ってみる

- Android Studioから「Start a new Flutter project」を選択
  ![Kobito.OHndNY.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13994/84b42275-20be-cced-644c-d721e0236a8b.png "Kobito.OHndNY.png")

  - Flutter Applicationを選択してNext
  - Project nameを適当に指定（デフォルトの `flutter_app` のままでもいい）してNext
  - Company domainを適当に指定、Sample Applicationの "generate sample content" にチェックしてFinish
  - サンプルコード付きのプロジェクトが作成される
    ![Kobito.W0QeXI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13994/862ee307-bd23-efb4-d8cf-ef1a22dbdee8.png "Kobito.W0QeXI.png")

      - `main.dart` にメインロジックが記載されている
      - ちゃんとテストコード `widget_test.dart` も用意されているのが素敵 :star:
  - Emulatorを用意して起動
      - AVD Managerから端末のVMを用意して起動する 
      - ＋ボタンを押すと画面に表示された数値が増えていくサンプルアプリらしい
  - サンプルアプリの画像は前述の記事にキャプチャがあるので、そっちを見てください（という手抜き）

## アプリを実装する

クソアプリの境界線ってどこだろう？うっかりちゃんと作ってしまうとクソではなくなってしまうので、ここは慎重にクソアプリを作る必要がある。

今回は、macOSを始めとして Google Chrome や Evernote でも実装されている流行りの DarkMode を搭載すべく「ダークモードへの切り替え」をこのアプリに実装してみることにする。

### 漢は黙ってCI

とはいえロジックに手を出し始めるのではなく、眼の前に動くコードとテストコードがあるなら、まずCIを回さないとね。ということでCircleCIでのビルドを設定しておく。

- 生成されたサンプルプロジェクトをそのままコミット＆GitHubへプッシュする。

次に挙げるサイトを参考にすると、すでにFlutter用のビルドコンテナが用意されているのが分かるので、単純に `.circleci/config.yml` を追加するだけで、CircleCI上でビルドが動くようになる。[作業ログ](https://github.com/shionit/sample_flutter/issues/3)

- [FlutterでCI環境作ろうとしたけど諦めた話｜shogo yamada｜note](https://note.mu/shogoyamada/n/n014c59951c8d)
- [Setup CI/CD Pipeline for your Flutter App using CircleCI](https://medium.com/flutter-community/setup-ci-cd-pipeline-for-your-flutter-app-using-circleci-ef07e39982ab)


```.circleci/config.yml
 version: 2
 jobs:
   build:
     docker:
       - image: cirrusci/flutter
     steps:
       - checkout
       - run: flutter doctor
       - run: flutter test
       - run: flutter build -v apk
```

これで、branch push 時や、master マージ時にビルドが走るようになる。コケたら教えてくれるので、あんしんあんしん。

### モード切り替え用のコントロール（Switch）を配置

よし、本筋に戻ってまずはスイッチを配置するぞ。ということでサンプルをググってみる。

https://kodestat.gitbook.io/flutter/24-flutter-switch

これがシンプルにまとまっていて分かりやすい。他にもサンプルケースが色々あって良さげ。このサイトはGitBookで作られていて、元のリポジトリは [ここ](https://github.com/raunakhajela/Flutter-Tutorials) のようだ。

`main.dart` の内容をまるっとこのサンプルに書き換えて実行してみたら、ちゃんとスイッチのサンプルが表示された。

![Kobito.zdsg1w.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13994/2e99b43c-09ae-1bc4-69bc-42240ec0a42f.png "Kobito.zdsg1w.png")

そうそう、それよそれ。
今回はタイトル付きのスイッチを使いたいので、この部分＋ `_onChanged2()` 関数をパクればよろし。

```main.dart
new SwitchListTile(
    value: _value2,
    onChanged: _onChanged2,
    title: new Text('Hello World', style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.red)),
 )
```

実際には、こんなソースを `child: Column()` の `children: <Widget>[]` の先頭に追加しました。
この時点ではまだ `_changeDarkMode()` 関数はテキトーです。

```main.dart
Container(
  padding: EdgeInsets.all(40.0),
  child: SwitchListTile(
    value: _isDarkMode,
    onChanged: _changeDarkMode,
    title: Text('Dark mode', style: new TextStyle(fontWeight: FontWeight.bold)),
  ),
),
```

見た目はこんな感じに。

![Kobito.JqiQUR.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13994/6cd392b2-109d-bc7c-7dd8-9e41e3041dea.png "Kobito.JqiQUR.png")

パーツを配置しただけで「Dark modeに切り替えられる感」が８割増しましたね。

### いざ、DarkModeへ切り替え

残るは `_changeDarkMode()` 関数の実装だ。ということで試しに `backgroundColor` 指定だけblackで追加したら、文字は黒のままで画面全体が真っ黒になってしまったので、どうやら何か違うらしい。調べてみると Theme を切り替える必要がありそう。

[Using Themes to share colors and font styles - Flutter](https://flutter.dev/docs/cookbook/design/themes)

でもこの実装を真面目にやると面倒くさいなぁ、と調べ続けていたら、`DynamicTheme` というドンピシャなwidgetが公開されていたので、これを使おう。

- [How to Dynamic Theming in Flutter – Aswin Mohan – Medium](https://medium.com/@aswinmohanme/how-to-dynamic-theming-in-flutter-229fff8ae8ff)
- [How to dynamically change the theme in Flutter – ProAndroidDev](https://proandroiddev.com/how-to-dynamically-change-the-theme-in-flutter-698bd022d0f0)

`pubspec.yaml` のdependenciesに1行追加する。

```pubspec.yaml
dependencies:
  ...(中略)
  dynamic_theme: ^1.0.0
```

`main.dart` の修正箇所はこんな感じ。（抜粋）

```main.dart
import 'package:dynamic_theme/dynamic_theme.dart';

...(中略)

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return DynamicTheme(
      defaultBrightness: Brightness.light,
      data: (brightness) => ThemeData(
        primarySwatch: Colors.blue,
        brightness: brightness,
      ),
      themedWidgetBuilder: (context, theme) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: theme,
          home: MyHomePage(title: 'Flutter Dark Mode Sample'),
        );
      }
    );
  }
}
 
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
 
  final String title;
 
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isDarkMode = false;

  void _changeDarkMode(bool value) {
    setState(() {
      _isDarkMode = value;

      var _brightness = Brightness.light;
      if (_isDarkMode) {
        _brightness = Brightness.dark;
      }
      DynamicTheme.of(context).setBrightness(_brightness);
    });
  }
  ...(略)
}
```

- 結果
  - スイッチのOn/OffでThemeが動的に切り替わり、憧れのDarkModeに！ヒャッホウ！！ :night_with_stars: 
  ![dark-mode-sample2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13994/fd1cf79a-6951-5cba-cd7e-820d26d90918.gif "dark-mode-sample2.gif")


  - うーん、、、冷静に見ると「だからどうした感」のあるクソアプリですね :star2: 
  - 参考：[作業ログ](https://github.com/shionit/sample_flutter/pull/5/files)

## 感想

- 簡単にスマホアプリが作れそう
  - 今回はアプリを最終ビルドするところまでやっていないけど、簡単な動作確認レベルならすぐイケる
- dart はキャッチアップ楽ちん
  - エディタの補完も効けば何の苦もない
- ソースコードの保存　→　エミュレータへの自動反映、での動作確認が非常に快適
- クソアプリ作ってみるの楽しい

GW後半に軽くプログラミングしてみたい方は、いかがでしょうか？

お粗末さまでした。