프로그래밍/flutter

infinite PageView in Flutter

인썸니아 2023. 2. 7. 22:14

PageView 위젯으로 여러개의 페이지를 생성하고 animateToPage 메소드를 통해 이동해 보면 기본적으로는 아래와 같이 움직인다. 마지막 페이지에서 첫페이지 이동시 그 사이의 모든 페이지를 거치면서 이동하게 된다.

 

 

하지만, 아래처럼 마지막 페이지에서 다시 첫번째 페이지로 순환하는 형태로 자연스럽게 이동하고 싶을 때가 있다.

 

 

이를 구현하기 위한 옵션이 기본적으로 존재한다면 참 편할텐데... 없다.. 조금은 편법스럽게 처리하던가.. 아니면 외부 package를 사용하여야 한다.

 

PageView.builder 사용

PageView.builder 를 사용하고 itemCount 속성을 지정하지 않으면, Page를 무한으로 만들어 내는 것이 가능하다.

final PageController pageController = PageController(initialPage: 1000);

    ...

PageView.builder(
  controller: pageController,
  allowImplicitScrolling: true,
  itemBuilder: (context, idx) {
    return Image.asset(
      'assets/images/image_${idx%3+1}.jpeg',
      fit: BoxFit.cover,
    );
  },
),

 

코드를 보면 알 수 있겠지만, itemCount 를 설정하지 않았으므로 일단 무한하게 페이지를 이동할 수 있게 된다. 마지막 페이지에서 다시 첫번째 페이지로 이동하는 것이 목표이므로, itemBuilder 에 구현된 함수에 idx 를 나머지 연산자(%)로 처리하였다.

여기서, initialPage 를 1000으로 한 이유는, 첫번째 페이지에서 반대방향으로 이동하는 경우에 대해서도 대응하기 위해서이다.

 

carousel_slider package 사용

무한 순환 하는 PageView 를 구현해 놓은 package 를 사용하면 좀 더 쉽게 구현이 가능하다.

 

carousel_slider | Flutter Package

A carousel slider widget, support infinite scroll and custom child widget.

pub.dev

CarouselSlider(
  items: imageList,
  carouselController: pageController,
  options: CarouselOptions(
    height: height,
    viewportFraction: 1.0,
    onPageChanged: (page, reason) {
      if(page >= imageList.length-1) {
        precacheImage(imageList[0].image, context);
      }
      else {
        precacheImage(imageList[page+1].image, context);
      }
    },
  ),
),

이 패키지의 CarouselSlider 위젯은 autoPlay 속성을 가지고 있어서 별다른 처리 없이 간편하게 자동으로 순환하는 페이지를 생성할 수 있으며, duration, interval, curve 등도 간단하게 설정 가능하다.

 

precacheImage() 함수

페이지를 이동하는 도중에 다음 페이지에 적용되는 이미지가 뒤늦게 로딩이 되어 나타나는 현상을 볼 수 있다. 뭔가 자연스럽지 않고 상당히 거슬린다. 이 때에 precacheImage() 함수를 사용할 수 있는데, 아래와 같이 onPageChanged 속성의 콜백함수에서 사용할 수 있다.

onPageChanged: (int page) {
  if(page >= imageList.length-1) {
    precacheImage(imageList[0].image, context);
  }
  else {
    precacheImage(imageList[page+1].image, context);
  }
},

 

 

반응형