[Flutter/dart]Reduxパターンでアイテム削除に対応する
前提
以下のようなケースを考えます。
アイテムの一覧がある
ある1つのアイテムの詳細を表示するページがある
詳細ページではそのアイテムの削除ができる
削除したら他のページ(一覧ページなど)に戻る
これはアプリではよくあるパターンだと思います。
課題
reduxパターンを用いている、より具体的にはProviderで状態を管理している場合、以下のような構成になっているのではないでしょうか?
アプリ全体がStoreProviderの子になっており、storeの内容に変化があれば全体がリビルドされる
アイテム詳細ページは、StoreConnectorによってそのアイテムをstoreから切り出す
アイテム詳細ページ内で、アイテムを削除したら所定のページに遷移
//詳細ページに移行
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