본문 바로가기
Development/Flutter

Flutter - 6. Widget? GridView

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

이번 포스팅은 ListView와 유사하지만 격자 형태로 Widget을 표현할 수 있는 GridView를 알아보자.


1. GridView를 생성하는 5개의 방법

GirdView를 생성하는 방법은 5가지가 있다.

  1. GridView
  2. GridView.builder
  3. GridView.count
  4. GridView.extent
  5. GridView.custom

이번 포스팅에서 다룰 것은 1~4번의 방법이다.


2. GridView 생성자 사용

GridView의 생성자의 파라미터는 ListView의 생성자와 거의 유사하다.
추가로, ListView와 GridView는 동일한 부모를 상속하고 있다.

class GridView extends BoxScrollView {
   ...
}

class ListView extends BoxScrollView {
   ...
}

Widget 형태도 기본적으로 Widget 리스트를 스크롤할 수 있게 만드는 형식이고, 둘의 차이는 자식 Widget을 어떻게 나타내는지 차이가 있는 것이다.

무튼 한 행에 3개의 자식을 갖는 GridView를 만들어 보자.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeGridView1()
        )
      )
    );
  }

  Widget makeGridView1() {
    return GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        mainAxisSpacing: 4.0,
        crossAxisSpacing: 8.0,
        childAspectRatio: 1.0
      ),
      children: [
        Container(
          color: Colors.red,
          child: Text("This is Red Container!"),
        ),
        Container(
          color: Colors.blue,
          child: Text("This is Blue Container!"),
        ),
        Container(
          color: Colors.yellow,
          child: Text("This is Yellow Container!"),
        ),
        Container(
          color: Colors.green,
          child: Text("This is Green Container!"),
        ),
        ...
      ],
    );
  }
}

위와 같은 코드를 구성하자.


ListView에서 Delegate에 대해 언급한 적이 있을 것이다.
Delegate는 "대리자" 라는 의미로, 실제 구현을 외부에 위임하고 내부에선 외부에서 구현한 메서드를 참조하는 것이다.

GridView는 Widget을 그려내기 위해 몇 가지 정보가 필요하다.
한 행 혹은 열에 몇 개의 Widget을 그려 낼 것인지, Widget 사이사이의 공간은 몇인지, 각 Widget의 크기 비율은 어떠한지이다.
이것에 대한 정보를 갖는 Delegate 객체를 외부에 위임하여 구현하도록 하고 GridView 내부에서는 이 객체를 전달받아 GridView를 구성한다.
GridView의 생성자에 전달한 "SliverGridDelegateWithFixedCrossAxisCount"를 참고하자.

위 코드를 실행하면 위와 같은 화면을 볼 수 있다.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeGridView1()
        )
      )
    );
  }

  Widget makeGridView1() {
    return GridView(
       scrollDirection: Axis.horizontal,
       ...
    );
  }
}

만약 "scrollDirection" 파라미터에 "Axis.horizontal"를 주면 위와 같은 모습을 볼 수 있다.


3. GridView.builder

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeGridView2()
        )
      )
    );
  }

  Widget makeGridView2() {
    List<Color> colorList = [
      Colors.white,
      Colors.red,
      Colors.blue,
      Colors.green,
      Colors.yellow,
      Colors.brown,
      Colors.pink,
      ...
    ];

    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            mainAxisSpacing: 4.0,
            crossAxisSpacing: 8.0,
            childAspectRatio: 1.0
        ),
        itemCount: colorList.length,
        itemBuilder: (BuildContext context, int index) {
          return Container(
            color: colorList[index],
            child: Text(
              "List item :: $index"
            ),
          );
        }
    );
  }

}

이번엔 GridView.builder를 사용 해 보자.
ListView처럼 Widget을 그리는 데 사용할 데이터의 리스트가 필요하다.
또한 GridView의 생성자를 사용한 것과 같이 gridDelegate가 필요하다.

물론 동일한(혹은 유사한) 결과를 확인할 수 있다.


4. GridView.count

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeGridView3()
        )
      )
    );
  }

  Widget makeGridView3() {
    List<Color> colorList = [
      Colors.white,
      Colors.red,
      Colors.blue,
      Colors.green,
      Colors.yellow,
      Colors.brown,
      Colors.pink,
      ...
    ];

    return GridView.count(
      crossAxisCount: 2,
      crossAxisSpacing: 4.0,
      mainAxisSpacing: 6.0,
      childAspectRatio: 1.0,
      children: List.generate(colorList.length, (index) {
        return Container(
          color: colorList[index],
          child: Text(
              "List item :: $index"
          ),
        );
      })
    );
  }
}

GridView.count는 GridView 생성자를 사용한 방법과 비교하면 조금 더 편하게 사용할 수 있다.


Delegate를 넘기지 않고 파라미터에 직접 crossAxisCount, crossAxisSpacing, mainAxisSpacing, childAspectRatio을 넘기고 있다.


children을 명시적으로 받는 것은 GridView의 생성자를 사용하는 것과 동일하지만 위 예제는 List를 동적으로 생성하여 Widget을 만들어내는 방법을 사용했다.
위 방법 말고 List의 map 함수를 사용하는 방법도 좋다.
(사실 위 방법은 index가 필요해서 한 방법이고, List을 동일하게 생성하고 있어 비효율적일 수 있다.)

결과는 동일하니 첨부하지 않겠다.


5. GridView.extent

이 방법은 GridView의 생성자, GridView.count와 거의 동일하다.
하지만 가로 혹은 세로를 채울 Widget의 개수를 하나의 Widget의 너비를 통해 동적으로 구한다.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      home: SafeArea(
        child: Scaffold(
          body: makeGridView4()
        )
      )
    );
  }

  Widget makeGridView4() {
    List<Color> colorList = [
      Colors.white,
      Colors.red,
      Colors.blue,
      Colors.green,
      Colors.yellow,
      Colors.brown,
      ...
    ];

    return GridView.extent(
        maxCrossAxisExtent: 100.0,
        crossAxisSpacing: 4.0,
        mainAxisSpacing: 8.0,
        childAspectRatio: 1.0,
        children: List.generate(colorList.length, (index) {
          return Container(
            color: colorList[index],
            child: Text(
                "List item :: $index"
            ),
          );
        })
    );
  }
}

위의 코드를 보자. 다른 방법은 crossAxisCount를 받았다면, maxCrossAxisExtent를 받고 있다.
한개의 자식 Widget의 최대 너비 혹은 높이값이다. 이 값을 사용하여 crossAxisCount를 계산해준다.

maxCrossAxisExtent 값을 변경해가며 확인해보자. 
내 기준으로 maxCrossAxisExtent가 200일 때는 좌측, 100일때는 우측과 같다.
이는 디바이스의 사이즈에 따라 바뀔 수 있다.


GridView를 구성하는 방법도 어렵지 않았다.
Delegate라는 개념이 필요했지만, 단순하게 생각하면 객체를 생성해서 넘겨주는 것 뿐이다.

이제 화면을 구성할 수 있는 기본적인 Widget은 살펴보았다.
이제 "앱" 스럽게 화면을 구성할 수 있는 Widget들을 살펴보도록 할 것이다.

반응형