Web制作・アプリ開発をコストパフォーマンスで考えるなら

Flutterでメモアプリを実装してみた

こんにちは。graphy事業部の遠藤と申します。

業務でFlutterを検証する為、簡単にメモアプリを実装してみました。今回は、その時に得た知見について記載していきたいと思います。

Flutterとは

Flutterは、Googleから2018年2月頃に正式リリースされたクロスプラットフォームのフレームワークです。はじめはiOS/Androidのモバイル向けのみでしたが、最近ではデスクトップやWeb向けにも開発が進んでいます。プログラミング言語はDartを使って開発します。

Flutter Image

Flutterはオープンソースで開発されていますので、GitHubでソースを覗くことができます。GitHubのスター数も現在2019年6月で約68,000となっており、同類の人気フレームワークであるReact Nativeと大差なくなってきました。

iOS/Androidアプリをそれぞれ開発する場合は工数は約2倍かかりますので、それを単一のコードで開発できるのはとても魅力的です。

Flutterのインストール

Flutterの開発環境構築は、公式ドキュメントを参考にしてください。開発環境が整ったら、新しいプロジェクトを作成して試しに起動してみます。

$ flutter create my_app
$ cd my_app
$ flutter run

ボタンを押して数字をカウントするだけのシンプルなアプリが起動します。

Sample App

HotReloadを有効にした状態で、コードを修正するとすぐに結果がアプリに反映されると思います。React Nativeでもそうですが、UI系の修正をする度にビルドし直す必要が無いことでストレスフリーに開発を進めることが出来ますね。

わざとエラーを出してみました。見慣れてない影響かもしれませんが少し見づらい印象です。

Error App

Visual Studio Codeで開発環境を整えてみました。以下のようにブレークポイントを設定してデバッグすることも可能なので、とても開発しやすいと思いました。

Visual Studio Code

メモアプリの実装

Flutterを使って、簡単にメモアプリを実装してみました。ソースコードをGitHubにアップしておいたので参考にしてください。

https://github.com/ekazuki/flutter-memo-app

メモ追加処理

ホーム画面と編集画面のみのシンプルな構成で、追加ボタンを押してメモ追加ができます。

Add Memo

追加ボタンを押したタイミングで、Navigatorで編集画面をプッシュします。テキストが編集された際のコールバックとして_onChangedを渡して、メモ内容に更新がある度にデータが保存されます。

// main.dart
...
void _addMemo() {
  setState(() {
    _memoList.add("");
    _currentIndex = _memoList.length - 1;
    storeMemoList();
    Navigator.of(context).push(MaterialPageRoute<void>(
      builder: (BuildContext context) {
        return new Edit(_memoList[_currentIndex], _onChanged);
      },
    ));
  });
}

void _onChanged(String text) {
  setState(() {
    _memoList[_currentIndex] = text;
    storeMemoList();
  });
}
...

メモ削除処理

メモをフリックして、簡単操作でメモを削除できます。

Delete Memo

Dismissibleウィジェットが公式に提供されており、それを参考にして実装しました。フリックさせたいウィジェットをラップするだけで完了するので、とても簡単に追加することが出来ます。また、onDismissedが呼ばれるタイミングで、データ削除する処理を実装してます。

// main.dart
...
Widget _buildWrappedRow(String content, int index) {
  return Dismissible(
    background: Container(color: Colors.red),
    key: Key(content),
    direction: DismissDirection.endToStart,
    onDismissed: (direction) {
      setState(() {
        _memoList.removeAt(index);
        storeMemoList();
      });
    },
    child: _buildRow(content, index),
  );
}
...

データの保存

メモのデータは端末に保存することで、アプリを再起動しても消えないようにしてます。

Save Data

こちらのドキュメントを参考にshared_preferencesのプラグインを追加します。iOSのNSUserDefaultsとAndroidのSharedPreferencesをラップしたプラグインです。

...
dependencies:
  flutter:
    sdk: flutter
  # 追加
  shared_preferences: ^0.5.3
...

以下はshared_preferencesで端末のデータ取得/保存の処理を行っている部分になります。非同期の処理なので、読み込み中はローディング中のUIを出すなどの処理を書いておくと良いかと思います。

// main.dart
...
void loadMemoList() {
  SharedPreferences.getInstance().then((prefs) {
    const key = "memo-list";
    if (prefs.containsKey(key)) {
      _memoList = prefs.getStringList(key);
    }
    setState(() {
      _loading = false;
    });
  });
}
...
void storeMemoList() async {
  final prefs = await SharedPreferences.getInstance();
  const key = "memo-list";
  final success = await prefs.setStringList(key, _memoList);
  if (!success) {
    debugPrint("Failed to store value");
  }
}
...

まとめ

Flutterの開発環境構築から簡単なアプリの実装をしてみました。以下、アプリを実装して思ったことです。

  • React NativeとTypeScriptの経験があるなら、FlutterとDartでの開発はとっつきやすいんじゃないかと思った。
  • アニメーションはdebugモードで確認するとすぐにカクついてしまう。
    • アニメーション部分の確認は、releaseやprofileで実機で確認しなければならないので面倒。
    • アニメーションだらけのアプリになってくると、debugモード時に激重になっていきそう。
  • ビューを構成する部分のコードは複雑になりやすい。
  • マテリアルデザインでアプリを作りたい場合は、標準でUIが揃っているので楽に作れる。
  • FlutterはiOS/Androidのプラットフォーム毎のUIを使わない仕組みなので、UIは基本的に同じになる。
    • React Nativeではブリッジを介してプラットフォーム毎のUIを利用するので、同じコードでもUIに差異が生じることがある。

Flutterは公式ドキュメントも充実していて、とても作りやすいという印象でした。リリースから時間も経って情報量も増えてきましたので、ぜひ実務で使ってみてはいかがでしょうか。