Scroll View 만들기(3) - ListView

Scroll View 만들기(1) - SingleChildScrollView
Scroll View 만들기(2) - CustomScrollView
Scroll View 만들기(4) - GestureDetector + Stack 1편
Scroll View 만들기(5) - GestureDetector + Stack 2편

1편과 2편에서는 각각 SingleChildScrollView, CustomScrollView에 대해서 다뤄보았다. 이번 글에는 ListView에 대해서 다뤄보도록 하자.

ListView와 SingleChildScrollView + Column은 보여지는 뷰에는 큰 차이가 없다. 하지만 뷰의 구조나 위젯들의 목적에 따라서 ListView를 사용해야 하는 경우가 있다.

반복되는 위젯을 넣어줄 때 ListView를 자주 사용하는데, ListView는 ListView.builder형태로도 사용이 가능하기에 추가적으로 ListView.builder에 대해서도 알아보도록 하자.

Flutter

ListView는 스크롤을 만들어 주는 뷰이기도 하지만 그 쓰임새가 조금 다르다. 보통 동일한 위젯 구조를 가지는 경우에 많이 사용되는데, 이러한 경우에는 SingleChildScrollView를 사용하는 것보다는 ListView를 사용해주는 것이 성능면에서도 좋다고 한다.

ListView를 생성하는 방법에는 ListView, .builder, .custom, .separated 이렇게 4가지 방법으로 생성할 수 있는데, 여기서는 가장 기본 사용 방법인 ListView와 .builder 방식에 대해서만 알아보도록 하겠다.

ListView

먼저 ListView의 가장 기본이 되는 사용 법이다. 다른 Scroll View와 옵션은 비슷하다고 보면 되고 childern을 넣어줄 수 있다.

ListView에 위젯들을 배치하게 되는데, 아래와 같은 방법으로 index값을 설정하여 동일하게 반복되는 뷰를 넣어줄 수도 있다.
하지만 ListView를 아래의 예제의 방식대로 사용하게 되면 문제가 있다. 자세한 내용은 아래에서 좀 더 살펴보자.

ListView({
  Key? key,
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController? controller,
  bool? primary,
  ScrollPhysics? physics,
  bool shrinkWrap = false,
  EdgeInsetsGeometry? padding,
  double? itemExtent,
  Widget? prototypeItem,
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  bool addSemanticIndexes = true,
  double? cacheExtent,
  List<Widget> children = const <Widget>[],
  int? semanticChildCount,
  DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
  String? restorationId,
  Clip clipBehavior = Clip.hardEdge,
})
 ListView(
        children: [
          ...List.generate(
              1000,
              (index) => Center(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 12),
                      child: Text(
                        "index : $index",
                        style: TextStyle(
                            fontSize: 20,
                            fontWeight: FontWeight.bold,
                            color: Colors.accents[index % 15]),
                      ),
                    ),
                  ))
        ],
      ),

ListView.builder

이번에는 .builder 형태로 builder를 생성하여 ListView를 만드는 방법이다. 옵션 값은 동일하지만 다른 점이 있다면 ListView.builder에는 children이 없고 itemBuilder라는 옵션이 필수 값으로 되어있다.

아래 사용 방법을 보면 context, index값이 builder에서 나오는 것을 확인할 수 있는데, itemCount로 넣어주는 값이 바로 index형태로 제공을 해준다.

List 타입의 변수에 각 index 값을 통해서 접근하여 뷰에 노출이 되는 방식이다.

ListView.builder({
  Key? key,
  Axis scrollDirection = Axis.vertical,
  bool reverse = false,
  ScrollController? controller,
  bool? primary,
  ScrollPhysics? physics,
  bool shrinkWrap = false,
  EdgeInsetsGeometry? padding,
  double? itemExtent,
  Widget? prototypeItem,
  required Widget Function(BuildContext, int) itemBuilder,
  int? Function(Key)? findChildIndexCallback,
  int? itemCount,
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  bool addSemanticIndexes = true,
  double? cacheExtent,
  int? semanticChildCount,
  DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
  String? restorationId,
  Clip clipBehavior = Clip.hardEdge,
ListView.builder(
	itemCount: 10000,
	itemBuilder: (context, index) {
		return Padding(
			padding: const EdgeInsets.symmetric(vertical: 12),
				child: Text(
					"Builder : $index",
					textAlign: TextAlign.center,
				),
			);
		})

ListView VS ListView.builder

ListView와 ListView.builder 방식에 대해서 간단하게 살펴 보았는데, 두 개의 View의 방식에는 과연 차이가 없을까 라는 생각을 할 수있다.
ListView에도 List.generator, 반복문 for 등을 통해서 똑같이 ListView.builder 처럼 사용할 수 있는데, 왜 ListView.builder를 따로 제공하는 것일까?

두 개의 뷰의 차이에는 ListView.builder 방식이 성능에 더 우수하다고 한다. ListView.builder는 itemCount를 통해서 정확히 몇 개의 뷰가 랜더링 되는지를 알 수 있어 더 빠르게 랜더링이 가능하다고 한다.

실제로 ListView와 ListView.builder 두 가지의 방식으로 고화질, 고용량의 이미지를 노출한다고 했을 때, 리스트의 갯수가 적을 때는 문제가 없지만 그 수를 계속해서 늘려가다 보면 Memory 부분에서 차이가 점점 크게 나는 것을 확인할 수 있다.

해당 부분에 대해서 정확히 확인하기 위해 추가적으로 고화질의 네트워크 이미지를 받아와 메모리 부분에서 얼마만큼의 차이를 발생시키는지 추가적인 글을 작성하도록 하겠다.

마무리

지금 까지 Flutter에서 스크롤이 가능한 뷰를 생성하는 방법인 SingledChildScrollView, CustomScrollView, ListView에 대해서 살펴 보았는데, 이런 기본적인 스크롤 뷰를 생성하는 글을 작성한 이유는 바로 직접 스크롤 뷰를 만들어 보기 위해서였다.

개발을 하다보면 가끔 직접 스크롤을 만들어서 다양한 뷰를 만들고 싶을 때가 있다. 스크롤을 직접 만들어서 사용하는 부분에 대해서는 캐시, 포지션, 거리, 속도, 애니메이션 등 처리하여야 할 부분이 많았지만 그래도 한 번 만들어 보자.

다음 시간에는 GestureDetector로 직접 스크롤 뷰를 만들어 보는 내용에 대해서 글을 작성하도록 하겠다.

profile
Flutter Developer

0개의 댓글