Overview
한 화면(페이지) 내에서 좌우로 슬라이드하여 화면을 이동하는 효과를 구현해봅시다.
Flutter에서는 슬라이드 화면 넘김 효과를 위해 제공하는 'PageView' 클래스가 있습니다.
이 포스팅에서는 페이지뷰 클래스의 기본 활용법을 설명합니다.
막상 보면 코드는 별 게 없습니다.

PageView Class
페이지 별로 스크롤이 작동하도록 해주는 클래스입니다.
페이지 뷰의 각 하위 항목은 뷰포트와 동일한 크기여야 합니다.
사용 방법은 간단합니다.
- PageController
- 페이지 컨트롤러를 사용하여 표시되는 페이지를 제어하는데 사용합니다.
- PageController.initialPage
- 가장 먼저 표시되는 페이지를 지정합니다.
- 위젯이 정의된 순서대로 인덱스는 0부터 시작합니다.
- 동작 순서
- PageView가 먼저 생성됩니다.
- 페이지를 제어하기 위해 PageController를 정의합니다.
(이 단계에서 처음 화면에 표시될 내용(페이지 혹은 위젯)을 설정할 수 있습니다.) - PageController를 이용하여 페이지를 제어합니다. (페이지 슬라이드 이동)
샘플 코드 (예제)
아래의 샘플 코드는 Flutter 공식 문서에서 가져온 코드입니다.
import 'package:flutter/material.dart';
/// Flutter code sample for [PageView].
void main() => runApp(const PageViewExampleApp());
class PageViewExampleApp extends StatelessWidget {
const PageViewExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('PageView Sample')),
body: const PageViewExample(),
),
);
}
}
class PageViewExample extends StatelessWidget {
const PageViewExample({super.key});
@override
Widget build(BuildContext context) {
final PageController controller = PageController();
return PageView(
/// [PageView.scrollDirection] defaults to [Axis.horizontal].
/// Use [Axis.vertical] to scroll vertically.
controller: controller,
children: const <Widget>[
Center(
child: Text('First Page'),
),
Center(
child: Text('Second Page'),
),
Center(
child: Text('Third Page'),
),
],
);
}
}
구현 : 슬라이드 좌우로 이동하기
프로젝트 생성 및 메인 함수 페이지 연결
- 새 프로젝트를 생성하고 페이지 슬라이드를 구현할 페이지를 메인 함수에서 연결합니다.
void main() {
runApp(
const MaterialApp(
home: PageSlideExample(),
),
);
}
기본 클래스(페이지) 정의
- main 함수의 아래 내용을 모두 지우고 기본 페이지를 정의합니다.
- 공식 문서에서는 예제로 StatelessWidget으로 구현하였지만, 포스팅에서는 Stateful Widget 으로 구현합니다.
- 이유는 추 후 버튼 클릭이나 특정 액션으로 페이지 이동 효과를 주려면 뼈대를 Stateful Widget으로 만들어두는게 좋습니다.
팁 : 'st'만 입력하여도 추가 클래스를 쉽게 만들 수 있습니다.

// 메인과 연결될 클래스
class PageSlideExample extends StatefulWidget {
const PageSlideExample({super.key});
@override
PageSlideExampleState createState() => PageSlideExampleState();
}
class PageSlideExampleState extends State<PageSlideExample> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: // 내용 작성
);
}
}
페이지를 제어할 PageController 정의
- State를 상속한 클래스 아래에 페이지뷰 컨트롤러를 정의합니다.
class PageSlideExampleState extends State<PageSlideExample> {
// 페이지뷰 컨트롤러 설정
// initialPage : 0부터 차례대로 위젯들의 인덱스를 의미
final PageController _pageController = PageController(initialPage: 0);
// 코드 생략
}
각각의 페이지를 담을 PageView 정의
- 위젯이 빌드되는 영역에 PageView 위젯을 생성하고 컨트롤러를 위에서 정의한 컨트롤러로 설정합니다.
- PageView의 자식 위젯들은 리스트의 형태로 정의하여야 합니다. (children : [ ] )
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController, // 페이지 컨트롤러 설정
children: [
// 페이지를 이동할 위젯들 정의
],
),
);
}
각각의 페이지(하위 위젯) 정의
- PageView에 속할 컨텐츠들을 정의합니다.
- 포스팅에서는 간단한 이해를 위해 컨테이너로 구성된 위젯 3개를 정의하였습니다.
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController, // 페이지 컨트롤러 설정
children: [
// 페이지 컨트롤러의 첫 번째 위젯, 인덱스 : 0
Container(
color: Colors.red,
child: const Center(
child: Text('Page 1',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 두 번째 위젯, 인덱스 : 1
Container(
color: Colors.green,
child: const Center(
child: Text('Page 2',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 세 번째 위젯, 인덱스 : 0
Container(
color: Colors.blue,
child: const Center(
child: Text('Page 3',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
],
),
);
}
실행
이 포스팅은 안드로이드 가상 에뮬레이터를 사용하여 앱을 실행합니다.
플러터, VS Code, Android Studio, Android Virtual Emulator 설치는 아래의 포스팅을 참고해주세요.

전체 코드
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: PageSlideExample(),
),
);
}
class PageSlideExample extends StatefulWidget {
const PageSlideExample({super.key});
@override
PageSlideExampleState createState() => PageSlideExampleState();
}
class PageSlideExampleState extends State<PageSlideExample> {
// 페이지뷰 컨트롤러 설정
// initialPage : 0부터 차례대로 위젯들의 인덱스를 의미
final PageController _pageController = PageController(initialPage: 0);
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageController, // 페이지 컨트롤러 설정
children: [
// 페이지 컨트롤러의 첫 번째 위젯, 인덱스 : 0
Container(
color: Colors.red,
child: const Center(
child: Text('Page 1',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 두 번째 위젯, 인덱스 : 1
Container(
color: Colors.green,
child: const Center(
child: Text('Page 2',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 세 번째 위젯, 인덱스 : 0
Container(
color: Colors.blue,
child: const Center(
child: Text('Page 3',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
],
),
);
}
}
응용 : 세로로 화면 슬라이드하여 이동하기

화면을 위 아래로 슬라이드하도록 변경해봅시다. 딱 코드 한 줄만 추가하면 됩니다 =)
'PageView' 위젯의 'scrollDirection' 속성을 를 이용하여 이동하는 방향을 왼쪽, 오른쪽이 아닌 위, 아래로 변경할 수 있습니다.
해당 속성은 페이지 뷰의 담긴 컨텐츠(페이지 혹은 위젯)들이 슬라이드 되는 방향을 설정합니다.
기본 값은 수평(좌우)으로 되어있습니다.
- 위의 전체 코드에서 PageView 부분에 아래의 코드를 추가해줍니다.
PageView(
// 슬라이드 스크롤의 방향을 지정
scrollDirection : Axis.vertical // default : Axis.horizontal
// 코드 생략
)
응용 : 버튼으로 페이지 슬라이드하기
간단한 구현을 위해 플로팅 액션 버튼을 사용하며, setState 메서드를 이용하여 페이지 컨트롤러의 인덱스를 변경합니다.
페이지 컨트롤러의 인덱스 변경은 .animateToPage 메서드를 이용하여 동적으로 화면 전환을 할 수 있습니다.
단, 클래스가 StatelessWidget일 경우에는 StatefulWidget으로 변경하여야 합니다.

- 페이지의 인덱스를 저장할 정수형 변수를 하나 선언해줍니다. (pageIndex)
class PageSlideExampleState extends State<PageSlideExample> {
// 페이지뷰 컨트롤러 설정
// initialPage : 0부터 차례대로 위젯들의 인덱스를 의미
final PageController _pageController = PageController(initialPage: 0);
// 버튼으로 페이지를 이동할 인덱스 카운터 변수
int pageIndex = 0;
// 코드 생략
}
- 플로팅 액션 버튼을 추가해줍니다.
- (아래와 같은 모양으로 구현할 예정)

@override
Widget build(BuildContext context) {
return Scaffold(
body:
// body 코드 생략
// 플로팅 액션 버튼 추가
floatingActionButton: FloatingActionButton(
// 버튼 클릭 이벤트
onPressed: () {
// 버튼을 클릭하였을 때 수행할 코드
});
},
// 버튼 색상
backgroundColor: Colors.amber[400],
// 버튼 아이콘
child: const Icon(Icons.arrow_forward),
),
);
}
- 플로팅 액션 버튼의 버튼 클릭 이벤트를 정의해줍니다.
- 가장 처음에 선언한 pageIndex를 이용하여 페이지 슬라이드 이벤트를 만듭니다.
- 페이지 이동은 컨트롤러의 animateToPage 메서드를 활용합니다.
animateToPage 기본 구문
/* animateToPage 구문 */
// _pageController : PageView 클래스의 화면을 제어하는 PageController의 인스턴스를 의미
_pageController.animateToPage(
// 이동할 페이지의 인덱스
page,
// 페이지가 슬라이드되는 시간
duration: duration,
// 페이지가 슬라이드 될 때의 애니메이션 효과
curve: curve;
- animateToPage를 버튼 클릭 이벤트에 적용해줍니다.
floatingActionButton: FloatingActionButton(
// 버튼 클릭 이벤트
onPressed: () {
// 다음 페이지로 이동하기 위해 페이지 인덱스 +1
pageIndex++;
// 마지막 페이지일 경우 페이지는 첫번째 페이지로 되돌아감
pageIndex = pageIndex % 3;
// 앱 상태 변경
setState(() {
// animateToPage : 페이지뷰의 페이지 인덱스를 특정 인덱스로 이동하게하는 메서드
_pageController.animateToPage(
// 이동할 페이지의 인덱스
pageIndex,
// 페이지가 슬라이드되는 시간 : 1초동안 이동
duration: const Duration(milliseconds: 1000),
// 페이지가 슬라이드 될 때의 애니메이션 효과
// Curves.ease : 애니메이션 시작과 끝에 속도를 조절하여 자연스러운 효과를 줌
curve: Curves.ease);
});
},
backgroundColor: Colors.amber[400],
child: const Icon(Icons.arrow_forward),
),
전체 소스 코드
- 이해가 어렵다면 전체 소스 코드를 확인해주세요.
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: PageSlideExample(),
),
);
}
class PageSlideExample extends StatefulWidget {
const PageSlideExample({super.key});
@override
PageSlideExampleState createState() => PageSlideExampleState();
}
class PageSlideExampleState extends State<PageSlideExample> {
// 페이지뷰 컨트롤러 설정
// initialPage : 0부터 차례대로 위젯들의 인덱스를 의미
final PageController _pageController = PageController(initialPage: 0);
// 버튼으로 페이지를 이동할 인덱스 카운터 변수
int pageIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
scrollDirection: Axis.vertical,
controller: _pageController, // 페이지 컨트롤러 설정
children: [
// 페이지 컨트롤러의 첫 번째 위젯, 인덱스 : 0
Container(
color: Colors.red,
child: const Center(
child: Text('Page 1',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 두 번째 위젯, 인덱스 : 1
Container(
color: Colors.green,
child: const Center(
child: Text('Page 2',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
// 페이지 컨트롤러의 세 번째 위젯, 인덱스 : 0
Container(
color: Colors.blue,
child: const Center(
child: Text('Page 3',
style: TextStyle(color: Colors.white, fontSize: 24)),
),
),
],
),
floatingActionButton: FloatingActionButton(
// 버튼 클릭 이벤트
onPressed: () {
// 다음 페이지로 이동하기 위해 페이지 인덱스 +1
pageIndex++;
// 마지막 페이지일 경우 페이지는 첫번째 페이지로 되돌아감
pageIndex = pageIndex % 3;
// 앱 상태 변경
setState(() {
// animateToPage : 페이지뷰의 페이지 인덱스를 특정 인덱스로 이동하게하는 메서드
_pageController.animateToPage(
// 이동할 페이지의 인덱스
pageIndex,
// 페이지가 슬라이드되는 시간 : 1초동안 이동
duration: const Duration(milliseconds: 1000),
// 페이지가 슬라이드 될 때의 애니메이션 효과
// Curves.ease : 애니메이션 시작과 끝에 속도를 조절하여 자연스러운 효과를 줌
curve: Curves.ease);
});
},
// 버튼 색상
backgroundColor: Colors.amber[400],
// 버튼 아이콘
child: const Icon(Icons.arrow_forward),
),
);
}
}
참고