Dart에 발 들이기

바람찬허파·2023년 9월 9일
0

FutureBuilder

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이 들어있다.

List View

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이다.

ListView.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.separated

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로 인하여 사용자가 한 번에 보는 요소의 개수가 줄어들었다. 떄문에 콘솔창에서도 한 번에 렌더되는 요소의 개수가 준 것을 볼 수 있다.

0개의 댓글