2020. 11. 25. 07:38ㆍIT
Layout
지금까지 잘 따라왔고, 여기까지 도달을 하셨다면 이해가 안가는 부분이 꽤 많으셨을 것입니다. 자세한 설명도 없이 동작하는 앱을 다 따라서 왔기 때문에 얻은 것도 있겠지만 어렵다는 생각을 많이 했을 것입니다. 다시 기초로 돌아와 레이아웃을 살펴보면서 조금 더 플러터를 이해해 보도록 하겠습니다.
플러터에서 앱의 레이아웃을 결정짓는 것은 위젯들입니다. 플러터의 모든 것은 위젯이라고 해도 틀린말은 아닐 것입니다.
위젯들은 UI를 만드는데 사용되는 클래스이며, UI 요소와 레이아웃을 구성되는데 사용이 되고 간단한 위젯들이 모여서 복잡한 위젯들을 만들기도 합니다.
플러터로 만든 앱들에서 볼 수 있는 이미지들, 아이콘들 그리고 텍스트들은 모두 위젯입니다. 또한 눈에 보이지 않는 위젯들도 있습니다. 행, 열, 그리드, 배열 그리고 align과 같이 위젯들을 배열하는데 사용되는 것들 역시 위젯입니다.
이번에 설명드릴 layout은 다음의 링크를 기반으로 설명을 드립니다.
https://flutter.dev/docs/development/ui/layout
레이아웃을 하는 것과 위젯의 사용방법을 아는 것이 플러터를 배우는 전부라고 해도 될 것 같습니다.
레이아웃 트리의 기초
앱을 만들때에는 위젯들을 다양하게 구성을 해서 복잡한 위젯들을 만들 수 있습니다. 예를 들면 다음의 첫번째 이미지는 3개의 아이콘과 아이콘에 대응하는 레이블을 아이콘 아래에 보여주고 있습니.다
두번째 이미지는 각 UI 구성요소의 레이아웃을 비주얼하게 보여주고 있습니다.
이것을 위젯트리로 그리면 다음과 같습니다. 전에 하나씩 아래에서 쌓아 올리는 UI 레이아웃에 대해서도 설명을 드렸던 것과 같은데 다만 맨 바닥에 있는 UI 요소들을 위에서 부터 그렸다고 보시면 됩니다.
앞의 그림에서 눈에 보이지 않는 위젯이 컨테이너 위젯으로 핑크 색으로 표현된 것이 있습니다. 컨테이너 위젯은 그 안에 Child 위젯을 담을 수 있고 배경색을 넣을 수도 있다고 설명을 드린적이 있습니다. 이 컨테이너는 배경색 이외에도 padding, margin, border 등의 속성을 변경할 수 있습니다.
앞의 예에서 컨테이너 위젯은 텍스트의 margin을 주기 위해서 사용이 되었습니다. 또한 모든 아이콘과 텍스트들을 포함하는 열(Row) 역시 컨테이너 안에 들어있습니다. 맨 위에 표시된 핑크색 컨테이너를 말씀드리는 것입니다. 이는 열의 padding을 조정하기 위해서 사용이 되었습니다.
다른 UI, 즉 위젯의 구성은 각 위젯들의 속성을 설정함으로써 구성이 됩니다. 예를 들자면 아이콘의 컬러는 color 속성을 사용할 것이고 문자의 크기는 Text 위젯의 style 속성을 사용할 것입니다.
여기에서는 하나의 위젯을 어떻게 화면에 배치할 수 있는지를 알아보도록 하겠습니다.
Center 위젯
위젯들을 배치하는데 있어서 가장 기본이 되는 눈에 보이지 않는 위젯으로 Center 위젯이 있습니다. 레이아웃 위젯이라고도 부릅니다. Center 이외에도 Container, Row, Column, ListView, Stack 등이 있습니다. 여기서 살펴볼 Center 위젯은 가로 세로 모두 중간에 Child 위젯을 배치합니다.
Text(‘Hello World’), |
앞의 코드는 Hello World라는 문자를 화면에 보여주는 코드입니다.
Image.asset( ‘image/lake.jpg’, fit:BoxFit.cover ), Icon( Icons.star, color: Colors.red[500], ), |
앞의 코드는 이미지와 Icons.star라는 아이콘을 보여주는 코드입니다.
레이아웃 위젯은 하나의 Child를 갖는 Center나 Container 위젯이 있고, 여러 개의 위젯을 Children으로 갖는 Row, Column, ListView, Stack 등이 있습니다. 앞에서 ‘Hello World’를 출력하는 Text 위젯을 Center 위젯을 써서 표현을 하면 다음과 같습니다.
Center( child: Text(‘Hello World’), ), |
플러터 앱 자체를 위젯으로 볼 수 있습니다. 그리고 모든 위젯은 build 함수를 가지고 있죠. build 함수는 위젯을 그리는 곳이라고 볼 수 있습니다. 따라서 우리가 만든 앱의 build 함수에 앱에 포함되는 모든 위젯들을 넣어 화면을 구성을 합니다.
예를 들어 Material design을 사용하는 앱은 Scaffold라는 위젯을 사용할 수 있습니다. 이 위젯은 banner, background color를 지정할 수 있고 drawer, snack bar, bottom sheet 등을 추가할 수 있습니다. 그리고 body 속성에 Center 위젯을 직접 사용할 수 있습니다.
앞의 코드 형태는 몇 번을 봤을텐데 이제는 좀더 이해가 쉽게 되지 않나요?
Column & Row 위젯
플러터로 만드는 앱에서 일반적으로 레이아웃을 만들때 수직 또는 수평으로 위젯들을 비치하는 것입니다. 이때에 사용되는 레이아웃 위젯이 Column과 Row 입니다.
Column과 Row 위젯 안에 위젯들을 배치하기 위해서는 자식 위젯들을 열거해 줘야 합니다. 그리고 그 각각의 자식 위젯들도 column과 row를 가질 수 있습니다. 예를 들어 살펴보도록 하겠습니다.
Row 레이아웃 위젯의 예를 보도록 하겠습니다. 다음은 Row 위젯의 예로 두 개의 자식 위젯을 가지고 있습니다. 왼쪽에는 설명을 오른쪽에는 이미지를 가지고 있습니다.
Row 위젯의 첫번째 구성요소인 좌측의 설명을 나타내는 위젯은 그 안에 Column 위젯을 가지고 있습니다.
그 자식 위젯들 가운데에 다시 Row 위젯을 세 번째와 네번째 위젯이 가지고 있네요.
그리고 다시 네번째 위젯 안에는 Column 위젯이 있고 그 안에 3개의 자식 위젯이 있습니다.
Row와 Column은 가로와 세로로 위젯들을 배치할 수 있는 가장 기본 위젯입니다. 가장 많이 사용되는 위젯이기도 합니다. Row 위젯을 보다 사용하기 편하게 만들어 놓은 ListTile이라는 위젯이 있는데 이는 최대 세줄의 텍스트와 앞과 뒤쪽에 아이콘을 가지고 있으며, 이와 유사하게 ListView는 Column과 같은 형태의 위젯입니다.
위젯 배열하기
Column과 Row 위젯에서 각각의 자식 위젯들을 배열할 때, mainAxisAlignment와 crossAxisAlignment 속성을 사용할 수 있습니다. column 위젯을 예로들면 main 축은 수직이며 cross축은 수평 방향입니다. 다음 그림을 참고하시기 바랍니다.
x 이미지를 위젯에서 사용하기 위해서는 각각의 이미지들을 pubspec.yaml에 등록을 해 줘야 합니다. 각각의 이미지를 사용하기 위해서는 이미지 파일명을 다음과 같이 명시적으로 표시해 주어야 합니다.
flutter: assets: - assets/my_icon.png - assets/background.png |
만일 폴더 내의 이미지 전체를 사용한다고 하면 다음과 같이 폴더를 명시할 수 있습니다. 다만 하위 폴더는 자동으로 등록이되지 않으니 하위 폴더는 별도로 등록을 해 줘야 합니다.
flutter: assets: - directory/ - directory/subdirectory/ |
보다 상세한 사항은 다음의 웹 페이지를 참고하시면 됩니다.
https://flutter.dev/docs/development/ui/assets-and-images
다음의 예는 3개의 이미지를 100 픽셀 너비를 가지고 있고 화면은 300 픽셀 이상의 너비를 가지고 있습니다. 그래서 Row의 main 축을 spaceEvenly로 지정을 했습니다.
그리고 Column 역시 화면 크기가 300 픽셀이 넘기 때문에 동일하게 spaceEvenly로 지정을 했습니다. spaceEvenly는 child 위젯의 간격을 동일하게 만들어줍니다.
Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Image.asset('images/pic1.jpg'), Image.asset('images/pic2.jpg'), Image.asset('images/pic3.jpg'), ], ); Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Image.asset('images/pic1.jpg'), Image.asset('images/pic2.jpg'), Image.asset('images/pic3.jpg'), ], ); |
소스코드는 앞에서 보는 바와 같고, 화면에는 다음과 같이 출력이 됩니다.
위젯의 크기
레이아웃이 너무 커서 다음과 같이 검은색과 노란색 패턴으로 나타나는 것과 같이 보일 경우가 있습니다. 이런 경우에는 Expanded위젯을 사용해서 row 또는 column에 맞게 이미지를 출력할 수 있습니다.
Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Image.asset('images/pic1.jpg'), ), Expanded( child: Image.asset('images/pic2.jpg'), ), Expanded( child: Image.asset('images/pic3.jpg'), ), ], ); |
그런데 이미지 크기와 상관없이 위젯들을 채우고 싶을 때가 있습니다. 이럴 경우에는 Expanded 위젯에 flex 속성을 사용할 수 있습니다. 이 속성은 정수값을 가질 수 있으며 기본값은 1입니다. 다음의 예와 같이 세 개의 Image 위젯을 갖고 있는 Expanded 위젯 중에서 두 번째 위젯에 flex: 2를 넣어주면 가운데 보이는 이미지만 다른 이미지들의 2배로 보여집니다.
Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Image.asset('images/pic1.jpg'), ), Expanded( flex: 2, child: Image.asset('images/pic2.jpg'), ), Expanded( child: Image.asset('images/pic3.jpg'), ), ], ); |
위젯들 묶기
기본적으로 Row나 Column 위젯은 main 축의 넓이 전체를 사용합니다. 하지만 위젯들을 묶어 가깝게 표현을 하고 싶을 경우가 바로 다음의 별 아이콘들 처럼 rating을 줄 때입니다. 이럴 경우에는 MainAxisSize.min 속성을 사용할 수 있습니다. 이렇게 하면 container의 전체 너비를 사용하는 것이 아니라 최소한의 간격으로 위젯들을 위치시킵니다.
Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.black), Icon(Icons.star, color: Colors.black), ], ) |
중첩 위젯(row & column)
row와 column 위젯 내에 row나 column 위젯을 사용할 수 있으며 그 숫자에는 제한이 없습니다. 다음의 그림에서 보시면 별점을 준 것과 그 아래 요리 시간 등을 표현한 아이콘과 텍스트를 볼 수 있는데 이는 1단계의 좌측 텍스트 부분과 우측 사진, 좌측 텍스가 있는 쪽에서는 다시 column내에 제목과 소개글, 점수와 리뷰 숫자 그리고 그 아랫부분이 여러 번의 column과 row로 나뉘어서 구현되었음을 상상할 수 있을 것입니다.
이 중에서 점수와 리뷰어 숫자가 있는 부분에 대해서 위젯 트리를 그려보면 다음과 같습니다.
그리고 이것을 기준으로 코딩을 한다면 다음과 같이 할 수 있는데 Row에 MainAxisSize.min으로 속성값을 준 후에 Children으로 다섯개의 아이콘을 배치합니다.
그리고 새로운 Row를 만들어 첫번째는 아이콘 묶음을 넣고, 그 다음에 텍스트를 배치시켜 위젯을 완성할 수 있습니다.
var stars = Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.black), Icon(Icons.star, color: Colors.black), ], ); final ratings = Container( padding: EdgeInsets.all(20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ stars, Text( '170 Reviews', style: TextStyle( color: Colors.black, fontWeight: FontWeight.w800, fontFamily: 'Roboto', letterSpacing: 0.5, fontSize: 20, ), ), ], ), ); |
다음은 Rating 아이콘과 리뷰어 숫자 아래에 있는 아이콘과 두 개의 테스트가 함께 있는 3개의 묶음에 대한 내용입니다. 위젯 트리는 다음과 같이 그릴 수 있습니다.
그리고 이 부분을 만들기 위해서는 다음의 코드와 같이 TextStyle을 만들면 세 개의 아이콘 묶음에 동시에 적용할 수 있겠습니다.
다음으로는 컨테이너 안에 Row를 만들고 Row 안에 아이콘과 텍스트 묶음 3개가 들어가게 됩니다. 그리고 각각은 Column으로 만들어 아이콘과 두 개의 텍스트를 넣으면 됩니다.
final descTextStyle = TextStyle( color: Colors.black, fontWeight: FontWeight.w800, fontFamily: 'Roboto', letterSpacing: 0.5, fontSize: 18, height: 2, ); // DefaultTextStyle.merge() allows you to create a default text // style that is inherited by its child and all subsequent children. final iconList = DefaultTextStyle.merge( style: descTextStyle, child: Container( padding: EdgeInsets.all(20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Column( children: [ Icon(Icons.kitchen, color: Colors.green[500]), Text('PREP:'), Text('25 min'), ], ), Column( children: [ Icon(Icons.timer, color: Colors.green[500]), Text('COOK:'), Text('1 hr'), ], ), Column( children: [ Icon(Icons.restaurant, color: Colors.green[500]), Text('FEEDS:'), Text('4-6'), ], ), ], ), ), ); |
이제 앞에서 본 그림에서 왼쪽의 텍스트가 있는 부분을 leftColumn이라고해서 최종적으로 만들어 보면 다음과 같을 것입니다.
컨테이너 안에 column을 넣고 그 안에 제목, 설명, Rating그리고 아이콘 묶음을 넣으면 됩니다. 이 leftColumn은 다시 최상위 Row의 첫번째 요소가 될 것입니다.
final leftColumn = Container( padding: EdgeInsets.fromLTRB(20, 30, 20, 20), child: Column( children: [ titleText, subTitle, ratings, iconList, ], ), ); |
이제 마지막으로 앞에 그림에 나온 UI를 완성하기 위해서는 scaffold의 body에 다음과 같이 코딩을 해 넣을 수 있겠습니다.
그림에 있던 음식이 파블로바라는 후식인데 이 이미지는 Pixabay라는 사이트에서 가지고 왔습니다. 여기에서 이미지를 다운로드 받아서 사용을 할 수도 있고 Image 위젯의 network 함수를 이용해서 직접 불러서 사용을 할 수도 있습니다. 앞서 소개한 바와 같이 로컬에 있는 이미지를 사용하기 위해서는 Images.asset을 사용할 수 있고 pubspec.yaml에 사전에 해당 asset 폴더 또는 이미지를 등록해 줘야 합니다.
그리고 아래의 최종 코드에서 Card라는 위젯이 사용이 됩니다. 이 위젯은 사각형의 각 코너 부분이 라운드 되어 있고, 3D 효과처럼 그림자가 표현이 됩니다. 하지만 이름에서 보는 것과 같이 카드이기 때문에 클릭등의 이벤트를 받지는 못합니다.
body: Center( child: Container( margin: EdgeInsets.fromLTRB(0, 40, 0, 30), height: 600, child: Card( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 440, child: leftColumn, ), mainImage, ], ), ), ), ), |
-
이 예는 레이아웃을 배우기 위한 것으로서 아이폰이나 안드로이드 폰에서 보기 어려워 아이패드와 같은 태블릿에서 동작시키는 것을 추천하고 있습니다. 즉 세로로 보는 디바이스가 아니라 가로로 볼 수 있는 디바이스가 좋다는 말씀입니다.
'IT' 카테고리의 다른 글
내 손으로 만든 제품들 (0) | 2021.03.20 |
---|---|
2002 월드컵과 PDA (0) | 2021.01.06 |
[Flutter #15] gitHub (0) | 2020.11.25 |
[Flutter #14] 새로운 페이지 추가 (0) | 2020.11.25 |
[Flutter #13] 리스트에 아이콘 추가하기 (0) | 2020.11.25 |