auto_routeで、次のページに値を渡す
対象者
- auto_routeを使ったことがある
- auto_routeに興味がある
- タイプセーフに扱えるらしいのを知りたい
プロジェクトの説明
go_router
だと、タイプセーフにデータを扱えないと聞く?
何それ...
「タイプセーフ」または「型安全」とは、プログラミング言語の特性の一つで、その言語が型エラー(データ型が期待するものと異なるために発生するエラー)を防止する仕組みを持っていることを指します。
次のページに値を渡したいユースケースで意識するようになった。通常は、Stringしか渡せないらしい?
過去に書いた記事で紹介しているのですが、state.extra
を使う必要がありました😱
go_router_builder使えばタイプセーフに扱えるそうだが...
勉強会でも聞いたことあるが、auto_route
だとタイプセーフに扱うことができるらしい?
今回だと、 Bookクラスをコンストラクタ引数に渡すのですが、Book型だから、そのまま渡すだけだが、go_router
だと簡単にはいかない💦
go_routerの場合
ルートを定義しますよね。そして、次の画面に値渡すけどお決まりのコードを書かないと型のエラーが出てくる?
ルートの定義しているファイルで、パスを設定してextra
とas
で型変換を行う必要がありました。最近これが面倒臭いと感じるようになってきた💦
// 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(); // 適切なエラーページに差し替えてください
}
},
),
],
);
});
UI側のコードはコンスタラクタ引数に、extra: itemList[index]
で渡してあげる必要があります。Navigator1.0(Navigator.push)はこれを意識しなく良い。最近これを書くのが辛くなってきた笑
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');
},
),
);
},
),
),
auto_routeだとどうなのか?
Navigator.pushと変わらないみたいだ😅
何も特別なことはしていません。
ファイルはこれだけです。
モデルを作る
データを保持する入れものを作りましょう。
class Book {
final String title;
Book(this.title);
}
2. ページを作る
値を渡すページ。コンストラクタの引数を必須にしておく。Bookクラスをデータ型に指定する。
import 'package:auto_route/auto_route.dart';
import 'package:auto_route_app/screen/book.dart';
import 'package:flutter/material.dart';
()
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key, required this.book});
final Book book;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.indigo,
title: Text(book.title),
),
body: Center(
child: Text(book.title),
),
);
}
}
ダミーのデータを自動生成して、リストに表示してタップすると、次のページへ渡せるようにしてます。 go_router
みたいにおまじないみたいなコードは書きません。画面遷移するコードとコンストラクタ引数に値を渡すだけですね。
リストを表示するページ
import 'package:auto_route/auto_route.dart';
import 'package:auto_route_app/screen/book.dart';
import 'package:auto_route_app/screen/router.gr.dart';
import 'package:flutter/material.dart';
()
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Widget build(BuildContext context) {
final books = List.generate(10, (index) => Book('Book $index'));
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.indigo,
title: const Text('Book'),
),
body: ListView.builder(
itemCount: books.length,
itemBuilder: (context, index) {
final book = books[index];
return ListTile(
onTap: () {
final book = books[index];
// Data Type Bookを引数に渡す
context.router.push(DetailScreen(book: book));
},
title: Text(book.title),
);
},
),
);
}
}
2. ルートを定義する
ページを作ったらルートを定義しましょう。build_runer
のコマンドを実行するだけです。routes
の中のAutoRouteは、コードが自動生成されないと、コード保管で出てこないので注意。
import 'package:auto_route/auto_route.dart';
import 'package:auto_route_app/screen/router.gr.dart';
// flutter pub run build_runner watch --delete-conflicting-outputs
(replaceInRouteName: 'Page,Route')
class AppRouter extends $AppRouter {
List<AutoRoute> get routes => [
AutoRoute(page: HomeScreen.page, initial: true),
AutoRoute(page: DetailScreen.page),
];
}
あとは、実行するだけですね。
import 'package:auto_route_app/screen/router.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final _appRouter = AppRouter(); // auto_routeのrouterを追加
Widget build(BuildContext context) {
return MaterialApp.router( // MaterialApp.routerに変更
routerConfig: _appRouter.config(), // auto_routeのrouterを追加
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
[home page]
[detail page]
感想
どうです。「簡単でしよ」なぜこんな便利なパッケージが流行っていないのか謎だ.....
流行っていないら流行らせようかな。情報が少ないから、機能実装するときに困るんですよね😅
Discussion