🌟
【Flutter】go_router利用時、ルーティング定義を分割・切り替えるサンプル
背景
go_router を利用して、アプリ作成しています。
ルーティング定義が肥大化する懸念があり、
ルーティング定義を分割・切り替え可能か試しました。
結果、分割・切り替え可能だったため、そのサンプルを記載します。
より具体的には、認証前後で、ルーティング定義を分割・切り替えしました。
分割したルーティング定義の切り替え
分割したルーティングの切り替えについて、図解します。
1.認証前後でルーティング定義を切り替える
認証用レイヤ(AuthLayer)となる Widget を用意しました。
この認証用レイヤの中で、分割したルーティング定義を切り替えました。
分割したルーティング定義は下記です。
- 認証前(未認証)のルーティング定義:AuthRoutingWidget
- 認証後(認証済)のルーティング定義:MenuRoutingWidget
2.Widget Inspector で見るルーティング定義の切り替え
3.分割したルーティング定義の比較
応用
このサンプルでは、認証前後でルーティングの定義を分割・切り替えしました。
今後の応用として 「排他的な状態」毎にルーティングの定義を分割・切り替える。
といった設計の選択肢が持てるのではと考えています。
サンプルコード全体
サンプルコード全体
認証用レイヤ
lib/main.dart
import 'package:flutter/material.dart';
import 'auth_layer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const AuthLayer();
}
}
lib/auth_layer.dart
import 'package:flutter/material.dart';
import 'auth_routing_widget.dart';
import 'menu_routing_widget.dart';
class AuthLayer extends StatefulWidget {
const AuthLayer({super.key});
State<AuthLayer> createState() => _AuthLayerState();
}
class _AuthLayerState extends State<AuthLayer> {
// --------------------------------------------------
// Function : SignIn, SignOut
// -----------------------------
bool isSignedIn = false;
void onSignIn() {
setState(() => isSignedIn = true);
}
void onSignOut() {
setState(() => isSignedIn = false);
}
// --------------------------------------------------
Widget build(BuildContext context) {
return isSignedIn
? MenuRoutingWidget(onSignOut: onSignOut)
: AuthRoutingWidget(onSignIn: onSignIn);
}
}
分割したルーティング定義
lib/auth_routing_widget.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'signin_page.dart';
import 'signup_page.dart';
class AuthRoutingWidget extends StatelessWidget {
AuthRoutingWidget({
super.key,
required this.onSignIn,
});
final VoidCallback onSignIn;
late final goRouter = GoRouter(
initialLocation: '/signin',
routes: [
GoRoute(
path: '/signin',
builder: (context, state) => SignInPage(onSignIn: onSignIn),
routes: [
GoRoute(
path: 'signup',
builder: (context, state) => const SignUpPage(),
),
],
),
],
);
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: goRouter,
builder: (context, child) {
return child ?? const Text('No child');
},
);
}
}
lib/menu_routing_widget.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'article_detail_page.dart';
import 'article_page.dart';
class MenuRoutingWidget extends StatelessWidget {
MenuRoutingWidget({
super.key,
required this.onSignOut,
});
final VoidCallback onSignOut;
late final goRouter = GoRouter(
initialLocation: '/article',
routes: [
GoRoute(
path: '/article',
builder: (context, state) => ArticlePage(onSignOut: onSignOut),
routes: [
GoRoute(
path: 'articledetail/:id',
builder: (context, state) =>
ArticleDetailPage(id: state.params['id'] ?? ''),
),
]),
],
);
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: goRouter,
builder: (context, child) {
return child ?? const Text('No child');
},
);
}
}
認証前ページ ( SignIn, SignUp )
lib/signin_page.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class SignInPage extends StatelessWidget {
const SignInPage({
super.key,
this.title = 'signin',
required this.onSignIn,
});
final String title;
final VoidCallback onSignIn;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$title page'),
actions: [
TextButton(
child: const Text(
'SignUp > ',
style: TextStyle(color: Colors.white),
),
onPressed: () {
context.go('/signin/signup');
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('body: $title'),
ElevatedButton(
onPressed: () => onSignIn(),
child: const Text('SignIn'),
),
],
),
),
);
}
}
lib/signup_page.dart
import 'package:flutter/material.dart';
class SignUpPage extends StatelessWidget {
const SignUpPage({super.key, this.title = 'signup'});
final String title;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$title page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('body: $title'),
],
),
),
);
}
}
認証後ページ( Article, ArticleDtail )
lib/article_page.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class ArticlePage extends StatelessWidget {
const ArticlePage({
super.key,
this.title = 'article',
required this.onSignOut,
});
final String title;
final VoidCallback onSignOut;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$title page'),
actions: [
TextButton(
onPressed: () => onSignOut(),
child: const Text(
'SignOut > ',
style: TextStyle(color: Colors.white),
),
),
],
),
body: ListView.builder(
itemCount: 3,
itemBuilder: ((context, index) {
return ListTile(
title: Text('Article $index'),
onTap: () {
context.go('/article/articledetail/$index');
},
);
})));
}
}
lib/article_detail_page.dart
import 'package:flutter/material.dart';
class ArticleDetailPage extends StatelessWidget {
const ArticleDetailPage({
super.key,
this.title = 'article deltail',
required this.id,
});
final String title;
final String id;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$title page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('body: $title, id: $id'),
],
),
),
);
}
}
関連記事
本記事の続編を書きました。
Discussion