말랑한 하루

[Flutter] Infinity Scroll (무한 스크롤) 본문

개발/Flutter

[Flutter] Infinity Scroll (무한 스크롤)

지수는말랑이 2024. 2. 20. 18:31
반응형

무한 스크롤에 대한 Demo 및 Code는 다음 경로를 참고하세요.

※ reference : https://github.com/Jisup/flutter_sample/tree/main/lib/page/infinityScroll

 

무한 스크롤은 사용자 경험 디자인 패턴 중 하나로, 사용자가 스크롤을 활용하여 콘텐츠를 제공받을 때 일정량의 정보를 소비하고 나면 자연스럽게 다음 콘텐츠로 넘어갈 수 있도록 새로운 콘텐츠를 자동으로 추가하여 페이지를 넘기는 번거로움 없이 연속적인 내용을 볼 수 있게 해주는 방식입니다.

 

개발을 진행함에 있어 신경 써야 하는 부분은 Viewport, Content의 크기 관리와 사용자의 스크롤 제스처 조작입니다. 사용자가 Viewport 내에서 Content의 개수, 크기와 관련되어 일정량의 콘텐츠를 소비하였을 때 새로운 콘텐츠를 불러올 수 있도록 로직을 설계하는 것에 집중해야 합니다.

 

Flutter에서 무한 스크롤을 구현하기 위한 방법은 무엇이 있는지 UX 순서에 따라 기술하겠습니다. 구현에 사용되는 데이터는 {JSON} Placeholder를 활용하였습니다.

 

사용자가 콘텐츠를 소비하는 UX는 다음과 같습니다.

🍒 사용자가 콘텐츠를 스크롤 한다.

🍒 사용자의 스크롤이 Viewport의 마지막에 도달하거나, 사용자의 스크롤이 특정량의 콘텐츠를 소비한 Viewport에 도달한다.

🍒 사용자의 스크롤 제스처(아래에서 위로 당기기)를 통해 새로운 콘텐츠를 불러온다.

🍒 사용자가 새로운 콘텐츠를 스크롤 한다.

 

🐇 ScrollController

ScrollController에는 position 속성을 통해 스크롤의 현재 위치을 나타내는 pixel와 스크롤의 최대 크기를 나타내는 maxScrollExtent 값이 있습니다.

 

이 두 값을 활용하여 사용자의 스크롤이 컨텐츠의 어느 부분에 존재하는지 파악할 수 있고, 개발자는 특정 임계값에 새로운 콘텐츠를 불러오기 위한 로직을 추가할 수 있습니다.

 

StatefulWidget을 활용하여 ScrollController를 생성하고 Listener를 할당하여 원하는 동작을 이끌어냅니다. 핵심 코드는 다음과 같습니다.

  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() async {
    final maxScroll = _scrollController.position.maxScrollExtent;
    final currentScroll = _scrollController.position.pixels;

    // if (maxScroll != currentScroll) {
    if (maxScroll - currentScroll < contentHeight) {
      if (!isLoading) {
        setState(() {
          pullHeight = contentHeight;
        });
        await _fetchPhotos();
      }
      setState(() {
        pullHeight = 0;
      });
    }
  }

🐇 Listener + ScrollController

ScrollController의 Listener를 사용하지 않고, Flutter의 Listener Widget을 활용해도 좋습니다.

 

onPointerMove 속성을 통해 마우스의 움직임을 확인하고, onPointerUp 속성을 통해 마우스의 움직임이 멈춤을 확인할 수 있습니다. 하지만, ScrollControlller에서 현재 스크롤 정보와 최대 스크롤 크기 정보를 활용해야 하므로, 기존 ScrollController의 Listener를 사용하는 것이 낫습니다.

 

그러나, Listener Widget을 활용하면 사용자의 스크롤 속도(scrollVelocity)에 맞춰 로딩중인 애니메이션 컨테이너를 조작할 수 있습니다. 마치, 배달의 민족 애플리케이션의 땡겨요 처럼 말입니다.

 

스크롤이 작동하는 Viewport를 자식으로 가지는 Listener Widget을 생성하고, onPointerMove/onPointerUp 속성을 구현하여 사용자의 스크롤 제스쳐(땡겨요)를 통해 새로운 데이터를 불러올 수 있습니다. 기존 ScrollController처럼 사용자가 새로운 콘텐츠를 부드럽게 경험하게 만들기 위해서는 임계값의 설정을 조정해주면 됩니다.

Listener(
  onPointerMove: (event) {
    if (event.delta.dy >= 0) return;
    // if (_scrollController.position.maxScrollExtent - contentHeight <
    if (_scrollController.position.maxScrollExtent !=
        _scrollController.position.pixels) return;
    setState(() {
      pullHeight += -event.delta.dy;
    });
  },
  onPointerUp: (event) async {
    if (pullHeight > contentHeight && !isLoading) {
      setState(() {
        pullHeight = contentHeight;
      });
      await _fetchPhotos();
    }
    setState(() {
      pullHeight = 0;
    });
  },
  child: Scaffold(...),
);

🐇 infinity_scroll_pagination Library

※ reference : https://pub.dev/packages/infinite_scroll_pagination

 

무한 스크롤 페이지 매김, 끝없는 스크롤 페이지 매김, 자동 페이지 매김, 지연 로딩 페이지 매김, 프로그레시브 로딩 페이지 매김 등으로 알려진, 사용자가 화면을 아래로 스크롤할 때 작은 항목 덩어리를 천천히 로드하고 표시하는 데 도움이 되는 독립적이고 확장 가능하며 고도로 사용자 정의 가능한 패키지입니다.

$ flutter pub add infinite_scroll_pagination

라이브러리 내부 PagingController와 PageListView Widget 그리고 PagedChildBuilderDelegate Widget을 활용하여 요소를 간편하게 구성해갈 수 있습니다.

🐇 FlatList Library

※ reference : https://pub.dev/packages/flat_list

반응형
Comments