{% seo %}layout | myHobbyBest
layout
포스트
취소

layout

latout 만들기

Flutter의 레이아웃 메커니즘이 동작하는 방식을 이해하고 UI를 설계하는 방법을 연습해보자.

화면 쪼개기

구현하려는 목표 화면

이와 같은 형태의 화면을 구현하고자 한다고 가정해보자

  • 화면의 구성요소에서 Column이나 Row를 추출해보자.
  • Grid구성요소가 있는지 확인하자.
  • 겹쳐진 구성요소는 없는가?
  • 탭을 필요로하는가?
  • alignment, padding 또는 borders 같은 부분이 필요한지 살피자

우선 화면을 큰 구성요소로 쪼개보면 아래와 같은 레이아웃을 생각 할 수 있다. 즉 4개의 하위 요소를 가지는 하나의 Column으로 구성된다. 그 하위 요소들은 각각 하나의 이미지 요소, 두개의 Row 요소 그리고 하나의 Text요소로 볼 수 있다.

화면 레이아웃

  • 하위요소로 잘게 쪼개기 : 첫번쨰 row 는 (상부의 이미지에 대한 타이틀을 기록한 것으로 보이므로 임의로 title 영역이라고 부르자) 아래와 같이 세개의 하위 요소로 쪼갤 수 있다. 이떄 그 첫번쨰 하위 요소는 다시 두개의 하위 text 요소로 구성된 Column 요소로 볼 수 있고 그 다음은 Icon요소 와 Text요소 (숫자 요소)로 구성된다. Column 요소는 우측에 많은 여백을 가지므로 이를 구현할 수있는 Expaned 위젯을 사용해야 한다.

Title section

두번째 row는 (Button section) 세개의 하위 요소로 쪼갤 수 있으며 이들은 모두 두개의 하위 요소를 가지는 Column요소로 볼 수 있다. 세개의 Column요소들은 모두 상부에 Icon요소 하부에 Text 요소를 가지고 있다.

Button section

flutter 기본앱 코드작성하기

코드를 구현하기에 앞서 일단 IDE (VS CODE)를 이용해 flutter 기본앱 코드를 입력한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter layout demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter layout demo'),
        ),
        body: const Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

column 위젯 구현

body 위치의 Center 위젯 대신에 아래의 Column위젯을 구현하여 대체한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
Column(            
    children: [        
        Image.asset(  // imageSection     
            'images/lake.jpg',            
            width: 600,            
            height: 240,            
            fit: BoxFit.cover,            
        ),    
        titleSection,  
        buttonSection,     
        textSection,     
    ],
)

이제 하위 요소인 네개의 위젯 만 (imageSection, titleSection, buttonSection, textSection) 구체적으로 구현해 주면 된다. imageSection은 예제와 같이 이미 정의된 Image 위젯을 이용해 구현할 수 있다. 다만 .asset이 구현되기 위해서는 프로젝트 폴더의 최상위 디렉토리에 images 폴더를 생성한 후에 여기에 lake.jpg 이미지를 다운 받아 카피해 넣어준다. pubspec.yaml 파일을 열어서 flutter 위치에 아래와 같이 입력해준다.

1
2
3
4
    flutter:            
      uses-material-design: true            
      assets:            
        - images/lake.jpg

fit:BoxFit.cover 는 가능한 작게, 하지만 렌더링 박스 전체를 덮을 정도로만 작아야 한다는 것을 프레임워크에 알려준다.

titleSection 위젯 구현

아래와 같은 코드를 Build 위젯의 가장 윗 부분에 넣어 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Widget titleSection = Container(
  padding: const EdgeInsets.all(32),
  child: Row(
    children: [
      Expanded(
        /*1*/
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            /*2*/
            Container(
              padding: const EdgeInsets.only(bottom: 8),
              child: Text(
                'Oeschinen Lake Campground',
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            Text(
              'Kandersteg, Switzerland',
              style: TextStyle(
                color: Colors.grey[500],
              ),
            ),
          ],
        ),
      ),
      /*3*/
      Icon(
        Icons.star,
        color: Colors.red[500],
      ),
      Text('41'),
    ],
  ),
);

앞에서 레이아웃을 설정할 때 언급했던 것 같이 전체 Row는 Container안에 들어 있고, 사방으로 32 pixel의 패딩이 설치된다. Row 위젯이 하위 요소로 구성된다.

Row 위젯 내부에는 ExpandedIcon, Text위젯을 설치한다.

  • /* 1 */ Expanded 위젯의 width는 다른 요소들이 차지하고 남는 공간을 모두 점유하는 특징을 가진다. 따라서 그 안에 Column을 넣고 crossAxisAlignment propertyCrossAxisAlignment.start로 설정하면 Column이 Row의 시작 위치에 배치되고 Row의 우측에 다른 요소가 차지하고 남은 공간까지를 Expanded 위젯이 점유하므로 그 공간은 빈칸으로 채워진다.

  • /* 2 */ 첫 번째 Row의 텍스트를 Container로 감싸주면 그 내부에 padding을 설정할 수 있다. Column의 두 번째 하위요소는 글자색을 gray[500]으로 설정했다.

  • /* 3 */ Icon은 star하위요소로 정의하고 색상은 Color의 하위요소를 red[500] 선택한여 설정한다. 우측의 Text에는 숫자 41을 넣는다.

bottonSection 위젯 구현

버튼 영역은 모두 동일하게 텍스트 위에 아이콘을 배치한 column 레이아웃 3개를 담고 있다. 이 Row의 Column은 모두 동일한 영역을 차지한다. 또한 텍스트와 아이콘은 기본 색으로 그려진다.

각 컬럼의 코드가 거의 동일하기 때문에, 색상과, 아이콘, 그리고 텍스트를 인수로 받아 주어진 색으로 칠한 위젯을 반환하는 _buildButtonColumn() 이라는 private 헬퍼 메소드를 생성하는 것이 코드를 간략하게 하는데 도움이 된다.(변수명 앞에 underbar(_)는 private 변수임을 나타낸다) 메소드의 코드는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 Column _buildButtonColumn(Color color, IconData icon, String label) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, color: color),
        Container(
          margin: const EdgeInsets.only(top: 8),
          child: Text(
            label,
            style: TextStyle(
              fontSize: 12,
              fontWeight: FontWeight.w400,
              color: color,
            ),
          ),
        ),
      ],
    );
  }

이 함수는 아이콘을 Column 속에 직접 넣는다. 텍스트는 아이콘과 간격을 주기위해 상단 마진이 필요하므로 Container 위젯 속에 넣는다.

이제 이 함수를 호출해서 버튼섹션을 구성해보자.

1
2
3
4
5
6
7
8
9
10
11
12
Color themeColor = Theme.of(context).primaryColor;

Widget buttonSection = Container(
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      _buildButtonColumn(themeColor, Icons.call, 'CALL'),
      _buildButtonColumn(themeColor, Icons.near_me, 'ROUTE'),
      _buildButtonColumn(themeColor, Icons.share, 'SHARE'),
    ],
  ),
);

  • Theme 위젯

테마위젯은 응용 프로그램의 색상과 활자 선택에 대해 서술한다. 하위 위젯으로 [Theme.of]를 사용하여 현재 테마의 [ThemeData] 개체를 가져온다. 이렇게 하면 추후 테마가 변경되었을 때 자동으로 재 구축되어 그 변경 사항이 적용될 수 있도록 한다.

[Theme] 위젯은 [Theme]에 대한 [data]의 [ThemeData.iconTheme] 값으로 설정된 [IconTheme] 위젯을 의미한다.


이 함수에 해당 column을 위한 색상, Icon 종류와 그 이름을 함께 호출해서 Row를 작성한다. main axis의 정렬은 column들이 모두 동일한 영역을 차지하도록 MainAxisAlignment.spaceEvenly를 사용한다.

Text Section 작성

textSection은 사면에 적당한 크기의 여백을 주는 것, 텍스트가 폭방향으로 채워지면 자동으로 줄 바꿈하는 기능 이외에는 다른 처리가 필요없다. Container안에 32픽셀 크기의 padding을 넣고 Text 위젯 안에 필요한 텍스트를 넣어주는 것으로 끝이다. 하기의 코드를 bottonSection위젯 아래에 넣어준다.

1
2
3
4
5
6
7
8
9
10
11
12
Widget textSection = Container(
      padding: const EdgeInsets.all(32),
      child: const Text(
        'Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese '
        'Alps. Situated 1,578 meters above sea level, it is one of the '
        'larger Alpine Lakes. A gondola ride from Kandersteg, followed by a '
        'half-hour walk through pastures and pine forest, leads you to the '
        'lake, which warms to 20 degrees Celsius in the summer. Activities '
        'enjoyed here include rowing, and riding the summer toboggan run.',
        softWrap: true,
      ),
    );

여기서 softWrap: true 는 글이 column 끝까지 채워지면 자동으로 줄바꿈하는 역할을 한다.

가상 디바이스에서 App 실행 해보기

Selct a Device to use

가상 디바이스는 Android Studio에서 설치해야 한다. 여러 다른 자료들을 참조하여 적당한 device를 설치한다. VS CODE의 경우 아래 그림처럼 화면 우측하단에 Windows(windows-x64)라고 표시된 곳을 마우스 클릭하면 상단에 작은 device 입력창이 나타나는데 여기서 이미 설치한 device를 선택한다.(만일 우측하단에 No Device 라고 뜨면 Ctrl + Shift + p키를 누르고 상단에 나온 입력창에 Flutter:Launch Emulatot 또는 Flutter:Select Device라고 입력하면 디바이스 선택창이 나온다.)

flutter_application_1

디바이스를 선택하면 화면에

  1. 스마트폰 형상의 에뮬레이터가 나타나고 우측하단의 Windows(windows-x64) 표시가 선택한 디바이스 명으로 변경된다.
  2. 좌측 하단에 프로젝트 폴더명flutter_application_1이 보이는데 그 옆에 (release mode) 또는 (profile mode)라고 쓰여 있다면
  3. 좌측 상단 삼각형 run 단추 옆의 아래 꺽쇠를 눌러 flutter_application_1 로 선택해준다. 이후에 2.3.4.번 중 어느하나의 삼각형 런단추를 누르면 프로그램이 진행된다

Launching main.dart 크로그램이 진행됨에 따라서 콘솔창에 순차적으로 그림과 같은 메시지가 나타나고 디바이스 창에 목표하는 화면이 얻어진것을 볼 수 있다.

대개의 경우 이 상태에서 코드를 변경한 후에 이를 저장(Ctrl + s)하면 변경된 내용이 Hot reload 되면서 자동으로 반영된다.

ListView 위젯

만일 보다 작은 디바이스에서 스크롤 방식으로 화면작동이 가능하도록 하고 싶다면 body에서 사용한 Column 위젯을 ListView로 바꿔주면 된다.

column위젯 사용ListView 위젯 사용
column위젯 사용ListView 위젯 사용

만일 화면의 크기가 필요한 높이보다 작은 경우 column위젯은 왼쪽 그림처럼 에러가 발생하지만 ListView 위젯을 사용하는 경우 우측 그림에서 처럼 모자라는 영역은 아래쪽에 감춰지면서 스크롤 바가 표시되고 스크롤바를 움직이면 아래쪽을 볼 수 있게 된다.

참고자료

https://docs.flutter.dev/ 중에서- 레이아웃 만들기- 튜토리얼

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

git & github

-

Comments powered by Disqus.