카테고리 없음

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로 이동하게 되며 데이터 또한 공유된다.

반응형