Flutter iOS 音声メモアプリ開発(Tips) localstore

こちらの記事で端末内にデータを保存するlocalstoreパッケージの基本的な使い方について記述しました。

Flutter iOS 音声メモアプリ開発(1)永続的データの保存

Flutterで開発したアプリケーションの公開に挑戦してみたいと思います。 Flutter はAndroidとiOSのどちらも開発可能ではありますが、個人的にiPhoneユーザでありますので…

Flutterのlocalstoreパッケージの動作について、もう少し詳しくみていきます。

データファイル

localstoreのデータの実態は、JSONファイルです。このファイルの中身の確認方法について調べてみました。

以下は、こちらの記事を参考にさせていただきました。 https://capibara1969.com/2836/

iOSアプリのフォルダ構成

最初にvoice_memoアプリでデータを登録しておきます。

この状態で端末をMacに接続し、Xcodeからvoice_memoアプリのファイルをダウンロードします。

XcodeのメニューからWindows>Devices and Simulators を選択して、以下の画面を表示させます。

Download Containerを選択すると、以下のAppDataがダウンロードされます。

iOSの場合、アプリケーション毎に、独自のフォルダが割り当てられるようですが、今回の場合、Documents ,Library ,SystemData ,tmpフォルダが作成されていました。

アプリで記録したユーザデータの類は、Documents以下に保存するようです。

先述のように、2つのメモを登録した状態で、ファイルをダウンロードすると、AppData/Documents/todo以下に2つのファイルができていました。

ファイルの中身は以下の通りです。

{"id":"2023-02-05 16:54:15.415980","title":"今日も1日がんばりました","time":1675583655415,"sentiment":"😀","done":false}
{"id":"2023-02-05 17:18:41.033995","title":"明日も良い1日でありますように","time":1675585121033,"sentiment":"😀","done":false}

localstoreのデータはitemごとに一つのファイルが作成されていることがわかりました。

ファイル名がTimeスタンプになっているのは、後述する理由でidをTimeスタンプにしているためです。標準的な使い方だと、ファイル名はランダムにセットされたidになります。

データの表示順

localstoreで保存したデータをListViewで表示する際に、順番が入れ替わることがありました。そこで、initState()内で_itemsをソートする一文を追加しました。

void initState() {
    _subscription = _db.collection('todos').stream.listen((event) {
      setState(() {
        final item = Todo.fromMap(event);
        _items.putIfAbsent(item.id, () => item);
        _items = SplayTreeMap.from(_items, (a, b) => a.compareTo(b));  //  <- 追加
      });
    });
    if (kIsWeb) _db.collection('todos').stream.asBroadcastStream();
    super.initState();
  }

この場合、SplayTreeMapはidでソートすることとなります。登録した順にデータを並べたかったので、idをTimeスタンプに変更することで、時間順にソートすることとしました。

      //final id = Localstore.instance.collection('todos').doc().id;  // 基本的な使い方
      final id = now.toString();                       // 今回の処理 

この方法はあまり良い方法ではないかもしれません。もっと良い方法をご存知の方がおられましたら、TwitterのDMかメールでご教示いただけますと助かります。

ちなみに、localstoreのidの生成は、以下のような処理になっていました。

  @override
  DocumentRef doc([String? id]) {
    id ??= int.parse(
            '${Random().nextInt(1000000000)}${Random().nextInt(1000000000)}')
        .toRadixString(35)
        .substring(0, 9);
    return DocumentRef(id, this);
  }