말랑한 하루
[Flutter] Infinity Scroll (무한 스크롤) 본문
무한 스크롤에 대한 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
'개발 > Flutter' 카테고리의 다른 글
[Flutter] (Project) MapleApp: 31. 심사 통과 (0) | 2024.02.29 |
---|---|
[Flutter] Infinity Carousel (무한 캐러셀) (0) | 2024.02.23 |
[Flutter] TDD (Test Driven Development) - Riverpod (0) | 2024.02.08 |
[Flutter] TDD (Test Driven Development) - UseCase (0) | 2024.02.07 |
[Flutter] TDD (Test Driven Development) - Repository (0) | 2024.02.06 |