Flutter 공식 홈페이지에 있는 Cookbook - Create a nested navigation flow 에서 따온 움짤이다.
위 처럼 Scaffold 전체를 덮는 페이지를 push하는게 아니라 부분적으로 (위의 움짤에서는 AppBar 고정) 화면을 푸시하려면 어떻게 해야할까?
이때 필요한 것이 Nested Navigator이다!
공식 문서에 따르면
A widget that manages a set of child widgets with a stack discipline.
라고 첫줄에 정의되어 있다. 우리는 모바일에서 페이지를 이동할 때, 스택처럼 페이지를 쌓고 꺼내면서 이동하게 되는데 쉽게 말해서 이 Navigator가 위와 같은 stack을 관리한다고 생각하면 된다!
The navigator manages a stack of Route objects and provides two ways for managing the stack.
즉 이 Route들이 있는 스택을 관리한다고 한다.
여기서 또 Route는 무엇인가하면
An abstraction for an entry managed by a Navigator
This class defines an abstract interface between the navigator and the "routes" that are pushed on and popped off the navigator.
Navigator가 관리하는 요소의 인터페이스 같은 것이다. 또 Most routes have visual affordances 라고 쓰여 있는데 이는 page가 push되고 pop될 때의 시각적인 애니메이션 등이 정의되어 있다는 것이다.
대표적으로 우리가 많이 쓰는 MaterialPageRoute는 platfrom-adaptive하게 전체 스크린을 transition하는 visual affordance를 갖고있는 Route이다. (dialog를 띄울때는 PopupRoute를 사용)
(참고로 아래와 같은 구조로 상속되어 있음)
Inheritance
Object → Route → OverlayRoute → TransitionRoute → ModalRoute
PageRoute → MaterialPageRoute
Nested Navigator를 사용하면 위와 같은 구조로 Navigator가 존재하게 된다.
Root Navigator는 MaterialApp에 기본적으로 포함되어 있는 Navigator이다.
MainPage(page1, page2)에서 Page3으로 넘어갈 때 Navigator.push를 활용하여 넘어가면 전체 페이지가 전환될 것이다. 그러나 MainPage의 page1, page2 간의 화면 전환에서 Nested Navigator를 활용한다면 MainPage의 기본 골격(위의 움짤에서는 AppBar가 해당)은 그대로 유지되고 화면전환이 될 것이다!
그러면 코드로는 어떻게 구현되어 있는지 알아보자
// Navigator를 식별해주는 key
final _navigatorKey = GlobalKey<NavigatorState>();
// 이런 식으로 root navigator를 이용하지 않고, GlobalKey로 식별한
// Nested Navigator를 활용하여 화면전환
void _onDiscoveryComplete() {
_navigatorKey.currentState!.pushNamed(routeDeviceSetupSelectDevicePage);
}
void _onDeviceSelected(String deviceId) {
_navigatorKey.currentState!.pushNamed(routeDeviceSetupConnectingPage);
}
void _onConnectionEstablished() {
_navigatorKey.currentState!.pushNamed(routeDeviceSetupFinishedPage);
}
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _isExitDesired,
child: Scaffold(
// 이 코드 부분에서는 정의 되어 있지 않지만 AppBar를 리턴하는 함수이고, 이렇게 AppBar를 고정
appBar: _buildFlowAppBar(),
// body부분에 Navigator를 할당하여 이 부분만 페이지 전환이 일어나도록 함⭐️⭐️⭐️
body: Navigator(
// 이 Navigator를 식별하기 위한 key를 할당
key: _navigatorKey,
initialRoute: widget.setupPageRoute,
onGenerateRoute: _onGenerateRoute,
),
),
);
}
// push가 일어날때 RouteSetings에 따라 Route를 생성해주는 함수
Route _onGenerateRoute(RouteSettings settings) {
late Widget page;
switch (settings.name) {
case routeDeviceSetupStartPage:
page = WaitingPage(
message: 'Searching for nearby bulb...',
onWaitComplete: _onDiscoveryComplete,
);
break;
case routeDeviceSetupSelectDevicePage:
page = SelectDevicePage(
onDeviceSelected: _onDeviceSelected,
);
break;
case routeDeviceSetupConnectingPage:
page = WaitingPage(
message: 'Connecting...',
onWaitComplete: _onConnectionEstablished,
);
break;
case routeDeviceSetupFinishedPage:
page = FinishedPage(
onFinishPressed: _exitSetup,
);
break;
}
// MaterialPageRoute에 담아서 리턴
return MaterialPageRoute<dynamic>(
builder: (context) {
return page;
},
settings: settings,
);
}
references: