Flutter에서 Go Router를 사용해 타입-안전 내비게이션 구현하는 방법

Flutter에서 Go Router를 사용해 타입-안전 내비게이션 구현하는 방법
Cozy CodingPosted On Jun 22, 20248 min read

Flutter에서 안전한 탐색: Go Router 및 Go Router Builder와 함께하는 가이드

배경

타입 안전한 네비게이션을 사용하면 탐색 로직이 일관되고 유지보수가 용이해지며 디버깅 및 향후 코드 수정이 상당히 간단해집니다.

이 기술은 웹용 Flutter 앱을 구축할 때 특히 유용합니다. URL을 원활하게 관리하고 부드러운 네비게이션 경험을 보장해줍니다.

이 블로그에서는 go_router 및 go_router_builder 패키지를 사용하여 Flutter에서 유형 안전한 네비게이션을 구현하는 방법을 살펴볼 것입니다.

종료까지, 유형 안전한 라우트 설정, 코드 생성 및 Flutter 애플리케이션에서 네비게이션을 관리하는 방법에 대해 포괄적으로 이해하게 될 것입니다.

소개

타입 안전한 네비게이션을 사용하면 네비게이션 로직이 일관되고 오류가 없음을 보장합니다.

매개변수를 잘못 구문 분석하거나 경로 이름과 매개변수에 오타를 쓰는 위험을 제거하여 코드를 유지 관리하기 쉽고 디버깅하기 쉬운 상태로 유지할 수 있습니다.

웹을 대상으로 하는 플러터 앱을 개발할 때 타입 안전한 네비게이션을 사용하여 URL을 쉽게 관리할 수 있습니다.

이 블로그를 통해 최종적으로 얻을 것은 무엇인가요?

마크다운 형식으로 테이블 태그를 변경해주세요.

우리는 우리가 반복적으로 하는 것이다. 훌륭함은 행위가 아니라 습관이다. Justly를 시도해보고 오늘부터 당신의 습관을 만들어보세요!

시작해봅시다

전체를 5단계로 나눠서 더 잘 이해할 수 있도록 설명해 드리겠습니다.

단계 1: 의존성 추가

프로젝트의 pubspec.yaml 파일에 종속성을 추가해주세요.

dependencies:
  # Router API 기반의 내비게이션을 활용하기 위해 필요합니다.
  go_router: <최신 버전>

dev_dependencies:
  # go_router와 함께 타입 안전한 경로를 생성하기 위한 도구입니다.
  go_router_builder: <최신 버전>
  # go_router_builder의 코드 생성을 실행하는 도구입니다.
  build_runner: <최신 버전>

단계 2: 경로 정의

이제 각 화면에 대한 클래스를 만들어 GoRouteData로 확장하고, 최상위 경로에 @TypedGoRoute() 주석을 추가해주세요.

모든 클래스를 하나의 파일에 작성하여 코드 생성을 보다 쉽게 할 수 있도록 해 보세요.

@TypedGoRoute<HomeRoute>(
  path: '/',
  routes: [
    TypedGoRoute<ItemDetailsRoute>(path: 'items/:id')
  ],
)
class HomeRoute extends GoRouteData {
  @override
  Widget build(BuildContext context, GoRouterState state)
    => const HomeScreen();
}

class ItemDetailsRoute extends GoRouteData {
  final String id;
  const ItemDetailsRoute({required this.id});

  @override
  Widget build(BuildContext context, GoRouterState state) =>
      ItemDetailsScreen(id: id);
}

@TypedGoRoute<SignInRoute>(
  path: '/sign-in',
  routes: [
    TypedGoRoute<VerifyOtpRoute>(path: "verify"),
  ],
)
class SignInRoute extends GoRouteData {
  @override
  Widget build(BuildContext context, GoRouterState state) =>
      const SignInScreen();
}

class VerifyOtpRoute extends GoRouteData {
  final String $extra;

  const VerifyOtpRoute({required this.$extra});

  @override
  Widget build(BuildContext context, GoRouterState state) =>
     VerifyOtpScreen(verificationId: $extra);
}

이 코드에서는 각 화면에 대한 클래스를 GoRouteData를 확장하고 TypedGoRoute로 주석 처리하여 생성했습니다. 또 다른 화면으로 데이터를 전달하기도 했습니다.

자세히 살펴보겠습니다.

GoRouteData: GoRouteData은 화면이나 페이지를 반환하거나 사용자를 다른 페이지로 리디렉션하는 메서드를 오버라이드할 수 있는 추상 클래스입니다. 이 메서드들 중 하나를 반드시 사용해야 합니다.

class HomeRoute extends GoRouteData {

  // 이렇게 parentNavigationKey를 정의할 수 있습니다. (선택 사항)
  static final GlobalKey<NavigatorState> $parentNavigatorKey = rootNavigatorKey;

  @override
  Widget build(BuildContext context, GoRouterState state) {
    // 여기서 반환된 위젯은 사용자가 이 경로로 이동할 때 표시됩니다.
    return const HomeScreen();
}

  @override
  Page<void> buildPage(BuildContext context, GoRouterState state) {
    // 여기서 반환된 Page는 사용자가 이 경로로 이동할 때 표시됩니다.
    // 여기서 CustomTransitionPage를 반환하여 페이지 전환을 설정할 수도 있습니다.
    return const CupertinoPage(child: HomeScreen());
  }

  @override
  String? redirect(BuildContext context, GoRouterState state){
      // 여기서 이 경로로 이동할 때 사용자를 리디렉션해야 할 위치나 경로를 지정할 수 있습니다.
      return "/login";
      // 리디렉션을 막으려면 null을 반환하세요.
  }
}

TypedGoRoute: TypedGoRoute 어노테이션은 경로 트리를 정의하는 데 사용됩니다. 맨 위 수준 경로 클래스마다 TypedGoRoute를 사용하여 경로 목록을 생성해야 합니다.

@TypedGoRoute<TopLevelRoute>(
  path: '/top-level-route-path',
  routes: [
    // 여기에 서브 루트 어노테이션을 이렇게 정의할 수 있습니다.
    TypedGoRoute<SubRoute>(
        path: 'sub-route-path'
        routes: []
        name: 'sub route'
    )
  ],
  name: 'top level route' // 선택 사항
)
@TypedGoRoute<MyRouteGeneric>()

이제 쿼리 매개변수, 경로 매개변수 및 라우트에서 추가 매개변수를 어떻게 사용할 수 있는지 알아봅시다.

경로 매개변수:

  • 경로 매개변수는 : 기호를 사용하여 라우트 경로 내에 정의됩니다 (예: /products/:id).
  • URL 구조의 특정 부분을 나타냅니다.

쿼리 매개변수:

  • URL 뒤에 ? 기호를 붙여 데이터를 추가합니다 (예: /products?category=electronics).
  • 요청을 수정하는 선택적인 필터와 같은 데이터에 사용됩니다.

추가: 경로나 쿼리 매개변수로 캡처되지 않는 경로로 데이터를 전달하는 방법, 추가 개체를 전달할 수 있습니다.

@TypedGoRoute<ProductDetailsRoute>(path: '/details/:id')
class ProductDetailsRoute extends GoRouteData {
  // path에 정의된 변수 이름이 경로 매개변수로 사용됩니다.
  final String id;

  // path에 정의되지 않은 변수 이름은 쿼리 매개변수로 사용됩니다.
  final String code;

  // 추가 데이터를 사용하려면 변수 이름을 $extra로 설정해야 합니다.
  final String $extra;

  const ProductDetailsRoute({required this.id, required this.code, required this.$extra});

  @override
  Widget build(BuildContext context, GoRouterState state) =>
      ProductDetails(pathParameterId: id, queryParameterCode:code, extraData: $extra);
}

단계 3: 코드 생성

루트를 정의한 후에는 루트 목록과 확장을 생성해야 합니다. 이를 위해 build_runner를 사용해야 합니다. 현재 파일에 생성된 파일 파트를 추가해 보겠습니다.

part 'routes.g.dart'; //part '<current-file>.g.dart';

이제 build_runner 명령어를 실행해 봅시다.

dart run build_runner build --delete-conflicting-outputs

현재 파일 디렉토리에 routes.g.dart 파일이 생성됩니다.

단계 4: GoRouter 초기화

이제 $appRoutes를 routes에 전달할 수 있으며, 생성된 location getter를 사용하여 정확한 경로 위치를 가져올 수 있습니다.

최종 라우터는 GoRouter(
  initialLocation: HomeRoute().location, // location getter is generated.
  //$appRoutes is generated
  routes: $appRoutes,
  redirect: (context, state) { // Optional
    // 여기서 경로 위치를 반환하여 리디렉션이 가능합니다.
    // 또한 사용자가 검색 URL을 통해 화면으로 이동하는 것을 방지할 수도 있습니다.
    // 리디렉트를 방지하려면 null을 반환합니다.
  }
  errorBuilder: (context, e) => ErrorScreen(e), // Optional
  navigatorKey: rootNavigationKey, //Optional
);

단계 5: 다른 화면으로 이동

이제 라우트를 설정했으니, 다른 화면으로 이동하는 네비게이션 방법을 살펴봅시다.

Go:

현재 화면 스택을 주어진 경로 대상으로 교체해주세요.

await Screen2Route(id: id).go(context);

이미지

푸시:

페이지 스택에 위치를 푸시하세요.

await Screen2Route(id: id).push(context);

// push 과정에서 값 반환도 가능합니다
final result = await Screen2Route(id: id).push(context);

Push Replacement:

가장 위에 있는 페이지 스택을 해당 URL 위치로 대체합니다.

await Screen2Route(id: id).pushReplacement(context);

이미지

대체:

가장 상단의 스택 페이지를 주어진 페이지로 교체하지만, 동일한 페이지로 취급합니다.

await Screen2Route(id: id).replace(context);

이미지

이제 네비게이션 구현을 마쳤습니다. 👏

image

Now, let’s take a look at how we can implement a shell route with type-safe navigation using go_router and go_router_builder.

Related Articles

We're grateful to have you on this journey with us!

만약 읽은 내용이 마음에 드신다면, 아래에 👏 👏👏를 꼭 눌러주시기 바랍니다. 작가로서 이것은 정말 소중한 일이에요!

아래 댓글 섹션에서 의견을 공유해주세요. 여러분의 의견은 콘텐츠를 더욱 풍부하고 가치 있는 것으로 만들어주며, 더 많은 유익한 기사를 작성할 동기를 얻게 해줍니다.

흥미로운 기사 업데이트를 받으시려면 Canopas를 팔로우해주세요!