Flutter(for web)で"フリック入力ミスったー"を作った

表題の通り、Flutter(for web)を使用してフリック入力ミスを再現するWebアプリ、「フリック入力ミスったー」を作成しました。

フリック入力ミスったー/Flick Input Mistake Simulator

f:id:nanmo:20200308203047g:plain

手元にある範囲でしか動作確認できてませんが、大体のブラウザで問題なく動作すると思います。

制作中、Android向けにビルドして動作確認もおこなっていますが、Android/iOSアプリとしてリリースする予定は今のところありません。

制作メモ

フリック入力によるミスのうち、タッチ開始位置を間違えたり、フリックする方向を間違えたりするのをあえて再現することに面白みがあるんじゃないかと考えついて、今作るならReactでWebアプリかな? と思って調べつつ作りつつFlutterに移行しつつで大体一か月ぐらいで思いついた程度の実装が済んだので公開しました。

f:id:nanmo:20200308202928g:plain
Reactで作ったプロトタイプ

Reactから始めたはずがFlutterになったのは、

Reactのドキュメントを読み込んでJavascriptでざっくり作る
->Material UI導入してUIを付ける
->スタイリングが面倒そうなことに気づく
->進捗を方々に見せつつReact Native(for web)に移行した方が良いのでは? と考え始める
->Flutter(for web)という手もあると教えてもらう
->React Native, React Native(Expo), Flutterをそれぞれ調べつつ比較する
->事情はよく分からないが、React NativeのSliderがDeprecatedになっていることが気になる
->Betaだけどそこそこまともに動くみたいだし、Flutter(for web)でええか...

という経過をたどったからです。

フリック入力ミスを再現するのにあたっては、事前に考えていた機能設計で文字列と連想配列と2次元配列が扱えれば問題ないと判断していたので、JavascriptからDartに移行するのも抵抗はありませんでした。(いざやってみると、文法の違いや何やらで戸惑う点はありましたが)

フリック入力ミスを再現する仕組み

ひらがな、ローマ字、数字といった入力パネルをキーとして連想配列(Map)を作成し、Mapのバリューとして入力する文字を並べた2次元配列を用意しました(1次元目がタッチ位置に、2次元目がフリック方向に対応する)、これを一文字ずつ照らし合わせて、該当するものがあればタッチ位置ミスやフリック方向ミスの判定を行って、タッチ位置ミスであれば1次元目の選択をランダムにずらし、フリック方向ミスであれば2次元目の選択をランダムにずらして選択・表示する、というやり方です。(二つのミスは独立に判定しているので、両方発生する場合もあります)

//Dart
  static const Map flickInputTextMap = {
    'hiragana': [
      ['あ', 'い', 'う', 'え', 'お'],
      ['か', 'き', 'く', 'け', 'こ'],
      ['さ', 'し', 'す', 'せ', 'そ'],
      ['た', 'ち', 'つ', 'て', 'と'],
      ['な', 'に', 'ぬ', 'ね', 'の'],
      ['は', 'ひ', 'ふ', 'へ', 'ほ'],
      ['ま', 'み', 'む', 'め', 'も'],
      ['や', '「', 'ゆ', '」', 'よ'],
      ['ら', 'り', 'る', 'れ', 'ろ'],
      ['わ', 'を', 'ん', 'ー'],
      ['、', '。', '?', '!'],
    ],
    //以下パネルごとにMap定義が続く

ローマ字・数字の入力パネルや、ひらがなも濁音・半濁音で被る文字があるので、照らし合わせる前に入力パネルの順番をシャッフルするような処理も入れています。

該当する文字が無い場合は全走査することになるので、最悪1文字ごとに3次元配列にアクセスするような感じになるので、自動実行の際は最大文字数を設定して制限するようにしています。

上の方法でのシミュレートは、変換ミスや、パネルを間違えるといった場合が含まれないことになりますが、てにをはの間違いぐらいが面白みがあると思っていたのと、実装難度のバランスを考えてこうした方法になっています。(漢字を開いてさらに間違えてどうこうとかやりたくなかった)

中華フォント問題

Flutterでビルドしたアプリが日本語で動作させるといわゆる中華フォント問題が発生することがあり、いくつか解決法があるようなのですが、今回は flutter_localizations を使ってサポート言語に日本語のみを設定する方法をとりました。(そのうち公式に修正されるかもしれないので、いつまでこういった類の作業が必要なのかは謎)

//Dart
//Flutter 1.14.6 / channel beta
import 'package:flutter_localizations/flutter_localizations.dart';
//中略
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'material app title',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [const Locale('ja', 'JP')],
//後略

作ってみて

  • 型が無いのはやっぱり辛い時もある(React/JSでイベントオブジェクトという名の別物に行き当たった)
  • Flutter/Dartはドキュメント自体はそろっているけど、やっぱりまだ日本語のドキュメントが少なく感じた。
  • Flutterのウィジット/コンポーネントは種類も機能も充実しているが、同じ機能を実現するのにいくつも手段があるので、それはそれで面倒に感じた。
  • React/Github Pagesで考えていたものが気がついたらFlutter(for web)/Netlifyになっていた。どういうことなの...
  • ReactもFlutterもsetStateしたあとのStateの更新タイミングで結構困りがちだった

(予定はないけど)更新するなら

  • 入力パネルの順番シャッフル頻度の調整
    • 今だと文章全体のルーチンが始める前に一回シャッフルするだけなので。ただ、あまり頻繁にやるとメモリを食いつぶすような気もして微妙。
  • UIの調整
    • 変換対象テキストと変換後テキストの位置が離れているので、UIコンポーネントの順番を入れ替えるか、オプションをまとめて閉じておくことができるようにする
  • 多言語対応
    • 英語切り替え対応はしたいけど、そもそも日本語圏以外でフリック入力ってどういう扱いなんやろか...
  • アイコン
    • 各ストアのアプリで出すなら、Flutterデフォルトのじゃないのがやっぱりほしい

最後に

フリック入力のミスを再現するアプリ」のアイコンってなんだよと思いながら、マテリアルデザインのドキュメント読みながらひねり出したのが次の画像になります。

f:id:nanmo:20200308202215p:plain

以上です。