Navigator 기초 — push, pop, Named Routes
Navigator 기초 — push, pop, Named Routes
앱에서 화면 전환은 필수입니다. Flutter는 Navigator를 사용해 스택 기반으로 화면을 관리합니다. 웹의 브라우저 히스토리와 비슷한 개념입니다.
Navigator의 동작 원리
Navigator는 화면(Route)을 스택 으로 관리합니다.
push → 새 화면을 스택 위에 쌓기
pop → 현재 화면을 스택에서 제거 (이전 화면으로 돌아감)
┌─────────┐
│ C 화면 │ ← 현재 화면 (최상단)
├─────────┤
│ B 화면 │
├─────────┤
│ A 화면 │ ← 루트 화면
└─────────┘
기본 push/pop
새 화면으로 이동 (push)
// 홈 화면
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('홈')),
body: Center(
child: ElevatedButton(
onPressed: () {
// 새 화면으로 이동
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailScreen(),
),
);
},
child: const Text('상세 화면으로'),
),
),
);
}
}
이전 화면으로 돌아가기 (pop)
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('상세')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context); // 이전 화면으로
},
child: const Text('뒤로 가기'),
),
),
);
}
}
데이터 전달
다음 화면으로 데이터 넘기기
// 생성자로 전달
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
title: '게시물 제목',
id: 42,
),
),
);
// 받는 쪽
class DetailScreen extends StatelessWidget {
final String title;
final int id;
const DetailScreen({
super.key,
required this.title,
required this.id,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('ID: $id')),
);
}
}
이전 화면으로 결과 돌려주기
// A 화면: 결과를 기다림
final result = await Navigator.push<String>(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (result != null) {
print('선택한 값: $result');
}
// B 화면: 결과를 돌려줌
Navigator.pop(context, '선택된 아이템');
Named Routes
라우트에 이름을 붙여서 관리하는 방식입니다.
// MaterialApp에서 라우트 정의
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/detail': (context) => const DetailScreen(),
'/settings': (context) => const SettingsScreen(),
'/profile': (context) => const ProfileScreen(),
},
)
// 이름으로 이동
Navigator.pushNamed(context, '/detail');
// 인자 전달
Navigator.pushNamed(
context,
'/detail',
arguments: {'id': 42, 'title': '제목'},
);
// 인자 받기
final args = ModalRoute.of(context)!.settings.arguments
as Map<String, dynamic>;
onGenerateRoute (동적 라우팅)
MaterialApp(
onGenerateRoute: (settings) {
// '/detail/42' 같은 동적 경로 처리
if (settings.name?.startsWith('/detail/') ?? false) {
final id = settings.name!.split('/').last;
return MaterialPageRoute(
builder: (context) => DetailScreen(id: int.parse(id)),
);
}
// 404 처리
return MaterialPageRoute(
builder: (context) => const NotFoundScreen(),
);
},
)
Navigator 2.0 vs 1.0
| 구분 | Navigator 1.0 | Navigator 2.0 |
|---|---|---|
| 방식 | 명령형 (push/pop) | 선언적 |
| 딥링크 | 제한적 | 완전 지원 |
| 웹 URL | 제한적 | 완전 지원 |
| 복잡도 | 낮음 | 높음 |
실무에서는 Navigator 2.0을 직접 구현하기보다 GoRouter 같은 라우팅 패키지를 사용하는 것이 일반적입니다. 이 내용은 별도의 글에서 다루겠습니다.
유용한 Navigator 메서드
// pushReplacement: 현재 화면을 교체 (로그인 → 홈)
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomeScreen()),
);
// pushAndRemoveUntil: 스택을 정리하며 이동 (로그아웃)
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginScreen()),
(route) => false, // 모든 이전 라우트 제거
);
// popUntil: 특정 화면까지 돌아가기
Navigator.popUntil(context, (route) => route.isFirst);
// canPop: 뒤로 갈 수 있는지 확인
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
// maybePop: 가능하면 pop, 아니면 무시
Navigator.maybePop(context);
WillPopScope — 뒤로가기 가로채기
// 뒤로가기 시 확인 다이얼로그 표시
PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
if (didPop) return;
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('나가시겠습니까?'),
content: const Text('변경사항이 저장되지 않습니다.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('취소'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('나가기'),
),
],
),
);
if (shouldPop == true && context.mounted) {
Navigator.pop(context);
}
},
child: Scaffold(
appBar: AppBar(title: const Text('편집')),
body: const Text('폼 내용'),
),
)
정리
- Navigator는 스택 기반으로 화면을 관리합니다 (push/pop)
await Navigator.push()로 다음 화면에서 결과를 받을 수 있습니다- Named Routes로 문자열 기반 라우팅이 가능하지만, 타입 안전성이 부족합니다
pushReplacement는 화면 교체,pushAndRemoveUntil은 스택 초기화에 사용합니다- 실무에서는 GoRouter 같은 패키지를 많이 사용합니다
댓글 로딩 중...