🏰
GoRouterで次のページにListの値を渡したい
工夫しないと上手くいかなかった!
go_routerを使用して、画面遷移した先のページにString型の値を渡す記事はよく見かける気がしますが、Listを使った記事はないような気がしたので、書くことにしました。
モデルクラスを作る
今回はダミーのデータを保存するモデルクラスを作ります。
domain/item_model.dart
// Listで表示するアイテムのモデルを定義します。
class Item {
Item({required this.name});// コンストラクターで、nameプロパティを必須にしています。
final String name;// このクラスは、名前を表すnameプロパティを持っています。
}
作ったモデルクラスは、状態を保つことができる Notifierで使用する。今回は追加と削除ができる機能を作っています。これは実験用に作ったコードなので、いいコードではないかもです🙅
application/item_state.dart
import 'package:design_patterns/domain/item_model.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final errorMessageProvider = StateProvider<String?>((ref) => null);
final itemListProvider =
NotifierProvider<ItemListNotifier, List<Item>>(ItemListNotifier.new);
class ItemListNotifier extends Notifier<List<Item>> {
build() {
return [];
}
void addItem(String name) {
try {
if (name.isEmpty) {
ref.read(errorMessageProvider.notifier).state = 'nullはダメです!';
}
state = [...state, Item(name: name)];
} catch (e) {
throw (e.toString());
}
}
void removeItem(int index) {
state = [...state]..removeAt(index);
}
}
画面遷移するページ
こちらのページで入力フォームをから新しいリストを作成して次のページへ値を渡します。
lib/presentation/ui/item_list.dart
import 'package:design_patterns/application/item_state.dart';
import 'package:design_patterns/presentation/router_path.dart';
import 'package:design_patterns/presentation/snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
final controllerProvider = StateProvider((ref) => TextEditingController());
final errorMessageProvider = StateProvider<String?>((ref) => null);
class ListItemPage extends ConsumerWidget {
const ListItemPage({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
final itemList = ref.watch(itemListProvider);
final _controller = ref.watch(controllerProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Item List'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Item Name',
),
),
),
ElevatedButton(
onPressed: () {
ref.read(itemListProvider.notifier).addItem(_controller.text);
SnackBarUtil.show(context, 'Item added');
},
child: const Text('Add Item')),
Expanded(
child: ListView.builder(
itemCount: itemList.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
// pushの中に、extraとしてitemList[index]を渡す
context.push(Routes.detail, extra: itemList[index]);
},
title: Text(itemList[index].name),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
ref.read(itemListProvider.notifier).removeItem(index);
SnackBarUtil.show(context, 'Item removed');
},
),
);
},
),
),
const SizedBox(height: 16),
],
),
),
);
}
}
画面遷移したページ
コンストラクタを作って前のページから値を受け取って、UIに表示できる様にしています。
lib/presentation/ui/detail.dart
import 'package:design_patterns/domain/item_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class DetailPage extends ConsumerWidget {
const DetailPage({super.key, required this.item});
final Item item;
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(item.name),
],
),
),
);
}
}
GoRouterの設定ファイル
Listの値を次のページへ渡すには、以下のように書きます。やったことなかったので結構躓きました!
extra propertyを使用して、画面遷移の情報とオブジェクトを次のページへ渡すことができるそうです。
if文でnullチェックをして、値があれば画面遷移先に値を渡すことができて、nullだったら、SomeErrorPageへ画面遷移するようになっています。でもダミーデータがnullだと、SomeErrorPageへ画面遷移しなくなった?
後でコードを書き換えたら、SomeErrorPageへ画面遷移しなくなりました😅
router.dart
import 'package:design_patterns/domain/item_model.dart';
import 'package:design_patterns/presentation/router_path.dart';
import 'package:design_patterns/presentation/ui/detail.dart';
import 'package:design_patterns/presentation/ui/home.dart';
import 'package:design_patterns/presentation/ui/item_list.dart';
import 'package:design_patterns/presentation/ui/some_error.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
// goRouterProviderは、GoRouterを提供するProviderです。
final goRouterProvider = Provider<GoRouter>((ref) {
return GoRouter(
routes: [
GoRoute(
path: Routes.home, // pathは、GoRouterで使用するパスを定義します。
builder: (context, state) {
return const HomePage();
},
),
GoRoute(
path: Routes.item,
builder: (context, state) {
return const ListItemPage();
},
),
// 次のページに遷移する際に、extraでItemクラスのnameプロパティを渡しています。
GoRoute(
path: Routes.detail,
builder: (context, state) {
final item = state.extra as Item?;
// nullチェックを行います。
if (item != null) {
// extraで渡されたItemクラスのnameプロパティを表示するぺージに遷移します。
return DetailPage(item: item);
} else {
// エラーが発生した場合は、SomeErrorPageに遷移します。
return const SomeErrorPage(); // 適切なエラーページに差し替えてください
}
},
),
],
);
});
まとめ
ざっくりとですが、解説してみました。もっと凝ったものを作りたかったのですが、良いアイディアがなかったので、ダミーのデータを使ったものにしました。誰かのお役に立てると嬉しいです。
全体のソースコードはこちらのリポジトリにあります。
Discussion