Flutter

[Flutter] GridView란? GridView 사용법

참깨빵위에참깨빵_ 2024. 8. 22. 02:54
728x90
반응형

격자 형태의 뷰를 플러터에서 구현하려면 GridView 클래스를 사용해서 구현할 수 있다.

 

https://api.flutter.dev/flutter/widgets/GridView-class.html

 

GridView class - widgets library - Dart API

A scrollable, 2D array of widgets. The main axis direction of a grid is the direction in which it scrolls (the scrollDirection). The most commonly used grid layouts are GridView.count, which creates a layout with a fixed number of tiles in the cross axis,

api.flutter.dev

그리드의 main축 방향은 스크롤되는 방향이다. 가장 일반적으로 쓰이는 그리드 레이아웃은 가로축에 고정된 수의 타일로 레이아웃을 만드는 GridView.count와 최대 가로축 범위를 갖는 타일로 레이아웃을 만드는 GridView.extent다. 커스텀 SliverGridDelegate는 정렬되지 않거나 겹치는 배열을 포함해서 임의의 2D 자식 배열을 만들 수 있다. 많은(또는 무한한) 수의 자식이 있는 그리드를 만들려면 GridView.builder 생성자를 써서 그리드 델리게이트에 SliverGridDelegateWithFixedCrossAxisCount 또는 SliverGridDelegateWithFixedCrossAxisExtent 중 하나를 사용한다
커스텀 SliverChildDelegate를 사용하려면 GridView.count를 사용하고 자식의 선형 배열을 만들려면 리스트뷰를 쓴다...(중략)

 

아래는 그리드뷰 예시 코드다. 공식문서에서 말한 GridView.count는 사용하지 않고 만들었다.

실행하면 아래 화면을 볼 수 있다.

 

 

Text를 표현할 basic_text.dart 파일과 그리드뷰를 표현할 grid_screen.dart 파일을 각각 만들어서 메인에서 사용했다.

먼저 basic_text.dart 파일이다.

 

import 'package:flutter/material.dart';

class BasicText extends StatelessWidget {
  const BasicText({
    super.key,
    required this.number,
    required this.color,
  });

  final String number;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Center(
        child: Text(
          number,
          style: TextStyle(
            fontSize: 20.0,
            fontWeight: FontWeight.bold,
            color: Colors.black,
          ),
        ),
      ),
    );
  }
}

 

 

다음은 grid_screen.dart다.

 

import 'package:flutter/material.dart';
import 'package:test_flutter_app/screens/basic_text.dart';

class GridScreen extends StatelessWidget {
  const GridScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        childAspectRatio: 3 / 2,
        crossAxisSpacing: 20,
        mainAxisSpacing: 20,
      ),
      children: const [
        BasicText(
          number: "1",
          color: Colors.limeAccent,
        ),
        BasicText(
          number: "2",
          color: Colors.red,
        ),
        BasicText(
          number: "3",
          color: Colors.orange,
        ),
        BasicText(
          number: "4",
          color: Colors.blue,
        ),
        BasicText(
          number: "5",
          color: Colors.lightBlueAccent,
        ),
        BasicText(
          number: "6",
          color: Colors.purple,
        ),
      ],
    );
  }
}

 

GridView를 사용하려면 gridDelegate를 필수로 구현하라고 컴파일 에러가 발생한다.

필수 구현 대상이라 구현하긴 해야겠는데 대체 뭘까?

 

https://api.flutter.dev/flutter/widgets/GridView/gridDelegate.html

 

gridDelegate property - GridView class - widgets library - Dart API

SliverGridDelegate gridDelegate final A delegate that controls the layout of the children within the GridView. The GridView, GridView.builder, and GridView.custom constructors let you specify this delegate explicitly. The other constructors create a gridDe

api.flutter.dev

그리드뷰 안의 자식 레이아웃을 제어하는 델리게이트다. GridView, GridView.builder, GridView.custom 생성자를 쓰면 이 델리게이트를 명시적으로 지정할 수 있다. 다른 생성자는 암시적으로 gridDelegate를 생성한다

 

gridDelegate의 값으로 SliverGridDelegateWithFixedCrossAxisCount라는 엄청 긴 이름의 클래스를 넘긴다. 이건 뭔지 확인한다.

 

https://api.flutter.dev/flutter/rendering/SliverGridDelegateWithFixedCrossAxisCount-class.html

 

SliverGridDelegateWithFixedCrossAxisCount class - rendering library - Dart API

SliverGridDelegateWithFixedCrossAxisCount class Creates grid layouts with a fixed number of tiles in the cross axis. For example, if the grid is vertical, this delegate will create a layout with a fixed number of columns. If the grid is horizontal, this de

api.flutter.dev

십자축에 고정된 수의 타일이 있는 그리드 레이아웃을 만든다. 그리드가 세로인 경우 이 델리게이트는 고정된 수의 column이 있는 레이아웃을 만든다. 그리드가 가로면 고정된 수의 row로 레이아웃을 만든다
이 델리게이트는 같은 크기, 간격의 타일로 그리드를 만든다
const SliverGridDelegateWithFixedCrossAxisCount({
  required this.crossAxisCount,
  this.mainAxisSpacing = 0.0,
  this.crossAxisSpacing = 0.0,
  this.childAspectRatio = 1.0,
  this.mainAxisExtent,
}) : assert(crossAxisCount > 0),
     assert(mainAxisSpacing >= 0),
     assert(crossAxisSpacing >= 0),
     assert(childAspectRatio > 0),
     assert(mainAxisExtent == null || mainAxisExtent >= 0);

 

필수 매개변수인 crossAxisCount에는 세로 열을 총 몇 개 만들 것인지 숫자를 넣는다.예시 코드에선 2를 넣었는데 그래서 세로 2열이 화면에 표시된다.

그 외 선택 매개변수인 AxisSpacing들은 그리드 아이템들을 가로, 세로로 얼마나 떨어뜨려놓을지를 결정한다. 양 옆 마진을 추가해서 화면 양 옆으로부터 떨어뜨려놓진 않는다. childAspectRatio는 그리드 아이템의 가로세로 비율을 결정한다. 3 / 2를 넣었으니 3 / 2 = 1.5가 되서, 각 아이템은 1.5의 가로세로 비율을 갖고 화면에 표시된다. 직접 숫자를 조절해 보면서 어떻게 작동하는지 보면 금방 이해될 것이다. 당연하지만 분수 형태가 아니라 소수 형태로 넣어도 된다.

그리고 GridView는 여러 위젯들을 표시하기 위해 children 매개변수를 제공한다. 이 안에 그리드 형태로 표시할 위젯들을 넣으면 된다.

 

다음은 GridView.builder()를 사용하도록 바꾼 예시다.

 

import 'package:flutter/material.dart';
import 'package:test_flutter_app/screens/basic_text.dart';

class GridScreen extends StatelessWidget {
  const GridScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      padding: const EdgeInsets.all(12.0),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        childAspectRatio: 1,
        crossAxisSpacing: 20,
        mainAxisSpacing: 20,
      ),
      itemCount: 6,
      itemBuilder: (context, index) {
        return const Column(
          children: [
            BasicText(
              number: "1",
              color: Colors.limeAccent,
            ),
            BasicText(
              number: "2",
              color: Colors.red,
            ),
            BasicText(
              number: "3",
              color: Colors.orange,
            ),
            BasicText(
              number: "4",
              color: Colors.blue,
            ),
            BasicText(
              number: "5",
              color: Colors.lightBlueAccent,
            ),
            BasicText(
              number: "6",
              color: Colors.purple,
            ),
          ],
        );
      },
    );
  }
}

 

 

itemCount는 선택 구현사항인데 구현하지 않으면 거의 무한대로 생성된다. 여기선 6으로 하드코딩했지만, 보통 리스트 형태로 그리드뷰에 표시할 데이터를 넘겨받을테니 그 리스트의 크기를 넣어주면 적절하게 생성될 것이다.

itemBuilder에는 context, index를 받는 익명 함수를 넣으면 된다. 아래는 GridView.builder의 공식문서니 참고한다.

 

https://api.flutter.dev/flutter/widgets/GridView/GridView.builder.html

 

GridView.builder constructor - GridView - widgets library - Dart API

GridView.builder({ Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollController? controller, bool? primary, ScrollPhysics? physics, bool shrinkWrap = false, EdgeInsetsGeometry? padding, required SliverGridDelegate gridDelegate, re

api.flutter.dev

필요에 따라 생성되는 스크롤 가능한 2차원 위젯 배열을 만든다. 이 생성자는 실제 표시되는 자식에 대해서만 빌더가 호출되므로 자식 수가 많은(또는 무한한) 그리드뷰에 적합하다. itemCount를 0이 아닌 값으로 넘기면 그리드뷰가 최대 스크롤 범위를 추정하는 기능이 향상된다
itemBuilder는 0보다 크거나 같고 itemCount보다 작은 인덱스에서만 호출된다. itemBuilder가 null을 리턴해도 작동할 수 있다. 이 때 스크롤뷰는 itemCount에 도달하지 않았어도 itemBuilder 호출을 중지한다. null을 리턴하면 유저가 스크롤뷰의 끝에 도달하지 않는 한 ScrollPosition.maxScrollExtent는 정확하지 않다. 또한 스크롤 시 스크롤바가 커질 수 있다. 정확한 ScrollMetrics를 위해 itemCount를 지정하는 게 좋다...(중략)...null이면 자식 빌더에서 리턴된 자식 순서가 바뀔 때 자식 위젯이 기존 렌더 오브젝트에 매핑되지 않을 수 있다. 이로 인해 상태가 손실될 수 있다. gridDelegate 매개변수는 필수다...(중략)
GridView.builder({
  super.key,
  super.scrollDirection,
  super.reverse,
  super.controller,
  super.primary,
  super.physics,
  super.shrinkWrap,
  super.padding,
  required this.gridDelegate,
  required NullableIndexedWidgetBuilder itemBuilder,
  ChildIndexGetter? findChildIndexCallback,
  int? itemCount,
  bool addAutomaticKeepAlives = true,
  bool addRepaintBoundaries = true,
  bool addSemanticIndexes = true,
  super.cacheExtent,
  int? semanticChildCount,
  super.dragStartBehavior,
  super.keyboardDismissBehavior,
  super.restorationId,
  super.clipBehavior,
  super.hitTestBehavior,
}) : childrenDelegate = SliverChildBuilderDelegate(
       itemBuilder,
       findChildIndexCallback: findChildIndexCallback,
       childCount: itemCount,
       addAutomaticKeepAlives: addAutomaticKeepAlives,
       addRepaintBoundaries: addRepaintBoundaries,
       addSemanticIndexes: addSemanticIndexes,
     ),
     super(
       semanticChildCount: semanticChildCount ?? itemCount,
     );

 

GridView.builder도 이래저래 사용해 보면서 어떻게 작동하는지 알면 될 것이다.

반응형