Mirror Todo
まずは、こちらの動画を御覧ください。
「Todoアプリっぽいけど入力した文字が文字化けしているな...」
そう思った方は一時停止してTodoアプリの文字をよーく見てください。
聡明なあなたなら、文字化けではないことに気がつくはずです。
そう、このアプリは、 追加したTodoが鏡文字になって表示される アプリなのです!
触って確かめることができるデモサイトも用意しました。
- 右上の+ボタンを押すか、Todo リスト下部の空白部分を押すことでダイアログが開きます。
- ダイアログ内の入力フォームに登録したいTodoを入れ、追加ボタンを押すと追加が完了します。
- 削除したいTodoのリスト項目をクリックすることでTodoが削除されます。
ぜひ触って本当に鏡文字になっているか確かめてください。
ソースコード
「話とかいいからソースコード早く見せろよ!」
そんな強強エンジニアのあなたのためにソースコードを先に提示しておきます。
本記事の内容
前置きが長くなりました。
本記事ではこのアプリを作るにあたっての開発体験談や、技術の話について記載します。
前半ではアプリ作成フレームワークであるFlutterでの開発体験について話します。
「Flutterってこんな事できるんだな」
「Flutter触ってみようかな」
そう思っていただければ嬉しいことこの上ありません。
後半ではより技術的な話として、内部データベース等のパッケージの技術選定理由、
今年リリースされたRiverpod 2.0 でのコード生成、
アプリのアーキテクチャについて触れます。
興味があるトピックがあれば幸いです。
ぜひ読んでみてください!
マルチプラットフォームなアプリ作成フレームワーク『Flutter』
今回作成したMirror Todo ですが、
Flutterというマルチプラットフォームアプリ作成フレームワークを用いて作成しています。
実は今回作成したMirror Todo、上で提示したWebアプリだけでなく、
iOSのモバイルアプリとしても、 Androidのモバイルアプリとしても実行することが可能です。
さらにその上、 macOSのデスクトップアプリとしても実行できます。
検証未ですが、おそらくLinux に対してもWindowsに対しても実行できるはずです。
Flutterは2022年12月現在、少なくともiOS, Android, Web , Windows, Linux, macOS の
6つのプラットフォームに対するアプリを単一のコードベースで作ることができるフレームワークとなっています。
また、このFlutter、パッケージやウィジェット(UIの構成要素)が豊富だったり、
UIをDart(Flutterで用いる言語)のみで構成することができたりと、
開発速度を上げるのにもとても良いです。
このMirror Todo もほぼ1日で作成することができました。
その他Flutterの利点については以下の記事で解説しています。
もしFlutterに興味を持っていただけたなら、読んでいただけますと幸いです。
鏡文字の仕組み
このアプリで一番気になるのは鏡文字をどう表現したのかではないでしょうか?
鏡文字は『文字列を逆順に並べ一文字ずつ反転させる』ことでできています。
なので作り方は、
- 鏡文字化したい文字列を1文字ずつ
List
として格納する - 1の
List
を逆順にする - 1文字ずつ反転させ表示する
1の「鏡文字化したい文字列を1文字ずつList
として格納する」については、
文字列(String
クラス)が持つsplit
というメソッドで実現できます。
2の「逆順にする」については、
List
クラスが持つreversed
というメソッドで実現できます。
まとめて実行したのが以下のコードです。
todo: todo.todo.split('').reversed.toList(),
一番難しそうな3ですが、Flutterに用意されている、Transform
というウィジェットを使えば簡単に実装できます。
ウィジェット(Widget)とはFlutterのUIの構成要素です。
テキストを表示するText
ウィジェットだったり
ウィジェットを横に並べるRow
ウィジェットだったり、
さまざまなウィジェットがFlutterでは用意されています。
Transform
ウィジェットは回転や移動、スケール変更など、様々な変形を実現できるウィジェットです。
今回はY軸(縦軸)回りに半回転させる(π ラジアン回転させる)、という使い方で、
文字を反転させるのに使っています。
以下が文字の反転部分のコードとなります。
child: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(kRotationRadians),
child: Text(todoString),
),
(kRotationRadians = π
)
パッケージの追加等不要で、反転処理がこれだけで済むのです。
Flutterの用意されているウィジェットの豊富さに驚いていただけたかと思います。
技術選定
ここからの話はFlutterにある程度触れたことのある方向けに記載します。
Flutterわからん、という方はまとめまで飛んでください。
Mirror Todo を作るにあたっての使用したパッケージと使用理由について記載します。
データ永続化
Drift を使用しました。
今回作成するアプリはWebだけでなくモバイルでも実行可能なように、
というのを目標にしていました。
有名なデータ永続化(内部データベース)パッケージとして、
iOS, Android, Web すべてのプラットフォームでデータ永続化が実装可能なパッケージを検討したところ、
Driftが当てはまりました。
isar も候補でありましたが、isarの最新バージョンでWebサポートがされていない、
という問題がありました。
バージョンを下げることも検討しましたが、他のパッケージと競合するため、候補から外れました。
isarのHP
https://isar.dev/ja/
問題のissue
https://github.com/isar/isar/issues/686
Driftの基本的な使い方については以下の記事を御覧ください。
状態管理
ウィジェット間での状態の共有方法として、
Riverpod を使用しました。(正確にはhooks_riverpod)
ウィジェット単体での状態管理についてはflutter_hooksを用いています。
Riverpodでの状態管理に使用するProvider
の作成には、
今年9月から10月にかけてリリースされた
riverpod_generatorを活用しました。
新しくて使ってみたかったから、という単純な理由で使用しました。
この点については次で解説します。
Riverpod Generator
Riverpod とは
Riverpod とは、ウィジェット間の状態の共有に用いられるフレームワークです。
Riverpod ではProvider
というオブジェクトを使用して状態を管理します。
このProviderの定義はグローバルのスコープにて以下のように書くことで可能です。
final todoDatabaseProvider = Provider((ref) => TodoDatabase());
このコードはTodoDatabase()
のインスタンスを状態として保管していることを意味しています。
状態を使用する際にはref
オブジェクトが使用可能な場所で、
ref.watch(todoDatabaseProvider)
とすることで使用が可能となります。
(一例として、ConsumerWidget
等を拡張したウィジェットを使用することでref
がbuild
メソッド内で使用できます。)
Riverpod についてより詳しい説明は以下の記事を御覧ください。
Riverpod Generator
Riverpod Generator は 上記のProvider
をコード生成で作成することを可能にするパッケージです。
上記例だと、以下のコードで同様のProvider
が実装されます。
@Riverpod(keepAlive: true)
TodoDatabase todoDatabase(TodoDatabaseRef ref) {
return TodoDatabase();
}
そんなにコード量変わらない(何なら増えてる)ので、
使う意味ある?と思われるかもしれません。
公式ページ(現在開発中のページ)に記載の
このRiverpod Generator を使うメリットを一部紹介します。
- どの
Provider
を使うかを悩まなくていい
Provider
には非同期のデータを扱うFutureProvider
やStreamProvider
など複数の種類が存在します。
どのProvider
を使うべきかについて、Riverpod Generatorを使用すると、
自動で適切なProvider
を自動生成してくれるため、悩む必要がなくなる、とのことです。
自分は使用したいProvider
がこれだから、Riverpod GeneratorでこのProvider
を使用するには、
この書き方で〜といった形で実装したため、
この点のメリットをあまり享受できませんでした。
初学者がRiverpod Generatorの書き方を覚えるだけでよくなる、
という点でのメリットなのかなと思っています。
- 複数のパラメータに応じた
Provider
を作成できる
Provider
ではfamily
修飾子を使用することでパラメータに応じたProvider
を作成可能でしたが、
1つのパラメータしか渡すことができませんでした。
Riverpod Generatorを用いることで、複数のパラメータを使用したProvider
を作成可能となります。
使用してみた感想
使用するパッケージが3つほど増える等の複雑さの増加があるため、
必ずRiverpod Generatorを使う必要があるか、というとそうではないと思いました。
特に今回のMirror Todoのような単純なアプリでは使用のメリットをあまり感じられなかったです。
(新感覚で面白くはありました。)
より複雑な状態管理を必要とするアプリでは役立つのかもしれない、という印象です。
アーキテクチャ
アプリのアーキテクチャについて解説します。
アーキテクチャを考えるにあたって、注意したのは以下の2点です。
- クラス、ファイルの役割を明確にすること
- UIが賢くならないようにすること(ロジックを持たないようにすること)
これをもとに考えたアーキテクチャが以下となります。
TodoRepository でロジック関係を一挙に担うことで、UIが賢くなるのを防いでいます。
(ロジック自体が少ないアプリではありますが...)
またデータベースはデータの管理をするだけ(処理は行わない)とすることで、
役割も分けられたかなと思っています。
アーキテクチャに知見のある方からすると
「リポジトリ層とデータ層の矢印は逆じゃなきゃダメだろう!」
という意見があるかもしれません。
おっしゃるとおりで、
例えばデータベースを取り替える必要があるといった要件には弱いアーキテクチャとなっています。
(この場合はTodoRepositoryとTodoDatabaseの間にインターフェースを挟んで、
TodoRepositoryはインターフェースを参照するようにする、といった変更が必要になるかなと思います。)
要件自体ないので考慮する必要はないといったらないのですが、
次作成する際には、この構成だとこういう弱点があるけど、考慮すべきか、というのをしっかり考えてアプリ作成しようと思います。
大事なことですが、これがTodoアプリのアーキテクチャの完成形だとか、
そういう話はしようとしていません。
Todoアプリの1つの例としてみていただければ、と思います。
まとめ
本記事では鏡を使わないと読めないといった意味で
使いみちのないクソアプリ、Mirror Todoを紹介しました。
Flutterでの開発体験の話や、技術面の話を記載しました。
いかがだったでしょうか?
この記事を通してFlutterに興味をもっていただけたら嬉しいです。
Flutterを触ったことがある方にとって、
DriftやRiverpod のキャッチアップの機会になれば幸いです。
今回作成したアプリのリポジトリは以下となります。
Flutterでのアプリ開発の参考にしていただければと思います。
(もし少しでもいいな、とおもっていただけたらスターいただけると幸いです。)
以上、今年のクソアプリの記事でした。
宣伝
Flutter大学 というFlutterエンジニアに特化した学習コミュニティに参加しています。
現時点で300人以上が参加する、Flutterを学ぶ人、使っている人の集まるコミュニティです。
以下の招待リンクから参加することができます。
また、普段、週刊Flutter大学というブログの記事作成をしています。
前の週にあったFlutter界隈のニュースなどをまとめたり、アプリ作成の役に立つ知識の記事を書いたりしています。
Flutter情報のキャッチアップに役立つこと間違いなしですので、
ぜひこちらも確認してみてください!
Flutterでのお仕事の依頼等の連絡はTwitterのDMまでお願い致します。
一緒にFlutterを楽しみましょう!
参考