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

最新記事

すべて表示

現象 あるアイコンは長押し時に所定の動作を実行します これを実現するために、GestureDetectorのonLongPressに処理を登録していました しかしいざビルドしてみると、アイコンが表示されたタイミングで処理が実行されてしまいました 以下が該当部分のソースコードです。 さあ、どこが間違っているでしょう? return GestureDetector( child: Containe

やりたいこと Reduxで状態管理をする。状態はAppStateとする。 アプリ開始時にリモートのデータベースからデータの取得を開始 データを取得 取得中は待機マークを表示 取得したデータからAppStateを作成 作成したAppStateを元にUIを描画 方法 3、4、5を実現するためには、「取得完了」のフラグをAppStateに追加しましょう(※1)。 ビューはこのフラグを見て表示を切り替えま

やりたいこと 画像を表示するWidgetがある 画像はリモートのストレージから取得する 取得するまでは時間がかかるので、その間は待機中である旨を伝える表示としたい。 よくあるのはぐるぐる 解決策 FutureBuildrで表示を切り替えましょう。 FutureBuilder<ImageProvider>( future: getImage(), builder: (context, s

あなたの買い物をサポートする
アプリ 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