🙌
【Flutter】go_routerでメニューバーがある状態で画面遷移
はじめに
Flutterのgo_routerを使って、メニューバーがある状態で画面遷移を行う方法です。
-
実行環境
Chrome -
flutterのバージョン
$ flutter --version
Flutter 3.3.10 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 135454af32 (5 weeks ago) • 2022-12-15 07:36:55 -0800
Engine • revision 3316dd8728
Tools • Dart 2.18.6 • DevTools 2.15.0
- go_router
pubspec.yamlにgo_routerを追加してください
dependencies:
go_router: ^5.2.1
機能
- ログイン画面でログインボタンを押すと、メニューバーを表示する画面に遷移し、ログアウトボタンを押すとログイン画面に戻ります。
- 画面遷移のアニメーションの設定をしていないので、遷移の仕方がおかしなことになっています。
ポイント
メニューバーがある画面のルーティングはShellRouteで定義します
実装手順
-
メニューバー(HomeNavigationRail)を作成。
- ここでどのタブがアクティブにするか、タブを押したらどこに遷移するかを定義。
-
各画面を作成。
-
ルートの定義でメーニューバーを表示させる画面の遷移をShellRouteでまとめる(メニューバーを使わないルートはShellRouteで囲まない)
コード
メニューバーの実装
home_navigation_rail.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class HomeNavigationRail extends StatelessWidget {
const HomeNavigationRail({
required this.child,
Key? key,
}) : super(key: key);
final Widget child;
Widget build(BuildContext context) {
return Scaffold(
body: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NavigationRail(
trailing: ElevatedButton(
onPressed: (() async {
context.go('/login');
}),
child: const Text(
"ログアウト",
),
),
backgroundColor: const Color.fromARGB(255, 194, 227, 255),
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text(
"ホーム",
),
),
NavigationRailDestination(
icon: Icon(Icons.article),
label: Text(
"記事一覧",
),
),
NavigationRailDestination(
icon: Icon(Icons.face),
label: Text(
"ユーザー一覧",
),
),
NavigationRailDestination(
icon: Icon(Icons.settings),
label: Text(
"設定",
),
),
],
selectedIndex: calculateSelectedIndex(context),
onDestinationSelected: (int index) =>
onItemTapped(index, context)),
Flexible(
child: Container(
padding: const EdgeInsets.all(16),
child: child,
),
)
],
),
);
}
static int calculateSelectedIndex(BuildContext context) {
final String location = GoRouterState.of(context).location;
if (location.startsWith('/home/article')) {
return 1;
}
if (location.startsWith('/home/user')) {
return 2;
}
if (location.startsWith('/home/setting')) {
return 3;
}
return 0;
}
void onItemTapped(int index, BuildContext context) {
switch (index) {
case 0:
GoRouter.of(context).go('/');
break;
case 1:
GoRouter.of(context).go('/home/article');
break;
case 2:
GoRouter.of(context).go('/home/user');
break;
case 3:
GoRouter.of(context).go('/home/setting');
break;
}
}
}
ルーティングの実装
page_router.dart
import 'package:flutter/material.dart';
import 'package:flutter_tab_base/each_screen.dart';
import 'package:flutter_tab_base/home_navigation_rail.dart';
import 'package:go_router/go_router.dart';
class PageRouter {
static final GoRouter router =
GoRouter(initialLocation: '/login', routes: <RouteBase>[
GoRoute(
path: '/login',
builder: (BuildContext context, GoRouterState state) {
return const LoginPage();
},
),
ShellRoute(
builder: (BuildContext context, GoRouterState state, Widget child) {
return HomeNavigationRail(child: child);
},
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
routes: <RouteBase>[
GoRoute(
path: 'home/article',
builder: (context, state) => const ArticlePage()),
GoRoute(
path: 'home/user',
builder: (context, state) => const UserPage()),
GoRoute(
path: 'home/setting',
builder: (context, state) => const SettingPage()),
],
),
],
)
]);
}
各画面の実装
each_screen.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
context.go('/');
},
child: const Text('ログイン'))),
);
}
}
class UserPage extends StatelessWidget {
const UserPage({super.key});
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('ユーザー'),
),
);
}
}
class ArticlePage extends StatelessWidget {
const ArticlePage({super.key});
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('アーティスト'),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('ダッシュボード'),
),
);
}
}
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('設定'),
),
);
}
}
ルーティングの設定
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_tab_base/page_router.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: PageRouter.router,
);
}
}
終わりに
これだと画面遷移の仕方がおかしいので、遷移するときのアニメーションを消したら良いと思います。
GitHub
参考
Discussion