5단계 기본 위젯 알아보기

송기영·2023년 12월 4일
0

플러터

목록 보기
7/25

5.1. 위젯 소개

플러터는 화면에 그려지는 모든 요소가 위젯으로 구성되어 있다. 플러터 프레임워크에서 기본적으로 수십가지의 위젯을 제공하며 앱 개발자가 직접 위젯을 만들 수도 있다.

위젯은 자식을 하나만 갖는 위젝과 자식을 여러개 갖는 위젯으로 나뉜다.

하나만 갖는 대표적인 위젯

  • Container : 자식을 담는 컨테이너 역할을 하며, 배경색, 너비와 높이, 테두리 등의 디자인을 지정할 수 있다.
  • GestureDetector : 플러터에서 제공하는 제스처 기능을 자식 위젯에서 인식하는 위젯으로 탭이나 드래그 그리고 더블 클릭 같은 제스처 기능이 자식 위젯에 인식됐을 때 함수를 실행할 수 있다.
  • SizedBox : 높이와 너비를 지정하는 위젯이며, Conatiner와는 다르게 디자인적 요소는 적용할 수 없지만 const 생성자로 선언할 수 있어서 퍼포먼스 측면에서 더 효율적이다.

다수의 자식을 가지는 위젯

  • Column : children 매개변수에 입력된 모든 위젯들을 세로로 배치한다.
  • Row : childrend 매개 변수에 입력된 모든 위젯들을 가로로 배치한다.
  • ListView : 리스트를 구현할 때 사용하며, 입력된 위젯이 화면을 벗어나게 되면 스크롤이 가능해진다.

5.1.1. Children와 Child 차이점

플러트는 위젯 아래에서 계속 위젯이 입력되는 형태로 **위젯 트리**를 구성하여 UI를 제작한다.

이름처럼 children은 여러개의 위젯을 추가, child는 하나의 위젯만 추가한다.

child와 children을 동시에 입력받는 위젯은 존재하지 않는다.

  • Child
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext contest) {
    return (MaterialApp(
      home: Scaffold(
        body: (Center(
					// 하나의 위젯만 가운데 정렬 가능
          child: Text("Code Fatory"),
        )),
      ),
    ));
  }
}
  • Children
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext contest) {
    return (MaterialApp(
      home: Scaffold(
          body: SizedBox(
        width: double.infinity,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
					// 여러 위젯 입력가능
          children: [Text("Code"), Text("Factory")],
        ),
      )),
    ));
  }
}

5.2. 텍스트 관련 위젯

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
        home: Scaffold(
            body: Center(
      child: Text(
        // 작성하고 싶은글
        "아무거나",
        // 글자에 스타일 적용
        style: TextStyle(
            // 글자크기
            fontSize: 16.0,
            // 글자굵기
            fontWeight: FontWeight.w700,
            // 글자 색상
            color: Colors.brown),
      ),
    ))));
  }
}

5.3. 제스처 관련 위젯

사용자가 키보드로 글자를 입력하는 행위 외의 모든 입력을 제스처라고 칭한다. 화면을 탭, 길게 누르는 등

제스처 관련 위젯은 하위 위젯에 탭이나 드래그처럼 특정 제스처가 입력됐을 때 인지하고 콜백할 수를 실행한다.

5.3.1. Button 위젯

  • TextButton : 텍스트만 있는 버튼
  • OutlinedButton : 테두리가 있는 버튼
  • ElevatedButton : 입체적으로 튀어나온 느낌의 배경이 들어간 버튼
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextButton(
                onPressed: () => {},
                style: TextButton.styleFrom(foregroundColor: Colors.red),
                child: Text("텍스트 버튼")),
            OutlinedButton(
                onPressed: () => {},
                style: OutlinedButton.styleFrom(foregroundColor: Colors.red),
                child: Text("테두리 버튼")),
            ElevatedButton(
                onPressed: () => {},
                style: ElevatedButton.styleFrom(foregroundColor: Colors.red),
                child: Text("입체 버튼"))
          ],
        ),
      )),
    ));
  }
}

위의 코드를 입력하면 아래와 같은 화면이 나온다.

코드를 작성하다가 오타가 실수로 버튼에 스타일 모두 TextButton으로 스타일을 주게되었는데 정상적으로 적용되는 상황이 생겼다.

그래서 찾아봤더니 TextButton, OutlinedButton, ElevatedButton의 반환값이 모두 ButtonStyle 객체로 반환되기 때문에 상관없다고 한다.

따라서 다음과 같이 입력해도 동일한 결과 값을 얻을 수 있다.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextButton(
                onPressed: () => {},
                style: ButtonStyle(
                    foregroundColor: MaterialStatePropertyAll(Colors.red)),
                child: Text("텍스트 버튼")),
            OutlinedButton(
                onPressed: () => {},
                style: ButtonStyle(
                    foregroundColor: MaterialStatePropertyAll(Colors.red)),
                child: Text("테두리 버튼")),
            ElevatedButton(
                onPressed: () => {},
                style: ButtonStyle(
                    foregroundColor: MaterialStatePropertyAll(Colors.red)),
                child: Text("입체 버튼"))
          ],
        ),
      )),
    ));
  }
}

5.3.2. IconButton 위젯

아이콘을 버튼으로 생성하는 위젯으로, Icons 클래스를 이용해 플러터에서 제공하는 기본 아이콘들을 사용할 수 있다.

아이콘 목록은 다음 링크에서 확인 가능하다.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [IconButton(onPressed: () => {}, icon: Icon(Icons.home))],
        ),
      )),
    ));
  }
}

5.3.3. GestureDetector 위젯

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GestureDetector(
              onTap: () => print("한번 탭"),
              onDoubleTap: () => print("두번 탭"),
              onLongPress: () => print("길게 탭"),
              child: Container(
                  decoration: BoxDecoration(color: Colors.red),
                  width: 100.0,
                  height: 100.0),
            )
          ],
        ),
      )),
    ));
  }
}

다음과 같은 화면을 한번, 두번, 길게 탭할 경우 다음과 같은 결과를 얻을 수 있다.

Untitled

Untitled

GestureDetector 위젯에서 제공하는 제스처 매개변수

매개변수설명
onTap한 번 탭했을 때 실행 하는 함수
onDoubleTap두번 연속으로 탭했을 때 실행되는 함수
onLongPress길게 누르기가 인식됐을 때 실행되는 함수
onPanStart수평 또는 수직으로 드래그가 시작됐을 때 실행되는 함수
onPanUpdate수평 또는 수직으로 드래그를 하는 동안 드래그 위치가 업데이트 될 때마다 실행되는 함수
onPanEnd수평 또는 수직으로 드래그가 끝났을때 실행되는 함수
onHorizontalDragStart수평으로 드래그를 시작했을때 실행되는 함수
onHorizontalDragUpdate수평으로 드래그하는 동안 드래그 위치가 업데이트 될 때마다 실행되는 함수
onHorizontalDargEnd수평으로 드래그가 끝났을때 실행되는 함수
onVerticalDragStart수직으로 드래그를 시작했을때 실행되는 함수
onVerticalDragUpdate수직으로 드래그하는 동안 드래그 위치가 업데이트 될 때마다 실행되는 함수
onVerticalDragEnd수직으로 드래그가 끝났을때 실행되는 함수
onScaleStart확대가 시작됐을 때 실행되는 함수
onScaleUpdate확대가 진행되는 동안 확대가 업데이트될 때마다 실행되는 함수
onScaleEnd확대가 끝났을 때 실행되는 함수

5.3.4. FloatingActionButton 위젯

앱 오른쪽하단 문의하기 버튼과 같은 버튼을 쉽게 구현할 수 있는 위젯

import 'package:flutter/material.dart';

void main() {
  runApp(FloatingActionButtonExample());
}

class FloatingActionButtonExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () => {},
          child: Text("클릭"),
        ),
        body: Container(),
      ),
    ));
  }
}

5.4. 디자인 관련 위젯

5.4.1. Container 위젯

Container 위젯은 말 그대로 다른 위젯을 담는 데 사용된다. 위젯의 너비높이를 지정하거나, 배경, 테두리를 추가할 때 많이 사용된다.

import 'package:flutter/material.dart';

void main() {
  runApp(FloatingActionButtonExample());
}

class FloatingActionButtonExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: Container(
          decoration: BoxDecoration(
              color: Colors.red,
              border: Border.all(width: 16.0, color: Colors.redAccent),
              borderRadius: BorderRadius.circular(16.0)),
          width: 200.0,
          height: 200.0,
        ),
      ),
    ));
  }
}

5.4.2. SizedBox 위젯

일정 크기의 공간을 공백으로 두고 싶을 때 사용된다. Container을 사용해도 가능하지만 SizedBox는 const 생성자를 사용했을때 퍼포먼스에서 이점을 얻을 수 있다.

import 'package:flutter/material.dart';

void main() {
  runApp(FloatingActionButtonExample());
}

class FloatingActionButtonExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: SizedBox(
        height: 200.0,
        width: 200.0,
        child: Container(
          color: Colors.red,
        ),
      )),
    ));
  }
}

5.4.3. Padding 위젯

child위젯에 여백을 제공할 때 사용한다. 상위 위젯과 하위 위젯 사이의 여백을 줄때 사용된다.

import 'package:flutter/material.dart';

void main() {
  runApp(FloatingActionButtonExample());
}

class FloatingActionButtonExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
        home: Scaffold(
            body: Container(
      color: Colors.blue,
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: Container(
          color: Colors.red,
          width: 50.0,
          height: 50.0,
        ),
      ),
    ))));
  }
}

패딩은 적용된 위젯이 차지하는 크기 내부에서 간격이 추가된다. 마진도 존재하며 마진은 Container에서 지정해주어야 한다.

다음은 패딩과 마진이 동시에 적용된 예제이다.

import "package:flutter/material.dart";

void main() {
  runApp(MarginApp());
}

class MarginApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: Container(
            color: Colors.amber,
            child: Container(
                color: Colors.blue,
                margin: EdgeInsets.all(16.0),
                child: Padding(
                  padding: EdgeInsets.all(16.0),
                  child: Container(
                    color: Colors.red,
                    width: 50.0,
                    height: 50.0,
                  ),
                ))),
      ),
    ));
  }
}

EdgeInsets 클래스 생성자

생성자설명
EdgeInsets.all(16.0)상하, 좌우로 매개변수에 입력된 패딩을 균등하게 적용한다.
EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0)가로와 세로 패딩을 따로 적용한다.
EdgeInsets.only(top: 16.0, bottom: 16.0, left: 16.0, right: 16.0)상하좌우 패딩을 각각 적용한다.
EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 16.0)상하좌우 패딩을 각각 적용한다.

5.4.4. SafeArea

스마트폰 디스플레이에 따라 가리면 안되는부분들이 있기때문에 존재하는 위젯이다. 예로 상단 배터리 표시, 시계표시와 같은 부분을 가리면 안되기 때문이다.

import "package:flutter/material.dart";

void main() {
  runApp(MarginApp());
}

class MarginApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
          body: SafeArea(
        top: true,
        bottom: true,
        left: true,
        right: true,
        child: Container(
          color: Colors.red,
          height: 300.0,
          width: 300.0,
        ),
      )),
    ));
  }
}

5.5. 배치관련 위젯

하위 위젯을 가로 또는 세로로 배치하거나 위젯 위에 위젯을 겹칠 때 사용한다.

5.5.1. Row 위젯

Column 위젯과 함께 가로 세로로 배치하는데 사용된다. Row는 말 그대로 가로로 위젯을 배치하는데 사용된다. 주축의 개념이 있으므로 Row는 가로 주축 세로 반대축이 된다.

import "package:flutter/material.dart";

void main() {
  runApp(RowWidgetExample());
}

class RowWidgetExample extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: SizedBox(
            height: double.infinity,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Container(
                  height: 50.0,
                  width: 50.0,
                  color: Colors.red,
                ),
                const SizedBox(
                  width: 12.0,
                ),
                Container(
                  height: 50.0,
                  width: 50.0,
                  color: Colors.green,
                ),
                const SizedBox(
                  width: 12.0,
                ),
                Container(
                  height: 50.0,
                  width: 50.0,
                  color: Colors.blue,
                ),
              ],
            )),
      ),
    ));
  }
}

5.5.2. Column 위젯

Column는 세로 주축 가로 반대축이 된다.

import "package:flutter/material.dart";

void main() {
  runApp(ColumnWidgetExample());
}

class ColumnWidgetExample extends StatelessWidget {
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: SizedBox(
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Container(
                width: 50.0,
                height: 50.0,
                color: Colors.red,
              ),
              const SizedBox(
                height: 16.0,
              ),
              Container(
                width: 50.0,
                height: 50.0,
                color: Colors.blue,
              ),
              const SizedBox(
                height: 16.0,
              ),
              Container(width: 50.0, height: 50.0, color: Colors.black)
            ],
          ),
        ),
      ),
    ));
  }
}

5.5.3. Flexible 위젯

Row나 Column에서 사용하는 위젯이다. Flexible 위젯을 사용하면 child가 크기를 최소한으로 차지하게 할 수 있다.

import "package:flutter/material.dart";

void main() {
  runApp(ColumnWidgetExample());
}

class ColumnWidgetExample extends StatelessWidget {
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: SizedBox(
          width: double.infinity,
          child: Column(
            children: [
              Flexible(flex: 1, child: Container(color: Colors.blue)),
              Flexible(flex: 1, child: Container(color: Colors.red))
            ],
          ),
        ),
      ),
    ));
  }
}

5.5.4. Expanded 위젯

Flexible 위젯을 상속하는 위젯이다. Expaned위젯을 사용하면 남은 공간을 최대한으로 차지한다.

import "package:flutter/material.dart";

void main() {
  runApp(ColumnWidgetExample());
}

class ColumnWidgetExample extends StatelessWidget {
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: SizedBox(
          width: double.infinity,
          child: Column(
            children: [
              Expanded(child: Container(color: Colors.blue)),
              Expanded(child: Container(color: Colors.red))
            ],
          ),
        ),
      ),
    ));
  }
}

5.5.5. Stack 위젯

위젯을 겹치는 기능을 제공해준다. children에 위치한 순서대로 위젯이 겹쳐진다.

import "package:flutter/material.dart";

void main() {
  runApp(ColumnWidgetExample());
}

class ColumnWidgetExample extends StatelessWidget {
  Widget build(BuildContext context) {
    return (MaterialApp(
      home: Scaffold(
        body: SizedBox(
          width: double.infinity,
          child: Stack(
            children: [
              Container(
                width: 300,
                height: 300,
                color: Colors.red,
              ),
              Container(width: 250, height: 250, color: Colors.black)
            ],
          ),
        ),
      ),
    ));
  }
}

위젯이라는 개념이 아직 조금 익숙하지 않지만 점점 익숙해지고 있는거 같다... 머릿속에서 잘 안그려지기는 하는데 어느 정도는 그려진듯하다.

profile
업무하면서 쌓인 노하우를 정리하는 블로그🚀 풀스택 개발자를 지향하고 있습니다👻

0개의 댓글