Stateless로도 데이터를 받아오는 함수를 기다리고, 작동이 완료하였는지에 대한 상태를 확인할 수 있다. 이전에는 StatefulWidget을 사용해서, bool isLoading 변수를 통해 데이터를 받아오는 것이 완료되었는지 확인하였다.
FutureBuilder에는
future : 무엇을 기다릴 것인지
builder : (context, snapshot)가 있다.
snapshot에는 future의 현재 상황이 담겨있다.
예를 들어 snapshot.hasdata는 future을 가져왔는가 (true, false)를 알 수 있다. 앞서 말했던, isLoading 변수를 통해 데이터를 받아왔는지 체크할 필요가 없는 것이다.
snapshot.data에는 가져온 데이터가 있다. 이 예제의 FutureBuilder의 builder 내 snapshot.data에는 Api가 리턴한 List이 들어있다.
Api를 통해 받아온 웹툰 데이터를 list의 형태로 출력하는 데에 사용된다.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
"오늘의 웹툰",
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
),
backgroundColor: Colors.white,
foregroundColor: Colors.green.shade500,
elevation: 3,
),
body: FutureBuilder(
future: webtoons,
builder: (context, snapshot) {
if (snapshot.hasData) {
// ***이 부분***
return ListView(
children: [
for (var webtoon in snapshot.data!) Text(webtoon.title)
],
);
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.green.shade600,
),
);
}
},
),
);
}
}
List VIew를 사용하는 해당 방법의 문제는 최적화 되지 않았다는 점이다.
한 번에 모든 내용을 빌드하기 때문에 > 인스타 그램과 같은 피드를 로딩한다면 문제가 생길 것이다 => 그에 대한 해결책이 ListVeiw.builder이다.
body: FutureBuilder(
future: webtoons,
builder: (context, snapshot) {
if (snapshot.hasData) {
// 여기 **
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var webtoon = snapshot.data![index];
return Text(webtoon.title);
},
);
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.green.shade600,
),
);
다음과 같이, ListView.builder에는 필수적으로 itemBuilder가 필요하다. 이는 FutureBuilder의 builder와 유사한데, itemBuilder : (context, index)의 형태를 띈다. 우리는 index를 통해 snapshot.data 담긴 배열의 요소에 접근 가능하다 snapshot.data![index]
이전 ListView와 달리 ListView.builder는 사용자가 보는 만큼만 로딩을 한다는 점이다. 웹툰 데이터가 담긴 30짜리 길이의 리스트라 하더라도, 사용자가 10개만 한 번에 볼 수 있다면 10개만 로딩한다. 이후 사용자가 스크롤을 통해 나머지 요소를 보게 된다면 -> 이후의 요소들을 10개 로딩한다.
이 상황에서는 '미국시골집' 까지만 itemBuilder에서 로딩을 한다. print(index)를 해보아도, 0~6까지만 출력된다.
이후 더 오른쪽으로 스크롤 하게 된다면 index의 값이 증가한다. 이 때도 30개 짜리 길이의 리스트이지만, 사용자가 보는 만큼만 index 12까지 로딩하는 것이다.
오른쪽 -> 방향으로 스크롤 했다가 왼쪽 <-> 방향으로 스크롤 한 경우도 동일하다.
43까지 로딩한 후, 왼쪽으로 로딩하게 되면 더 작은 번호의 인덱스의 요소도 필요할 때 로딩하게 된다. 사용자에게 보이지 않는 데이터 요소들은 메모리에서 삭제하는 것이다.
ListView.build에 하나의 필수요소가 추가되었다. separatorBuilder로, 요소를 구분하는 위젯을 리턴하는 것이다. 여기서는 SizedBox를 두었기 때문에, 각 요소의 Text 사이에 공백이 들어가게 된다.
body: FutureBuilder(
future: webtoons,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
print(index);
var webtoon = snapshot.data![index];
return Text(webtoon.title);
},
separatorBuilder: (context, index) {
return const SizedBox(
width: 20,
);
},
);
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.green.shade600,
),
);
}
},
),
추가적으로, 요소 사이 SizedBox로 인하여 사용자가 한 번에 보는 요소의 개수가 줄어들었다. 떄문에 콘솔창에서도 한 번에 렌더되는 요소의 개수가 준 것을 볼 수 있다.