[Flutter] GetX를 통한 중첩 네비게이션 구현 (Nested Navigation)

작성 날짜:

최근 업데이트 날짜:

중첩 네비게이션(Nested Navigation)은 같은 앱 내에서 하나 이상의 네비게이터를 중첩하여 사용하는 것이다. 오늘은 중첩 네비게이션이 어떤 경우에 필요한지, 그리고 어떻게 구현하는지를 알아보자.

이 글에선 GetX를 활용하여 중첩 네비게이션을 구현한다. 따라서 GetX의 네비게이션 부분에 대한 기본적인 이해가 필요하다. 만약 GetX에 대해서 알지 못한다면, 공식 패키지 사이트이전에 올린 GetX 정리글을 읽어보고 오는 것을 추천한다.

중첩 네비게이션이 필요한 상황

어떤 경우에 중첩 네비게이션이 필요할까? 보통 BottomNavigationBar를 사용할 때 중첩 네비게이션이 필요하다.

BottomNavigationBar가 들어가는 간단한 앱을 만들어 왜 그런지 알아보자.

App

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => const TemplatePage()),
        GetPage(name: '/inside', page: () => const InsidePage()),
      ],
    );
  }
}

GetX를 사용하기 때문에 MaterialApp 대신에 GetMaterialApp를 사용해야한다. 그리고 TemplatePageInsidePage에 대한 루트 이름을 설정한다.

TemplatePage

class TemplatePage extends StatelessWidget {
  const TemplatePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const OutsidePage(),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.arrow_back),
            label: 'First',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.arrow_forward),
            label: 'Second',
          ),
        ],
      ),
    );
  }
}

TemplatePageBottomNavigationBar이 들어가는 페이지이다. 지금은 간단한 예시이기 때문에 bodyOutsidePage 하나만 넣지만, 원래는 각 BottomNavigationBarItem 개수대로 페이지를 만들어야한다.

OutsidePage

class OutsidePage extends StatelessWidget {
  const OutsidePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Outside Page'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () => Get.toNamed('/inside'),
          child: const Text('Inside'),
        ),
      ),
    );
  }
}

OutsidePage에는 InsidePage로 이동시켜주는 버튼이 들어간다. 해당 버튼은 클릭 시에 Get.toNamed('/inside')를 호출하여 InsidePage로 화면을 이동시킨다.

InsidePage

class InsidePage extends StatelessWidget {
  const InsidePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Inside Page'),
      ),
    );
  }
}

InsidePage는 버튼 클릭 시에 나타나는 내부 페이지이다.

확인

BottomNavigationBar를 가지는 간단한 앱을 만들었다. 이제 한번 앱을 실행해보자.

InsidePage로 이동할 때 아래의 BottomNavigationBar가 사라진다. 이는 네비게이터 역할을 하는 GetMaterialApp이 위젯 트리 상에서 BottomNavigationBar보다 상단에 있기 때문이다.

하지만 일반적으로 같은 탭 내에서의 페이지 이동 시에는 BottomNavigationBar가 유지되어야한다. 따라서 BottomNavigationBar보다 하위에 네비게이터를 하나 더 생성해야하고, 그 네비게이터에서 InsidePage로의 페이지 이동을 담당하도록 설정해야한다.

그리고 그러한 작업이 바로 중첩 네비게이션이다.

중첩 네비게이션 적용

이제 중첩 네비게이션이 무엇인지, 어느 경우에 필요한지를 알았다. 이제 적용시켜볼 차례이다.

App

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => const TemplatePage()), // InsidePage에 대한 루트 삭제.
      ],
    );
  }
}

GetMaterialApp에서는 InsidePage에 대한 루트가 더 이상 필요없기 때문에 삭제한다.

TemplatePage

class TemplatePage extends StatelessWidget {
  const TemplatePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator( // 네비게이터 추가.
        key: Get.nestedKey(1), // id 설정.
        initialRoute: '/outside', // OutsidePage를 initialRoute로 설정.
        onGenerateRoute: (settings) {
          if (settings.name == '/outside') {
            return GetPageRoute(
              page: () => const OutsidePage(),
            );
          } else if (settings.name == '/inside') { // `InsidePage`에 대한 루트 설정.
            return GetPageRoute(
              page: () => const InsidePage(),
            );
          }
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.arrow_back),
            label: 'First',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.arrow_forward),
            label: 'Second',
          ),
        ],
      ),
    );
  }
}

TemplatePageNavigator(네비게이터)를 추가한다. 이 때 기존에 있던 GetMaterialApp과 이 Navigator를 구분하기 위해 Get.nestedKey(1)로 id를 설정한다. 이 때 숫자는 꼭 1이 아니어도 된다.

그리고 Navigator에서 OutsidePageinitialRoute로 설정하고, InsidePage에 대한 루트도 설정한다.

OutsidePage

class OutsidePage extends StatelessWidget {
  const OutsidePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Outside Page'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () => Get.toNamed('/inside', id: 1), // id 추가.
          child: const Text('Inside'),
        ),
      ),
    );
  }
}

기존 Get.toNamed('/inside')id를 추가하여 사용한다. 이때 id는 자신이 호출하려는 네비게이터의 id와 동일한 것으로 입력해야한다.

확인

이제 앱을 다시 돌려서 중첩 네비게이션이 잘 적용되었는지 확인해보자.

InsidePage로 들어갈 때도 BottomNavigationBar가 유지되는 것을 볼 수 있다.

결론

이번에는 중첩 네비게이션을 구현하는 방법을 알아봤다. 예시에서는 Get.toNamed()만 사용했지만, GetX의 다른 네비게이션 기능들도 중첩 네비게이션 환경에서 id를 지정하여 사용할 수 있다.

그리고 BottomNavigationBar는 중첩 네비게이션이 사용되는 대표적인 상황일 뿐이다. 다른 위젯들도 화면 이동 시에 유지되도록 할 수 있다.

자신의 상황에 맞춰서 자유롭게 구현해보자.

주의할 점

중첩 네비게이션에도 주의할 점이 하나 있다. 중첩 네비게이션을 사용하면 메모리에 스택처럼 아래부터 네비게이터를 쌓아가게 된다. 이것은 RAM 소비에 좋지 않으니 너무 과도하게 사용하지 않도록 조심하자.

태그:

카테고리:

최근 업데이트 날짜:

댓글남기기