[Flutter/Widget of the Week] PageView

작성 날짜:

최근 업데이트 날짜:

Widget of the Week 유튜브 영상


페이지 단위로 스크롤 할 수 있는 화면을 그려준다. PageView에 들어가는 페이지들은 모두 뷰포트(viewport) 사이즈로 화면에 보이게 된다.

TextFieldTextEditingController를 통해 컨트롤되는 것 같이, PageViewPageController를 통해 컨트롤된다. 처음 보일 페이지(initialPage), 각 페이지가 차지할 뷰포트 사이즈 비율(viewportFraction) 등을 설정할 수 있다.

사용 예시

PageView의 사용법은 매우 간단하다. children에 각 페이지가 될 위젯을 리스트 형태로 넣어주고, 컨트롤러만 할당해주면 된다.

class PageViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final PageController _controller =
    PageController(initialPage: 0);
    return Scaffold(
      appBar: AppBar(
        title: Text('PageView example'),
      ),
      body: PageView(
        controller: _controller,
        children: [
          Container(
            color: Colors.yellow,
            child: Center(
              child: Text('First Page'),
            ),
          ),
          Container(
            color: Colors.green,
            child: Center(
              child: Text('Second Page'),
            ),
          ),
          Container(
            color: Colors.blue,
            child: Center(
              child: Text('Third Page'),
            ),
          ),
        ],
      ),
    );
  }
}

예시 코드에서 children에 들어간 3개의 Container가 각각 하나의 페이지를 구성하는 것을 볼 수 있다.

또한 PageController(_controller 변수)를 선언하고, 이를 PageViewcontroller로 할당했다. _controller 변수의 initialPage0으로 설정했기 때문에 children 중에 가장 앞에 위치한 노란색 컨테이너가 제일 먼저 사용자에게 보여진다. 예시에서는 확인할 수 없지만, _controller 변수를 통해서 PageView의 페이지를 이동시키는 등의 컨트롤을 할 수 있다.

PageView.builder 생성자

ListViewListView.builder 생성자와 비슷하다.

PageView.builder 생성자는 사용자에게 보이고 있는 페이지에 대해서만 builder를 호출하므로 children 개수가 아주 많거나 무한할 경우에 유용하다. itemCount를 통해 children 개수를 설정할 수 있는데 itemCountnull 로 설정할 경우에 children 개수가 무한대가 된다.

PageView.builder(
  controller: _controller,
  itemBuilder: (context, index) {
    return Container(
      child: Center(
        child: Text(index.toString()),
      ),
    );
  },
  itemCount: null,
)

itemCountnull로 설정했더니 페이지가 무한으로 늘어나는 것을 확인할 수 있다.

PageView.custom 생성자

PageView를 사용자의 의도대로 커스터마이즈해서 사용할 수 있게 해주는 생성자 같은데 정확한 사용법과 용도를 모르겠다. 그런데 기본 생성자의 경우에는 children을 통해 위젯 리스트를 받아서 이를 자동으로 childrenDelegate에 할당시키는 반면에, PageView.custom 생성자의 경우에는 직접 childrenDelegate으로 받는다.

Properties

Property Description Type Default
allowImplicitScrolling 각 children 위젯들에 내부 스크롤 부여 여부. bool false
childrenDelegate children 위젯을 PageView 위젯에게 제공하는 역할. 어떤 식으로 돌아가는지는 잘 모르겠다. SliverChildDelegate  
clipBehavior content가 범위를 넘어갈 때 해당 content를 자르는 방법. Clip Clip.hardEdge
controller PageView를 컨트롤하기 위한 컨트롤러. PageController  
dragStartBehavior 사용자의 드래그를 인식하는 방법. (down: 사용자가 화면을 누르기 시작했을 때, start: 사용자가 화면을 누른 상태로 옆으로 끌기 시작했을 때) DragStartBehavior DragStartBehavior.start
onPageChanged 뷰포트 중앙에 위치한 페이지가 변경될 때마다 호출되는 함수. ValueChanged<int>?  
pageSnapping 페이지 단위로 화면이 넘어갈지 여부. bool true
physics 페이지 뷰가 사용자 입력에 반응하는 방법. ScrollPhysics?  
restorationId 스크롤의 위치 정보를 저장하고 복원하는 역할. String?  
reverse children 위젯을 역방향으로 보여줄 것인지 여부. bool false
scrollDirection 스크롤 방향, children 위젯 나열 방향 Axis Axis.horizontal

allowImplicitScrolling

children 위젯에 내부 스크롤 부여하는지를 의미한다. 말이 조금 어려운데, 특정 화면에서 다른 화면으로 갔다가 다시 돌아왔을 때 이전의 스크롤 위치로 돌아가는지 여부라고도 표현할 수 있겠다.

allowImplicitScrolling: false allowImplicitScrolling: true

allowImplicitScrollingfalse(디폴트)일 때는 첫번째 화면의 스크롤 위치가 다시 맨 위로 돌아갔다. 하지만 pageSnappingtrue일 때는 이전의 스크롤 위치로 되돌아갔다.

pageSnapping

사용자가 화면을 드래그할 때, 페이지 단위로 넘어갈지 여부를 의미한다.

pageSnapping: true pageSnapping: false

pageSnappingtrue(디폴트)일 때는 화면이 페이지 단위로 끊어서 넘어갔다. 하지만 pageSnappingfalse일 때는 ListView처럼 넘어간다.

reverse

children 위젯을 역방향으로 보여줄 것인지 여부를 의미한다.

reverse: false reverse: true

reversefalse(디폴트)일 때는 1-2-3 순서로 화면이 구성되었다. 하지만 reversetrue일 때는 3-2-1 순서로 화면이 구성되는 것을 볼 수 있다.

여기서 주의할 점은 순서가 거꾸로 되었다고 3번 화면부터 보이는 것이 아니다. initialPagechildren을 뒤집기 이전을 기준으로 계산한다.

scrollDirection

PageView의 스크롤 방향, 즉 children 위젯의 나열 방향을 의미한다.

scrollDirection: Axis.horizontal scrollDirection: Axis.vertical

scrollDirectionAxis.horizontal(디폴트)일 설정했을 때는 children 위젯이 가로 방향으로 나열되었다. 하지만 scrollDirectionAxis.vertical일 때는 세로 방향으로 나열되었다.

댓글남기기