[개발글쓰기 2일차] GoRouter를 이용한 Bottom Navigation Bar - 2 : shellRoute 응용 , 프로젝트에 적용하기

이혁진·2024년 3월 11일
1
post-thumbnail

전 포스팅에서는 GoRouter 를 이용해 navigation Bar 을 구현하는 방법에 대해 알아보았습니다.

이번 포스팅에서는 ShellRoute를 응용하여 프로젝트에 적용해보는 과정을 담아보겠습니다.

구현 목표

개발하고자 하는 구현 목표는 아래에 네비게이션 바였습니다. 처음 구현을 하고자 할 때 궁금한 점은 다음과 같았습니다.

  • ShallRoute 의 문법은 무엇이지?
  • 현재 태스트 앱에서는 '/' 페이지를 거치고 이동해야한다. 어떻게 구현할 수 있을까?
  • ToDo 페이지는 정보를 유지해야하지만, Board와 My 페이지 같은 경우는 정보를 유지하지 않고 각 페이지를 새로고침해야한다 어떻게 구현 할 수 있을까?

ShellRoute의 문법

ShellRoute는 GoRouter 라우팅 시스템 내에서 중첩된 라우팅을 구현하기 위한 클래스입니다. ShellRoute를 사용하면 애플리케이션의 주요 부분(예: 탭 바, 드로어)을 공통 UI 셸로 유지하면서 내부 컨텐츠를 독립된 Navigator로 관리할 수 있습니다. 기본 문법은 다음과 같습니다:

ShellRoute(
  routes: [
    GoRoute(
      path: '/somePath',
      builder: (context, state) => SomeWidget(),
    ),
    // 추가 라우트...
  ],
)

기본 페이지('/' 페이지)를 거치는 방법

애플리케이션에서 모든 페이지가 기본 페이지('/')를 거치도록 구성하려면, initialLocation 속성을 사용하여 GoRouter를 설정할 때 기본 경로를 지정할 수 있습니다. 예를 들어, 모든 페이지 요청이 '/' 경로를 거쳐 이동하도록 설정하려면 다음과 같이 구현할 수 있습니다:

final goRouter = GoRouter(
  initialLocation: '/',
  routes: [
    ShellRoute(
      path: '/',
      builder: (context, state, child) => BasePage(child: child),
      routes: [
        GoRoute(
          path: 'todo',
          builder: (context, state) => TodoPage(),
        ),
        // 추가 라우트...
      ],
    ),
  ],
);

상태를 유지하지 않는 페이지 구현

예제에서는 창의 상태를 저장하였지만, 실제 프로젝트에서는 상태를 저장하지 않고 페이지를 이동합니다. 이때는 StatefulShellRoute 가 아닌 ShellRoute 를 사용해야 합니다.

  ShellRoute(
      navigatorKey: _shellNavigatorKey,
      builder: (context, state, child) =>
          BottomNavigationBarScaffold(child: child),
      routes: [
        GoRoute(
            path: 'todo',
            pageBuilder: (context, state) =>
                const NoTransitionPage(child: ToDoPage())),
        GoRoute(
            path: 'board',
            pageBuilder: (context, state) => const NoTransitionPage(
                  child: TeamsListScreen(),
                )),
        GoRoute(
            path: 'my',
            pageBuilder: (context, state) => const NoTransitionPage(
                  child: MyPage(),
                )),
      ],
    ),

실제 적용

프로젝트에 적용하는 과정은 다음과 같았습니다.

  1. body 를 주입받는 BottomNavigation Bar 가 존재하는 Scaffold 위젯 생성

class BottomNavigationBarScaffold extends StatefulWidget {
  const BottomNavigationBarScaffold({Key? key, required this.child})
      : super(key: key);
  final Widget child;

  @override
  State<BottomNavigationBarScaffold> createState() =>
      _BottomNavigationBarScaffoldState();
}

class _BottomNavigationBarScaffoldState
    extends State<BottomNavigationBarScaffold> {
  int selectedIndex = 0;

  void onDestinationSelected(int index) {
    setState(() {
      selectedIndex = index;
    });
    switch (index) {
      case 0:
        context.go('/todo');
        break;
      case 1:
        context.go('/board');
        break;
      case 2:
        context.go('/my');
      // 추가 탭이 있다면 여기에 구현
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: widget.child,
      // bottomNavigationBar: BottomNavigationBar(
      //   onTap: changeTab,
      //   currentIndex: currentIndex,
      //   items: const [
      //     BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
      //     BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Chat'),
      //     // 추가 탭이 있다면 여기에 아이템 추가
      //   ],
      // ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: selectedIndex,
        destinations: const [
          NavigationDestination(label: 'Todo', icon: Icon(Icons.check_circle)),
          NavigationDestination(label: 'Board', icon: Icon(Icons.dashboard)),
          NavigationDestination(label: 'My', icon: Icon(Icons.person)),
        ],
        onDestinationSelected: onDestinationSelected,
      ),
    );
  }
}
  1. Route Config 에 ShellRoute 를 추가


final _shellNavigatorKey = GlobalKey<NavigatorState>();

final router = GoRouter(initialLocation: '/', routes: [
  GoRoute(path: '/', builder: (context, state) => const HomeScreen(), routes: [
    GoRoute(
      path: 'example',
      name: 'example',
      builder: (context, state) => const FormDemoScreen2(),
    ),
    GoRoute(
      path: 'login',
      name: 'login',
      builder: (context, state) => const LoginScreen(),
    ),
    GoRoute(
      path: 'signup',
      name: 'signup',
      builder: (context, state) => const SignInScreen(),
    ),
    GoRoute(
      path: 'emailSignup',
      name: 'emailSignup',
      builder: (context, state) => const EmailSignInScreen(),
    ),
    GoRoute(
      path: 'teamsList',
      name: 'teamsList',
      builder: (context, state) => const TeamsListScreen(),
    ),
    GoRoute(
      path: 'teamCreate',
      name: 'teamCreate',
      builder: (context, state) => const TeamCreateScreen(),
    ),
    GoRoute(
      path: 'teamDetail',
      name: 'teamDetail',
      builder: (context, state) => TeamDetailScreen(),
    ),
    GoRoute(
      path: 'projectDetail',
      name: 'projectDetail',
      builder: (context, state) => const ProjectDetailScreen(),
    ),
    GoRoute(
      path: 'projectCreation',
      name: 'projectCreation',
      builder: (context, state) => ProjectCreationScreen(),
    ),
    GoRoute(
      path: 'projectUpdate',
      name: 'projectUpdate',
      builder: (context, state) => ProjectUpdateScreen(),
    ),
    GoRoute(
      path: 'main',
      name: 'main',
      builder: (context, state) => const MainScreen(),
    ),
    ShellRoute(
      navigatorKey: _shellNavigatorKey,
      builder: (context, state, child) =>
          BottomNavigationBarScaffold(child: child),
      routes: [
        GoRoute(
            path: 'todo',
            pageBuilder: (context, state) =>
                const NoTransitionPage(child: ToDoPage())),
        GoRoute(
            path: 'board',
            pageBuilder: (context, state) => const NoTransitionPage(
                  child: TeamsListScreen(),
                )),
        GoRoute(
            path: 'my',
            pageBuilder: (context, state) => const NoTransitionPage(
                  child: MyPage(),
                )),
      ],
    ),
  ]),
]);

결과

profile
앱 개발과 AI, 서비스에 관심이 많은 학생입니다.

1개의 댓글

comment-user-thumbnail
2025년 3월 19일

잘 봤습니다. 여기서 다시 뵈니 반갑네요.

답글 달기