카테고리 없음
flutter) 바텀 네비게이션바 + goRouter
STUFIT
2024. 6. 19. 18:01
반응형
하단 네비게이션을 만드는데 기존에 있던 코드와 별개로 goRouter를 이용해서 하단 네비게이션 바를 사용하고 싶었는데, 어느 해외 유튜버를 보고 해결책을 찾을 수 있었다.
먼저 기존의 바텀 네비게이션바 코드이다.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomBottomNavBar extends StatefulWidget {
final int currentIndex;
final Function(int) onItemSelected;
const CustomBottomNavBar({
Key? key,
required this.currentIndex,
required this.onItemSelected,
}) : super(key: key);
@override
_CustomBottomNavBarState createState() => _CustomBottomNavBarState();
}
class _CustomBottomNavBarState extends State<CustomBottomNavBar> {
@override
Widget build(BuildContext context) {
return Container(
height: 100.h, // 원하는 높이로 조절
child: BottomNavigationBar(
backgroundColor: Colors.white, // 네비게이션 색상 조절
iconSize: 70.sp, // 기본 아이콘 크기 조절
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w), // 아이콘 사이 간격 조절
child: Image.asset(
'assets/images/icons/navigator/home.png',
width: 30.w,
height: 30.h,
),
),
label: '메인',
),
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w), // 아이콘 사이 간격 조절
child: Image.asset(
'assets/images/icons/navigator/ranking.png',
width: 30.w,
height: 30.h,
),
),
label: '통계',
),
BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w), // 아이콘 사이 간격 조절
child: Image.asset(
'assets/images/icons/navigator/back.png',
width: 30.w,
height: 30.h,
),
),
label: '달력',
),
],
currentIndex: widget.currentIndex,
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.grey,
onTap: widget.onItemSelected,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: TextStyle(
fontFamily: 'BMJUA',
fontSize: 12.sp,
),
unselectedLabelStyle: TextStyle(
fontFamily: 'BMJUA',
fontSize: 12.sp,
),
selectedIconTheme: IconThemeData(
size: 40.sp, // 선택된 아이콘의 크기 조절
color: Colors.amber[800], // 선택된 아이콘의 색상
),
unselectedIconTheme: IconThemeData(
size: 30.sp, // 선택되지 않은 아이콘의 크기 조절
color: Colors.grey, // 선택되지 않은 아이콘의 색상
),
),
);
}
}
이번에는 goRouter에 사용될 바텀 네비게이션 코드이다.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:wallet_worrior/src/state/calendar/calendar_provider.dart';
import 'package:wallet_worrior/src/state/expense/daily_budget_provider.dart';
import 'package:wallet_worrior/src/state/expense/expense_provider.dart';
import 'package:wallet_worrior/src/state/statistics/statistic_provider.dart';
class ScaffoldWithNavBar extends StatefulWidget {
final StatefulNavigationShell navigationShell;
const ScaffoldWithNavBar({super.key, required this.navigationShell});
@override
State<ScaffoldWithNavBar> createState() => _ScaffoldWithNavBarState();
}
class _ScaffoldWithNavBarState extends State<ScaffoldWithNavBar> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: widget.navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: const<BottomNavigationBarItem>[
BottomNavigationBarItem(icon: ImageIcon(AssetImage('assets/images/icons/category/homes.png')),label: '메인'),
BottomNavigationBarItem(icon: ImageIcon(AssetImage('assets/images/icons/category/statistic.png')),label: '통계'),
BottomNavigationBarItem(icon: ImageIcon(AssetImage('assets/images/icons/category/days.png')),label: '달력'),
],
currentIndex: widget.navigationShell.currentIndex,
onTap: (int index)=> _onTap(context,index),
selectedItemColor: Colors.amber[800],
unselectedItemColor: Colors.grey,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: TextStyle(
fontFamily: 'BMJUA',
fontSize: 12.sp,
),
unselectedLabelStyle: TextStyle(
fontFamily: 'BMJUA',
fontSize: 12.sp,
),
selectedIconTheme: IconThemeData(
size: 40.sp, // 선택된 아이콘의 크기 조절
color: Colors.amber[800], // 선택된 아이콘의 색상
),
unselectedIconTheme: IconThemeData(
size: 30.sp, // 선택되지 않은 아이콘의 크기 조절
color: Colors.grey, // 선택되지 않은 아이콘의 색상
),
),
);
}
// void _onTap(BuildContext context, int index){
void _onTap(BuildContext context, int index) {
switch (index) {
case 0:
Provider.of<DailyBudgetProvider>(context, listen: false).fetchDailyBudget(DateTime.now());
break;
case 1:
Provider.of<StatisticsProvider>(context, listen: false).loadStatistics();
break;
case 2:
Provider.of<CalendarProvider>(context, listen: false).loadCalendarList(DateTime.now());
break;
}
widget.navigationShell.goBranch(index,initialLocation: index == widget.navigationShell.currentIndex);
}
}
onTap에서는 새로고침을 위해 클릭시마다 provider를 사용해서 데이터를 갱신하였다.
이제 이것을 goRouter에 적용해보자.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:wallet_worrior/enum/login_platform.dart';
import 'package:wallet_worrior/shared/scaffold_navigation_shell.dart';
import 'package:wallet_worrior/src/auth/token.dart';
import 'package:wallet_worrior/src/model/budget/expense_model.dart';
import 'package:wallet_worrior/src/state/login/login_state.dart';
import 'package:wallet_worrior/src/view/screens/calendar/calendar_page.dart';
import 'package:wallet_worrior/src/view/screens/login/login_page.dart';
import 'package:wallet_worrior/src/view/screens/login/sign_up_page.dart';
import 'package:wallet_worrior/src/view/screens/login/splash_page.dart';
import 'package:wallet_worrior/src/view/screens/main/expense_add_page.dart';
import 'package:wallet_worrior/src/view/screens/main/expense_list_page.dart';
import 'package:wallet_worrior/src/view/screens/main/main_page.dart';
import 'package:wallet_worrior/src/view/screens/statistics/statistics_page.dart';
import 'package:wallet_worrior/src/view/screens/user/user_detail_page.dart';
import 'package:wallet_worrior/src/view/screens/user/user_list_page.dart';
class AppRouter {
static const String splashPath = '/splash';
static const String loginPath = '/login';
static const String termPath = '/term';
static const String userListPath = '/user/list';
static const String userDetailsPath = '/user/details/:userNo';
static const String signUpPath = '/signup';
static const String mainPagePath = '/main';
static const String expenseAddPath = '/expense';
static const String expenseListPath = '/expense/list';
static const String statisticsPath = '/expense/statistics';
static const String calendarPath = '/expense/calendars';
static GoRouter createRouter(LoginState loginState) {
return GoRouter(
refreshListenable: loginState,
initialLocation: loginPath,
routes: [
_goRoute(splashPath, const SplashPage()),
_goRoute(loginPath, const LoginPage()),
_goRoute(userListPath, const UserListPage()),
_goRoute(signUpPath, SignUpPage()), // 회원가입 페이지 라우트 추가
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) =>
ScaffoldWithNavBar(navigationShell: navigationShell),
branches: [
StatefulShellBranch(
routes: <RouteBase>[_goRoute(mainPagePath, const MainPage())],
),
StatefulShellBranch(
routes: <RouteBase>[
_goRoute(statisticsPath, const StatisticsPage())
],
),
StatefulShellBranch(
routes: <RouteBase>[
_goRoute(calendarPath, const CalendarPage())
],
),
],
),
GoRoute(
path: expenseAddPath,
builder: (context, state) {
final Map<String, dynamic> extras =
state.extra as Map<String, dynamic>;
final selectedDate = extras['selectedDate'] as DateTime;
final expense = extras['expense'] as Expense?;
return ExpenseAddPage(
selectedDate: selectedDate, expense: expense);
}),
GoRoute(
path: expenseListPath,
builder: (context, state) {
final selectedDate = state.extra as DateTime;
return ExpenseListPage(selectedDate: selectedDate);
}),
GoRoute(
path: userDetailsPath,
builder: (context, state) {
final userNo = int.parse(state.pathParameters['userNo']!);
return UserDetailsPage(userNo: userNo);
},
),
],
redirect: (context, state) => _redirectLogic(loginState, state),
);
}
static Future<String?> _redirectLogic(
LoginState loginState, GoRouterState state) async {
final accessToken = await TokenStorage.getAccessToken();
final providers = await TokenStorage.getLoginType();
final isLoggedIn = accessToken != null;
final platform = providers != LoginPlatform.none;
final isAtLogin = state.uri.path == loginPath;
print(
'소셜:${platform},맴버:${loginState.isMember}, 로그인:${isLoggedIn}, 고잉로그인:${isAtLogin}');
if (state.uri.path == splashPath) {
if (isLoggedIn) {
return mainPagePath;
}
}
// return null;
}
static GoRoute _goRoute(String path, Widget view) =>
GoRoute(path: path, builder: (context, state) => view);
}
여기서 나는 StatefulshellRouter를 사용해서 위에서 만든 바텀 네비게이션 클래스를 리스트화 하였다.
이렇게 되면 하단 버튼을 눌렀을때 내가 지정해놓은 path로 이동하게 되며 데이터 또한 공유된다.
반응형