인문주의 사피엔스

Flutter에서 AlertDialog에 GridView 포함하기 본문

프로그래밍/Flutter

Flutter에서 AlertDialog에 GridView 포함하기

인문주 2022. 2. 21. 15:59
반응형

Flutter에서 AlertDialog에 GridView를 포함할 때 예상과 다른 결과를 발견하게 됩니다. 그 원인을 분석하고 해결 방법을 살펴보겠습니다.

 

1. GridView in AlertDialog

다음은 AlertDialog의 content에 GridView를 입력하는 소스코드입니다.

 

 

그런데 이 코드를 실행하면 화면에 AlertDialog가 표시되지 않습니다. 대신 디버그 창에 아래와 같은 오류 메시지가 출력됩니다.

 

 

Flutter 문서(https://api.flutter.dev/flutter/material/AlertDialog-class.html)에서 오류의 원인을 유추할 수 있습니다. Flutter 문서는 AlertDialog에 대해 다음과 같이 설명합니다.

 

However, be aware that since AlertDialog tries to size itself using the intrinsic dimensions of its children, widgets such as ListView, GridView, and CustomScrollView, which use lazy viewports, will not work. If this is a problem, consider using Dialog directly.

 

오류 메시지와 문서의 설명을 종합해서 그 의미를 다음과 같이 풀이할 수 있습니다.

 

  1. AlertDialog의 크기를 결정하기 위해 필요한 자식 위젯들의 고유 크기(intrinsic dimensions)를 계산할 수 없는 예외 상황이 발생했다.
  2. ListView, GridView, CustomScrollView 등의 위젯들은 크기를 미리 계산할 수 없다. 왜냐하면 필요한 시점이 될 때까지 자식 위젯들의 생성이 지연(lazy)되기 때문이다.
  3. 이것이 문제가 된다면 Dialog를 직접 사용하라.

 

이렇게 되는 구체적인 원인을 다음과 같이 AlertDialog의 build 함수에서 찾을 수 있습니다.

 

Widget dialogChild = IntrinsicWidth(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: columnChildren,
  ),
);

 

원인은 바로 IntrinsicWidth입니다(IntrinsicWidth가 필요한 이유에 대해서는 별도의 문서로 정리했습니다). Flutter 문서(https://api.flutter.dev/flutter/widgets/IntrinsicWidth-class.html)는 IntrinsicWidth에 대해 다음과 같이 설명합니다.

 

A widget that sizes its child to the child's maximum intrinsic width.
자식 위젯의 크기를 그 자식 위젯의 최대 고유 너비로 만드는 위젯.

 

즉 IntrinsicWidth는 하위 위젯들 가운데 고유 너비가 가장 큰 것을 찾아야 합니다. 그런데 ListView, GridView, CustomScrollView 등의 위젯은 그 특성상 하위 요소들의 고유 너비를 미리 계산할 수가 없습니다. 그런 상황에서 ListView나 GridView를 AlertDialog의 content에 바로 입력했기 때문에 앞에서와 같은 오류 메시지가 출력된 것입니다. 상황은 SimpleDialog의 경우도 마찬가지입니다. SimpleDialog 역시 build 함수에서 IntrinsicWidth를 사용하기 때문입니다.

 

2. GridView in Dialog

다음은 Flutter 문서의 설명대로 Dialog를 직접 사용하는 소스코드입니다.

 

 

다음은 위 코드를 실행한 결과입니다.

 

 

GridView가 성공적으로 표시되었습니다. 하지만 이 경우의 단점은 타이틀이나 버튼도 직접 구현해야 한다는 것입니다. AlertDialog의 타이틀과 버튼을 그대로 활용하려면 content에 GridView를 입력할 수 있는 방법을 찾아야 합니다. 먼저 AlertDialog의 기본 동작부터 살펴보겠습니다.

 

3. AlertDialog

다음은 AlertDialog의 content에 FlutterLogo를 입력하는 소스코드입니다.

 

 

 

다음 그림은 위 코드의 실행 결과입니다.

 

 

이 결과의 의미 역시 AlertDialog의 build 함수에서 확인할 수 있습니다.

 

Widget dialogChild = IntrinsicWidth(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: columnChildren,
  ),
);

 

AlertDialog의 title과 content을 감싸는 Column이 세로축 방향으로 공백 없이 크기가 최소화된 것은 MainAxisSize.min의 효과 때문입니다. title과 content의 너비를 동일하게 만드는 것은 CrossAxisAlignment.stretch와 IntrinsicWidth의 효과 때문입니다. 그리고 만약 너비가 280보다 짧다면 Dialog에 내부에 설정된 제약(constraint)에 의해 280보다 커지도록 늘어나게 됩니다.

 

4. Column in AlertDialog

ListView나 GridView의 경우 고유 너비의 계산이 문제가 되는 반면 Column은 그렇지 않습니다. 다음은 AlertDialog의 content에 Column을 입력하는 소스코드입니다.

 

 

다음 그림은 위 코드의 실행 결과입니다.

 

 

‘scrollable: true’를 적용하면 AlertDialog의 build 함수에서 SingleChildScrollView가 Column를 감싸게 됩니다. 만약 ‘scrollable: true’를 제거하거나 ‘scrollable: false’로 두면 Column의 내용이 많은 경우에 아래과 같이 overflow 오류가 발생됩니다. 따라서 content에 Column을 입력할 때는 ‘scrollable: true’를 설정하거나 SingleChildScrollView를 직접 사용해야 합니다.

 

 

5. Column in SizedBox in AlertDialog

Column에 SizedBox를 적용함으로써 크기를 제한할 수도 있습니다. 그 경우에는 overflow 오류가 생기지 않도록 다음 소스코드에서 처럼 SingleChildScrollView를 사용해야 합니다.

 

 

 

다음은 위 코드의 실행 결과입니다.

 

 

SizedBox에 설정된 너비는 화면 크기에 따라 실제 결과와 차이를 보입니다. 왜냐하면 실제 너비는 Dialog에 설정된 최소 너비(280)나 여러 padding 값의 영향을 받기 때문입니다. 태블릿처럼 충분히 큰 화면이라면 위에 설정된 'width=400'이 ‘최대 너비’의 역할을 하게 됩니다. 

 

6. GridView in SizedBox in AlertDialog

결론적으로 ListView나 GridView를 AlertDialog에 포함하기 위해 필요한 것은 SizedBox로 너비를 지정하는 것입니다. AlertDialog의 build 함수에서 있는 IntrinsicWidth의 입장에서는 자식 위젯인 SizedBox의 고유 너비를 계산할 수 있게 되기 때문입니다. 다음은 SizedBox를 사용하는 소스코드입니다.

 

 

 

다음은 위 코드의 실행 결과입니다.

 

 

5번에서와 마찬가지로 여기서 설정된 SizedBox의 너비도 Dialog의 최소 너비(280)와 padding 값의 영향을 받기 때문에 실제 결과와 차이를 보이게 됩니다.

 

다음은 전체 소스코드입니다.

 

 

 

 

반응형
Comments