1. 무한스크롤 구현
NotificationListener 위젯
- 하위 위젯에서 특정한 알림(스크롤 이동 등)이 발생 했을 때
onNotification 속성에 정의한 함수 실행
onNotification 속성에 들어가는 함수에는 notification 이 파라미터로 들어감
- 스크롤 시작할 땐
ScrollStartNotification 객체,
- 스크롤 이동할 땐
ScrollUpdateNotification 객체,
- 스크롤 끝났을 땐
ScrollEndNotification 객체가 전달됨
ScrollUpdateNotification 객체엔 ScrollMetrics 타입의 metrics 속성이 있음
metrics.pixels: 현재 스크롤 위치.
metrics.maxScrollExtent: 스크롤 가능한 최대 범위.
metrics.minScrollExtent: 스크롤 가능한 최소 범위.
metrics.extentAfter: 현재 위치 이후 남은 스크롤 거리.
metrics.extentBefore: 현재 위치 이전의 스크롤 거리.
- 현재 스크롤 위치(metrics.pixels)가 스크롤 가능한 최대 범위(metrics.maxScrollExtent)보다 클 때 데이터 추가 요청
onNotification 리턴타입 bool ⇒ 이벤트 버블링 취소할지 여부
- 버블링 : 하위 노드에서 발생한 이벤트가 상위 노드로 전파되는 것 의미 ⇒ false 로 리턴
NotificationListener(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.pixels >=
notification.metrics.maxScrollExtent) {
}
}
return true;
},
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
},
),
)
2. 새로고침
RefreshIndicator 위젯
- 당겨서 새로고침(Pull-to-Refresh) 동작을 쉽게 구현할 수 있는 머티리얼 디자인 위젯
onRefresh 속성에 Future<void> Function() 선언하여 구현
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<int> items = List.generate(20, (index) => index).toList();
bool isFetching = false;
void fetchMore() async {
if (isFetching) {
return;
}
print("fetchMore");
isFetching = true;
await Future.delayed(Duration(seconds: 3));
final newList =
List.generate(20, (index) => index + items.last + 1).toList();
items.addAll(newList);
setState(() {});
isFetching = false;
}
Future<void> onRefresh() async {
print("onRefresh");
if (isFetching) {
return;
}
isFetching = true;
await Future.delayed(Duration(seconds: 3));
items = List.generate(20, (index) => index).toList();
setState(() {});
isFetching = false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: NotificationListener(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.pixels >=
notification.metrics.maxScrollExtent) {
fetchMore();
}
}
return true;
},
child: RefreshIndicator(
onRefresh: onRefresh,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
color: Colors.amber,
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(20),
child: Text('${items[index]}'),
);
},
),
),
),
);
}
}