GoRouter — 선언적 라우팅과 딥링크 처리
GoRouter — 선언적 라우팅과 딥링크 처리
GoRouter는 Flutter 공식 추천 라우팅 패키지입니다. Navigator 2.0의 복잡함을 숨기고, 선언적으로 라우트를 정의할 수 있게 해줍니다.
설치
dependencies:
go_router: ^14.0.0
기본 설정
final GoRouter _router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/detail/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailScreen(id: id);
},
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsScreen(),
),
],
);
// MaterialApp.router에 전달
MaterialApp.router(
routerConfig: _router,
)
네비게이션
// 이동
context.go('/detail/42');
// push (스택에 추가)
context.push('/detail/42');
// 뒤로 가기
context.pop();
// 교체
context.pushReplacement('/home');
// 쿼리 파라미터
context.go('/search?q=flutter&page=1');
// 쿼리 파라미터 받기
GoRoute(
path: '/search',
builder: (context, state) {
final query = state.uri.queryParameters['q'] ?? '';
final page = state.uri.queryParameters['page'] ?? '1';
return SearchScreen(query: query, page: int.parse(page));
},
)
go vs push
| 메서드 | 동작 | 뒤로가기 |
|---|---|---|
go() | 해당 경로로 이동 (스택 교체) | 부모 경로로 |
push() | 현재 스택 위에 추가 | 이전 화면으로 |
면접 포인트: go()는 URL 기반 네비게이션(웹 스타일), push()는 스택 기반 네비게이션(앱 스타일)입니다. 웹에서는 주로 go(), 모바일에서는 상황에 따라 선택합니다.
중첩 라우팅 (ShellRoute)
하단 탭 네비게이션처럼 공통 UI를 유지하면서 내부 화면만 전환하는 패턴입니다.
final _router = GoRouter(
routes: [
ShellRoute(
builder: (context, state, child) {
return ScaffoldWithNavBar(child: child);
},
routes: [
GoRoute(
path: '/home',
builder: (context, state) => const HomeTab(),
),
GoRoute(
path: '/search',
builder: (context, state) => const SearchTab(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileTab(),
),
],
),
// ShellRoute 밖의 라우트 (전체 화면)
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
),
],
);
// 하단 네비게이션 바 위젯
class ScaffoldWithNavBar extends StatelessWidget {
final Widget child;
const ScaffoldWithNavBar({super.key, required this.child});
@override
Widget build(BuildContext context) {
return Scaffold(
body: child,
bottomNavigationBar: NavigationBar(
selectedIndex: _calculateIndex(GoRouterState.of(context).uri.path),
onDestinationSelected: (index) {
switch (index) {
case 0: context.go('/home');
case 1: context.go('/search');
case 2: context.go('/profile');
}
},
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: '홈'),
NavigationDestination(icon: Icon(Icons.search), label: '검색'),
NavigationDestination(icon: Icon(Icons.person), label: '프로필'),
],
),
);
}
int _calculateIndex(String path) {
if (path.startsWith('/search')) return 1;
if (path.startsWith('/profile')) return 2;
return 0;
}
}
리다이렉트 (인증 가드)
final _router = GoRouter(
redirect: (context, state) {
final isLoggedIn = authService.isLoggedIn;
final isLoginRoute = state.matchedLocation == '/login';
// 로그인 안 했으면 로그인 화면으로
if (!isLoggedIn && !isLoginRoute) {
return '/login?redirect=${state.matchedLocation}';
}
// 로그인했는데 로그인 화면이면 홈으로
if (isLoggedIn && isLoginRoute) {
return '/home';
}
return null; // 리다이렉트 하지 않음
},
routes: [...],
);
특정 라우트에만 리다이렉트
GoRoute(
path: '/admin',
redirect: (context, state) {
if (!authService.isAdmin) {
return '/unauthorized';
}
return null;
},
builder: (context, state) => const AdminScreen(),
)
에러 페이지 (404)
final _router = GoRouter(
errorBuilder: (context, state) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'404',
style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Text('페이지를 찾을 수 없습니다: ${state.uri.path}'),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('홈으로'),
),
],
),
),
);
},
routes: [...],
);
페이지 전환 애니메이션
GoRoute(
path: '/detail/:id',
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: DetailScreen(id: state.pathParameters['id']!),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
);
},
)
Extra 데이터 전달
// 객체 전달
context.go('/detail', extra: product);
// 받기
GoRoute(
path: '/detail',
builder: (context, state) {
final product = state.extra as Product;
return DetailScreen(product: product);
},
)
주의: extra는 딥링크나 URL 기반 복원이 안 됩니다. 가능하면 경로 파라미터를 사용하세요.
정리
- GoRouter는 Flutter 공식 추천 선언적 라우팅 패키지입니다
go()는 URL 기반 이동,push()는 스택 기반 이동입니다ShellRoute로 하단 탭 같은 중첩 네비게이션을 구현합니다redirect로 인증 가드 등 접근 제어를 처리합니다- 딥링크와 웹 URL을 자연스럽게 지원하는 것이 가장 큰 장점입니다
댓글 로딩 중...