본문 바로가기
Development/Flutter

Flutter - 5. Widget? ListView

by du.it.ddu 2021. 1. 12.
반응형

이전 포스팅에서 다양한 Widget들을 배치하기 위해 Row와 Coulmn을 다뤄보았다.
Row, Column에 많은 Widget을 배치하게 되어 화면을 넘어가게 되었을 때, 스크롤이 되지 않고 잘리는 모습을 확인할 수 있다.

이번 포스팅은 많은 Widget을 리스트 형식으로 배치하고 스크롤이 가능한 Widget인 ListView에 대해 알아 볼 것이다.


1. ListView를 생성하는 4개의 방법

우선, ListView를 생성하는 방법은 4개이다.

  1. ListView
  2. ListView.builder
  3. ListView.separated
  4. ListView.custom

이 포스팅에서 다룰 것은 1~3번의 방법이며, 4번은 Delegate란 개념이 포함되어 있어 추후 포스팅으로 미루겠다.

1번은 명시적으로 ListView의 생성자를 호출하고 children을 전달하는 방법이다.

2번은 builder를 사용하여 임의의 데이터에 대한 ListView를 구성하는 방법이다. Android를 다뤄보았다면 RecyclerView를, iOS를 다뤄보았다면 TableView를 생각하면 된다.

3번은 2번에 아이템을 구분하는 구분자(Divider or Separator)를 제공하는 방법을 더한 것이다.


2. 명시적인 children을 갖는 ListView 생성

생성자를 확인 해 보면, 역시나 다양한 파라미터가 존재한다.
스크롤 방향, 역순으로 ListView를 그릴것인지 등등 아주 다양하다. 다 알기도 어렵다

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeListView1()
        )
      )
    );
  }

  Widget makeListView1() {
    return ListView(
      children: [
        Container(
          color: Colors.red,
          height: 200,
          child: Text("This is Red Container!"),
        ),
        Container(
          color: Colors.blue,
          height: 100,
          child: Text("This is Blue Container!"),
        ),
        Container(
          color: Colors.yellow,
          height: 300,
          child: Text("This is Yellow Container!"),
        ),
        Container(
          color: Colors.green,
          height: 600,
          child: Text("This is Green Container!"),
        ),
      ],
    );
  }
}

위와 같은 코드를 작성 해 보자.
ListView의 children 파라미터에 List<Widget>을 받으므로, 내부에 어떤 Widget이든 전달해줄 수 있다.
Container에 다양한 height를 주고 child에 Text를 주어 확인할 수 있도록 구성하였다.

위와 같은 결과를 확인할 수 있을 것이다. 스크롤 또한 가능하다.

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeListView1()
        )
      )
    );
  }

  Widget makeListView1() {
    return ListView(
      scrollDirection: Axis.horizontal,
      children: ...
    );
  }
}

동일한 코드에서, ListView에 scrollDirection에 Axis.horizontal을 전달하면, 스크롤 방향이 가로로 바뀐다.
스크롤 방향이 바뀌면서 Widget의 배치 방법도 변경된다.

ListView는 손쉽게 가로 혹은 세로로 스크롤 가능하게 Widget들을 배치할 수 있다.


3. 임의의 데이터로 ListView 생성

ListView는 가변적인 데이터와 그에 따라 Widget을 동적으로 생성하는 것으로 많이 활용된다.
이번엔 데이터에 따라 동적으로 Widget을 생성할 수 있는 ListView에 대해 알아본다.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeListView2()
        )
      )
    );
  }
  
  Widget makeListView2() {
    List<Color> colorList = [
      Colors.white,
      Colors.red,
      Colors.blue,
      Colors.green,
      Colors.yellow,
      Colors.brown,
      Colors.pink,
    ];

    return ListView.builder(
        itemCount: colorList.length,
        itemBuilder: (BuildContext context, int index) {
          return Container(
            height: (index+1) * 50.0,
            color: colorList[index],
            child: Text(
              "List item :: $index"
            ),
          );
        }
    );
  }
}

ListView.builder 함수를 보도록 한다.
itemCount는 ListView가 그려낼 Widget의 개수이다. 데이터의 크기에 따라 itemCount를 변경해주면 된다.
itemBuilder는 그려낼 Widget을 반환해주는 Builder를 구현하는 부분이다.

위 예제는 List<Color> colorList를 데이터로 하여, colorList의 개수만큼 Widget을 그려낸다.
Widget는 Text를 하나 갖는 Container이며, height를 index에 따라 다르게 갖도록 하였다.

그리고 위와 같은 결과를 확인할 수 있다.


4. 임의의 데이터로 생성한 ListView에 구분선을 주자.

3번에서 한 결과를 보면, 아이템 간에 구분선이 없다.
어떻게 구성하냐에 따라 다르겠지만, 아이템 사이의 구분을 위해 간격을 준다거나 구분선을 준다거나 하는 방법이 필요할 수 있다.

Flutter는 이러한 방법으로 ListView.separated를 제공한다.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeListView2()
        )
      )
    );
  }
  
  Widget makeListView3() {
    List<Color> colorList = [
       ...
    ];

    return ListView.separated(
      ...
      separatorBuilder: (BuildContext context, int index) {
        return Divider(
          height: 10.0,
          thickness: 5.0,
          color: Colors.blueGrey,
        );
      },
    );
  }
}

위 코드에서 ListView.builder를 ListView.separated로 변경하고, separatorBuilder 파라미터를 구성하자.
itemBuilder와 유사한 방식이다.

위에선 가볍게 Divider를 구현하였지만, 원하는 Widget으로 구현하여도 무방할 것이다.
위 Divider는 height는 10.0px, 그리고 Divider에서 그려낼 구분선의 두께(thickness)는 5.0px, 색깔은 blueGrey이다.

결과는 위와 같다. 아이템 사이에 구분선이 생겼다.
구분선의 개수는 itemCount - 1 과 같다. 아이템 사이사이에 만들어주기 때문이다.
height가 10.0px, thickness가 5.0px이어서 BlueGrey색의 구분선 아래위로 비어있는 흰 색이 보이게 되었다.
height와 thickness를 동일하게 하면 꽉 찬 Divider를 확인 할 수 있다.


간단하게 ListView를 구성하는 방법을 알아보았다.
하지만 이런 한 행에 하나의 아이템을 구성하는 테이블 형식이 아닌, 그리드 형식의 Widget을 구성하고 싶을 수 있다.
예를 들면 갤러리, 인스타그램 등등이 될 수 있겠다.

이러한 Widget을 구성하기 위해 다음 포스팅에선 GridView에 대해 알아 볼 것이다.

반응형