개요
Flutter를 사용해서 앱을 개발해 보려고 합니다. 앱 개발에서 화면 이동을 위해서는 내비게이션을 사용해야 합니다. 이번 블로그 포스트에서는 Flutter에서 스택 내비게이션을 사용하여 화면을 이동하는 방법에 대해서 알아봅시다.
이 블로그 포스트에서 소개하는 소스 코드는 아래에 링크에서 확인할 수 있습니다.
Stack
스택 내비게이션은 화면 위에 화면을 표시하는 방식으로 화면을 이동합니다. 화면 위에 화면을 표시할 때에는 push
를, 위에 표시된 화면을 제거할 때에는 pop
을 사용합니다.
그럼 예제를 통해 스택 내비게이션을 이해해 봅시다. 다음 명령어를 사용하여 스택 내비게이션을 위한 프로젝트를 생성합니다.
flutter create my_app
cd stack
그럼 main.dart
파일을 다음과 같이 수정하여 스택 내비게이션을 사용해 봅시다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Navigator'),
),
body: Center(
child: ElevatedButton(
child: Text('Second Screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SecondScreen(),
),
);
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: ElevatedButton(
child: Text('Home Screen'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
그럼 이제 소스 코드를 하나씩 하나식 자세히 살펴 봅시다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
여기까지는 기본적인 앱 생성과 동일합니다. MaterialApp
을 사용하면, 앱이 실행될 때, 제일 먼저 표시할 화면을 home
파라메터에 설정할 수 있습니다. 여기에 우리는 Home
위젯을 설정하였습니다.
다음으로 스택 내비게이션을 사용하기 위해 두 개의 StatelessWidget
을 생성하였습니다. 하나는 MaterialApp
의 home
에 설정한 Home
위젯과 스택 내비게이션을 사용하여 Home 위젯 위에 표시할 SecondScreen
위젯을 생성하였습니다.
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: Center(
child: ElevatedButton(
child: Text('Second Screen'),
onPressed: () {...},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:...,
body: Center(
child: ElevatedButton(
child: Text('Home Screen'),
onPressed: () {...},
),
),
);
}
}
두 위젯은 Scaffold
를 사용하여 기본적으로 동일한 구조를 가지고 있습니다. 화면 가운데 ElevatedButton
위젯을 사용하여 버튼을 표시했습니다. 그리고 각 버튼을 눌렀을 때, 스택 내비게이션을 통해 화면 이동을 구현했습니다.
그럼 이제 각 버튼을 눌렀을 때, 스택 내비게이션을 호출하는 부분을 살펴봅시다. 일단 Home
화면에서 SecondScreen
으로 이동하는 코드를 살펴보겠습니다.
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
},
스택 내비게이션을 사용하여, 화면 위에 새로운 화면을 화면에 위에 표시하기 위해서는 Navigator
위젯의 push
함수를 사용할 필요가 있습니다. push
함수를 호출할 때, 위젯 트리의 위치 정보를 담고 있는 context
와 MaterialPageRoute
의 builder를 사용하여 화면위에 표시할 위젯을 파라메터로 전달해야 합니다.
이와 같이 코드를 작성하면 Home
위젯에 표시된 버튼을 누르면, Home 화면 위에, SecondScreen
위젯이 표시되게 됩니다.
그럼 이제 SecondScreen
에서 Home
화면으로 돌아가기 위해, SecondScreen
을 제거하는 코드를 확인해 봅시다.
onPressed: () {
Navigator.pop(context);
},
Navigator
위젯의 pop
를 호출하면 현재 표시되고 있는 화면이 제거되며, 현재 표시된 화면 아래에 있던 화면이 표시됩니다. pop
함수를 호출할 때는 현재 위치 정보를 가지고 있는 context
를 전달해야합니다.
Named routes
Flutter에서 여러 화면을 관리할 때, Named routes
를 사용할 수 있습니다.
그럼 Named routes
를 사용하여 스택 내비게이션을 사용해 봅시다. 우선 main.dart
파일을 다음과 같이 수정합니다.
import 'package:flutter/material.dart';
import 'ScreenA.dart';
import 'ScreenB.dart';
import 'ScreenC.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
initialRoute: 'ScreenA',
routes: {
'ScreenA': (context) => ScreenA(),
'ScreenB': (context) => ScreenB(),
'ScreenC': (context) => ScreenC(),
},
);
}
}
Named routes
를 사용하기 위해서는 MaterialApp
에서 routes
에 화면의 이름과 해당 이름의 화면 위젯을 선언합니다. 또한 home
파라메터 대신 initialRoute
를 사용하여 앱이 실행된 뒤 표시될 첫 화면 위젯을 정의합니다.
이번 예제에서는 각 화면 위젯을 lib/ScreenA.dart
, lib/ScreenB.dart
, lib/ScreenC.dart
파일을 생성한 후, 해당 파일에 코드를 작성하였습니다.
이렇게 생성한 위젯들을 main.dart
파일에서 다음과 같이 불러와 사용하였습니다.
import 'ScreenA.dart';
import 'ScreenB.dart';
import 'ScreenC.dart';
그럼 이제 각 화면에 대한 코드를 살펴보도록 하겠습니다. ScreenA.dart
파일 내용은 다음과 같습니다.
import 'package:flutter/material.dart';
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen A'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Screen B'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
),
ElevatedButton(
child: Text('Second C'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenC');
},
),
],
),
),
);
}
}
StatelessWidget
을 상속 받고, Scafford
위젯을 사용한 간단한 화면 위젯입니다. 앞에서 설명한 소스 코드와 동일한 구조이므로 자세한 설명은 생략하도록 하겠습니다. 여기서는 Named routes
를 사용하여 스택 내비게이션을 사용하는 부분만 살펴보도록 하겠습니다.
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
Named routes
를 사용하여 스택 내비게이션을 사용하기 위해서는 Navigator
위젯의 pushNamed
함수를 사용하면 됩니다. 이때, 위젯 트리의 위치 정보를 가지고 있는 contenxt
와 이동하고 싶은 화면의 이름을 파라메터로 전달하면 됩니다.
ScreenB.dart
와 ScreenC.dart
는 동일한 구조를 가지고 있으므로, 소스 코드만 공유하도록 하겠습니다.
- ScreenB.dart
import 'package:flutter/material.dart';
class ScreenB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen B'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
ElevatedButton(
child: Text('Second A'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenA');
},
),
ElevatedButton(
child: Text('Second C'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenC');
},
),
],
),
),
);
}
}
- ScreenC.dart
import 'package:flutter/material.dart';
class ScreenC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen C'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
ElevatedButton(
child: Text('Second A'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenA');
},
),
ElevatedButton(
child: Text('Second B'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
),
],
),
),
);
}
}
Named routes를 사용하면 좀 더 간단하게 화면 이동을 할 수 있으며, 여러 화면을 관리하기에 더 적합합니다.
popUntil
스택 내비게이션을 사용하다보면, 많은 화면들이 쌓이게 됩니다. 이렇게 쌓인 화면을 한번에 되돌아 가기 위해서는 popUntil
을 사용합니다.
popUntil
의 사용 방법을 확인하기 위해서 ScreenC.dart
파일을 열고 다음과 같이 수정합니다.
import 'package:flutter/material.dart';
class ScreenC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen C'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Home'),
onPressed: () {
Navigator.popUntil(context, (route) => route.isFirst);
},
),
],
),
),
);
}
}
화면을 첫 화면인 ScreenA
까지 되돌아 가기 위해서 ScreenC
에서는 다음과 같은 코드를 사용하였습니다.
onPressed: () {
Navigator.popUntil(context, (route) => route.isFirst);
},
첫 화면으로 돌아가기 위해 Navigator
의 popUntil
을 사용하였으며, route.isFirst
을 설정하였습니다. 이렇게 popUntil
을 사용하면 첫 화면으로 돌아갈 수도 있습니다. 또한, 다음과 같이 사용하면 특정 화면까지 되돌아 갈 수 있습니다.
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('ScreenA'));
},
위와 같이 사용하면, Named routes
에 정의된 이름을 사용하여 해당 화면까지 되돌아 갈 수 있습니다.
automaticallyImplyLeading
Flutter에서 스택 내비게이션을 사용하여 화면을 이동하면, 특별한 설정을 하지 않아도 왼쪽 상단에 이전 화면으로 돌아가는 버튼이 생성되는 것을 확인할 수 있다.
보통 이 기능을 사용하므로, 큰 문제가 되지 않지만, 때때로 이 기능을 사용하지 않고 싶을 때가 있다. 이 때, 사용 가능한 것이 automaticallyImplyLeading
옵션이다.
다음과 같이 AppBar 위젯의 automaticallyImplyLeading
옵션을 사용하면, 자동으로 생성되는 되돌아가기 버튼을 제거할 수 있다.
AppBar(
...
automaticallyImplyLeading: false,
)
Swipe back
Flutter에서 스택 내비게이션을 사용하면 기본적으로 스와이프 백(Swipe back) 기능을 사용하여 이전 화면으로 되돌아갈 수 있다.
만약, 이 기능을 사용하고 싶지 않다면, 다음과 같이 WillPopScope
위젯의 onWillPop
을 사용하여 스와이프 백 기능을 비활성화할 수 있다.
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
...,
),
);
}
위와 같이 스택 내비게이션을 사용하여 표시될 화면을 WillPopScope
위젯으로 감싸준 후, onWillPop
파라메터를 false
로 설정하면, 스와이프 백 기능을 비활성화 할 수 있습니다.
포커스
내비게이션을 사용하다보면, 현재 화면(A)에 다른 화면(B)이 표시된 후, 다시 현재 화면(A)으로 돌아왔을 때, 특정 함수를 실행하거나 어떤 동작을 실행하고 싶을 때가 있습니다. 이때, 다음과 같이 pushNamed
함수를 사용할 수 있습니다.
Navigator.pushNamed(context, 'B').then((value) {
// Do something after widget is focused(visible).
});
이렇게 pushNamed
함수를 사용하여 다음 페이지로 이동할 때, then
을 사용하여 다음 페이지가 pop
된 후, 특정 동작을 수행할 수 있는 콜백(Callback) 함수를 등록할 수 있습니다.
Full modal
기본적으로 스택 내비게이션은 오른쪽에서 왼쪽으로 슬라이드되면서 화면에 표시됩니다. 다음 코드를 사용하면 스택 내비게이션을 하단에서 위로 슬라이드되면서 표시되도록 할 수 있습니다.
import 'package:flutter/cupertino.dart';
...
Navigator.push(
context,
CupertinoPageRoute(
fullscreenDialog: true,
builder: (_) => SecondScreen(),
),
);
완료
이것으로 Flutter에서 스택 내비게이션을 사용하는 방법에 대해서 살펴보았습니다. 스택 내비게이션은 앱의 화면 전환에 가장 많이 사용되는 내비게이션이므로 사용법을 잘 익혀둡시다.
제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!
앱 홍보
Deku
가 개발한 앱을 한번 사용해보세요.Deku
가 개발한 앱은 Flutter로 개발되었습니다.관심있으신 분들은 앱을 다운로드하여 사용해 주시면 정말 감사하겠습니다.