(Flutter-기초 강의) 14. Route 하기 (Navigator, go_router)

Flutter에서의 Route 기능은 애플리케이션의 다양한 화면이나 페이지로 사용자를 안내하는 중요한 역할을 합니다.

이 기능을 통해 사용자는 앱 내에서 원활하게 화면을 전환하고, 다양한 데이터와 상호작용할 수 있습니다.

Route 란?

소개

Route는 Flutter 앱에서 사용자 인터페이스의 경로 또는 화면을 나타냅니다.

각 Route는 앱의 다른 화면이나 페이지를 의미하며, 사용자가 앱 내에서 이동할 때마다 새로운 Route로 전환되는 구조를 가지고 있습니다.

이를 통해 사용자는 앱의 다양한 부분을 자연스럽게 탐색할 수 있으며, 개발자는 사용자 경험을 효과적으로 설계할 수 있습니다.

기능

  1. 화면 전환 관리: 사용자가 앱 내에서 다른 페이지로 이동할 때, Route는 새로운 화면을 표시하고 이전 화면을 스택에서 관리합니다. 이는 사용자가 앱을 탐색할 때 일관된 경험을 제공합니다.
  2. 데이터 전달: Route를 통해 화면 간에 데이터를 전달할 수 있습니다. 예를 들어, 사용자가 선택한 아이템의 정보를 세부 정보 페이지로 전달하여 해당 내용을 표시할 수 있습니다.
  3. 사용자 경험 향상: 다양한 전환 효과와 애니메이션을 사용하여 Route 전환을 보다 매끄럽고 직관적으로 만들 수 있습니다. 이는 사용자의 몰입감을 높이고, 앱의 전반적인 외관을 개선하는 데 도움이 됩니다.

Navigator Widget

소개

Flutter에서 Navigator Widget 은 앱 내에서 페이지 간의 이동을 관리하는 매우 중요한 역할을 수행합니다.

Navigator는 Route기능을 스택을 통하여 관리하며, 사용자가 새로운 페이지로 이동하거나 이전 페이지로 돌아갈 때 해당 Route들을 스택에 추가하거나 제거함으로써 앱 내의 화면 전환을 가능하게 합니다.

기본 동작

  • Push: 새로운 Route(화면)을 Navigator 스택에 추가합니다. 이는 사용자가 새로운 화면으로 이동할 때 사용됩니다.
  • Pop: 현재 Route를 스택에서 제거합니다. 이는 사용자가 이전 화면으로 돌아갈 때 사용됩니다.

동작 예시

1. 기본 페이지 이동:

사용자가 버튼을 클릭했을 때 새로운 페이지로 이동하는 기본적인 예시입니다.

            ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) {
                    return const PageA();
                  }),
                );
              },
              child: const Text('Go to Page A'),
            ),

결과

basic-route-example

2. Named Route를 이용한 라우팅:

Named Route를 사용하면 앱 내에서 미리 정의된 이름으로 화면을 이동할 수 있습니다.

이 방법은 경로 이름(path name)를 통해 route를 관리합니다.

다음과 같이 MaterialApp 위젯내에서 routes 속성을 통해 경로 이름과 연결할 위젯을 정의합니다.

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => const MyHomePage(title: 'Start Project'),
        '/a': (context) => const PageA(),
        '/b': (context) => const PageB(),
      },
    );

Navigator.pushNamed method를 사용하여 화면을 이동합니다.

            ElevatedButton(
              onPressed: () {
                // go to '/b' path
                Navigator.pushNamed(context, '/b');
              },
              child: const Text('Go to Page B'),
            ),

결과

named-route-example

3. 매개변수를 전달하기

다음과 같이 매개변수(argText)를 route시 전달합니다.

            ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) {
                      return const PageC(argText: "Route with argument");
                    },
                  ),
                );
              },
              child: const Text('Go to Page C'),
            ),

해당 Widget(PageC)는 매개변수를 전달받아 생성자에서 사용합니다.

class PageC extends StatelessWidget {
  final String argText;

  const PageC({super.key, required this.argText});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        '(Route with argument)\nPage C\n$argText',
        textAlign: TextAlign.center,
        style: const TextStyle(fontSize: 30),
      ),
    );
  }
}

결과

argument-route-example

4. 결과 받기

다음과 같이 Navigator.pop 에 의해 리턴될 때, 반환 값을 전달 받을 수 있습니다.

            ElevatedButton(
              onPressed: () async {
                final result = await Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const PageD()),
                );

                AlertDialog dlg = AlertDialog(
                  title: const Text('Result'),
                  content: Text('$result'),
                );

                // ignore: use_build_context_synchronously
                showDialog(
                  context: context,
                  builder: (BuildContext context) => dlg,
                );
              },
              child: const Text('Go to Page D'),
            ),

PageDNavigator.pop 시, 결과를 리턴합니다.

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          ElevatedButton(
            onPressed: () {
              Navigator.pop(context, 'Option A');
            },
            child: const Text('Option A'),
          ),
          ElevatedButton(
            onPressed: () {
              // Return the result and return the prior page.
              Navigator.pop(context, 'Option B');
            },
            child: const Text('Option B'),
          )
        ],
      ),
    );
  }

결과

return-result-route-example

Go_router 패키지 소개

기존의 Navigator를 사용하는 방법도 있지만, 더 강력하고 직관적인 라우팅인 go_router 패키지를 필요에 따라 사용합니다.

이 패키지는 Flutter의 라우팅을 더욱 편리하게 만들어주며, 복잡성을 줄이는 데 크게 기여합니다.

자세한 정보는 go_router pub.dev 페이지에서 확인할 수 있습니다.

개요

go_router는 Flutter 라우팅을 위한 선언적, 강력하며, 유연한 패키지입니다.

기존의 Navigator 방식 대비 적은 코드로 더 많은 기능을 수행할 수 있으며, URL을 통한 깊은 링크(deep link) 지원, 라우트 가드(route guards), 파라미터와 쿼리 처리 등 고급 라우팅 기능을 제공합니다.

Go_router 사용의 장점

  • 선언적 라우팅: go_router는 선언적 API를 통해 라우트를 정의합니다.
    이는 라우트 구조를 한눈에 파악하기 쉽게 만들어 줍니다.
  • 간결성: 복잡한 라우트 구조도 간결하게 표현할 수 있어 코드의 양을 크게 줄일 수 있습니다.
  • 깊은 링크 지원: 외부 URL을 통해 앱 내 특정 페이지로 직접 이동하는 것을 지원합니다.
  • 라우트 가드: 사용자 인증 상태 등에 따라 라우트 접근을 제어할 수 있는 기능을 제공합니다.

설치

 $ flutter pub add go_router

사용하고자 하는 파일에 다음을 import 합니다.

import 'package:go_router/go_router.dart';

예제

이 코드는 기본 홈 화면과 ID에 따라 세부 정보를 표시하는 화면을 가지는 간단한 라우터 설정 예시입니다.

기본 routing

GoRouterGoRoute 를 통해 Routing Path를 정의합니다.

final _router = GoRouter(initialLocation: '/', routes: [
  GoRoute(
      path: '/',
      builder: (context, state) => const MyHomePage(title: 'Start Project')),
  GoRoute(path: '/e', builder: (context, state) => const PageE()),
]);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
      return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      routerConfig: _router,
    );
  }
}

그리고 이동하고자 하는 곳에서 다음과 같이 절대경로를 이용해 이동할 수 있습니다.

주의해야 할 점은 context.go() 경우 기본적으로 context.push()와 달리 뒤로 갈 경우, 이전 페이지로 이동하지 않습니다.

            ElevatedButton(
               onPressed: () => context.go('/e'),
               child: const Text('Go to Page E'),
            ),
매개변수를 포함한 routing

다음과 같이 route 시, 매개변수를 전달할 수 있습니다.

아래의 경우 /f/:argText를 통해 전달된 매개변수를 PageF 생성시 전달합니다.

final _router = GoRouter(initialLocation: '/', routes: [
  GoRoute(
      path: '/',
      builder: (context, state) => const MyHomePage(title: 'Start Project')),
  GoRoute(path: '/e', builder: (context, state) => const PageE()),
  GoRoute(
    path: '/f/:argText',
    builder: (context, state) =>
        PageF(argText: state.pathParameters['argText']!),
  ),
]);

이때 , context.go 시, 다음과 같이 매개변수(123)을 전달할 수 있습니다.

            ElevatedButton(
              onPressed: () => context.go('/f/123'),
              child: const Text('Go to Page F'),
            ),

참고 링크

Leave a Comment