top of page

[Flutter/dart]Reduxパターンでアイテム削除に対応する


前提

以下のようなケースを考えます。

  • アイテムの一覧がある

  • ある1つのアイテムの詳細を表示するページがある

  • 詳細ページではそのアイテムの削除ができる

  • 削除したら他のページ(一覧ページなど)に戻る

これはアプリではよくあるパターンだと思います。


課題

reduxパターンを用いている、より具体的にはProviderで状態を管理している場合、以下のような構成になっているのではないでしょうか?


  1. アプリ全体がStoreProviderの子になっており、storeの内容に変化があれば全体がリビルドされる

  2. アイテム詳細ページは、StoreConnectorによってそのアイテムをstoreから切り出す

  3. アイテム詳細ページ内で、アイテムを削除したら所定のページに遷移

//詳細ページに移行
await Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context)=> StoreConnector<AppState, ItemDetailViewModel>(
      converter: (store) 
      => ItemDetailViewModel.create(store, itemId), 
      //2 storeからitemIdのアイテムを取得
      builder: (context, model) {
        return ItemDetailPage(model);  //詳細ページ
      },
    )
  )
);
//削除ボタン
Widget _itemDeleteButton(){
  return GestureDetector(
    child: const Icon(Icons.delete),
    onTap: ()async{
       await _deleteItem();  //削除
       await Navigator.of(context).push(
         //3 別ページに移動
       );
    }
}

この場合、以下のような課題があります。

  • アイテムが削除されると、StoreProviderによりアプリ全体がリビルドされる

  • 詳細ページ作成処理も再度実行される

  • つまり、「storeから指定のアイテムを切り出す処理」(2)も再度実行される

  • すでにそのidは存在しないので、例外になる

もちろん詳細ページから抜け出すのが2の処理より早ければこの問題は起こりません(抜け出しているから詳細ページ作成処理は走らない)。

しかし、StoreProviderによるリビルドは詳細ページでの処理とは非同期に走るので、先に抜け出せることは保証できません。



解決策

  • 2の処理で、storeに指定のアイテムが存在しない場合はnullを返します。

  • StoreConnectorによる切り出し部分では、convertorの返り値がnullの場合は別途用意した別のページ(「このアイテムは削除されました」など)に遷移するようにします

await Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context)=> StoreConnector<AppState, ItemDetailViewModel?>(
      converter: (store) => 
      ItemDetailViewModel.create(store, itemId),
      builder: (context, model) {
        if(model == null){
          return ItemDeletedPage(); 
          //「このアイテムは削除されました」が表示される
        }
        return ItemDetailPage(model);
      },
    )
  )
);


最後に

根本的な解決にはなっていない気もしますが、プラットフォームに乗っかるうえではある程度設計に制約が出てしまうのは仕方ないかなと思います。


reduxに限らずProviderを用いていると、このように予期しない、制御もできないタイミングでページがリビルドされたりするので注意が必要ですね。


というか大前提として「削除されたアイテムを取り出すことなんてないだろ」と思って例外処理してないのが悪いですねw

最新記事

すべて表示

【Flutter/Dart】TextFieldで文字列をフォーマットする

やりたいこと TextFieldで入力フォームを作りたい。 例えば入力内容が金額の場合、3桁区切りで頭に¥を付けた表記にしたい。 ただしユーザにこれらを入力させるのではなく、ユーザはあくまで数字を入力するだけで、アプリ側で自動でフォーマットしたい。 方法 TextInputFormatterを継承し、所望のフォーマット処理を追加することで実現可能。 例えばこの記事などを参照。 以下は個人的に躓いた

【Flutter/Dart】iosシミュレータ起動後にデバイスに保存した画像が見つからない

現象 やってること iosシミュレータで画像をデバイスのローカルに保存 保存したパスをデータベースに保存 アプリ立ち上げ時にデータベースから画像パスを取得し、そのパスの画像を画面上に表示 起きている現象 iosシミュレータを再起動した場合、上記3で「ファイルパスが見つからない」というエラーが出る 原因 保存時のディレクトリを getApplicationDocumentsDirectory() に

【Flutter/dart】late変数が初期化されたかのチェック

やりたいこと 初期値さえ決まればあとは不変な変数がある ただし、コンストラクタ起動時にはまだ決定できない このような変数について late finalで変数を定義 (何らかのタイミングで)初期化されたかどうかをチェックし、されていなければ値を入れる(チェックしないとfinalに値を代入したエラーになるので) この場合、「初期化されたかどうか」はどのようにチェックしたらいいのか分からなかった nul

あなたの買い物をサポートする
アプリ Shop Plan

iphone6.5p2.png

​いつ何を買うかの計画を立てられるアプリです。

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

靴を大切にしよう!靴管理アプリ SHOES_KEEP

納品:iPhone6.5①.png

靴の履いた回数、お手入れ回数を管理するアプリです。

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

「後で読む」を忘れないアプリ ArticleReminder

気になった​Webサイトを登録し、指定時刻にリマインダを送れるアプリです

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png
bottom of page