본문 바로가기
취미노트/코딩공부

[flutter] 04 화면 구성의 시작 1, 기초 위젯

by 복습쟁이 2024. 8. 15.
반응형

- 이번에 할 거 -

  • 핫 리로드와 핫 리스타트
  • 컨테이너 위젯
  • 컬럼, 로우 위젯
  • 안드로이드스튜디오의 인스펙터라는 도구

 

1. 핫 리로드와 핫 리스타트의 차이

개발은 코드 넣고 화면 갱신해보고 이것의 반복 과정임.

 

[번개버튼] 핫 리로드 - UI적 요소만 제한적 갱신, 빠른 대신 데이터 갱신 없음

[세모버튼] 핫 리스타트 - 대부분 요소를 갱신

 

핫 리로드는 main 함수를 다시 실행시키지 않는다.

이 때문에, 앞선 페이지에서 연습 했던 위 사진의 코드는 핫 리로드가 먹지 않는다.

코드를 메인에서 분리하기 위헤 stateful 또는 stateless 위젯을 사용해야 한다.

 

Statelesswidget : 빠른 속도

Statefulwidget : 다양한 기능

 

위 코드는 Stateless widget으로 변환해볼 예정이다. 이 상황에 쓰기에 가볍기 때문이다.

안드로이드스튜디오에서는 Stateless Widget 틀을 자동으로 만들어준다.

main 함수를 벗어난 라인에서 stl을 입력 후 엔터를 치면 아래와 같이 틀이 생성된다.

 

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

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

 

여기에 이름을 주면 된다. 클래스 이름을 줄 때는 첫글자는 대문자로 줘야 한다. 이름을 MyApp으로 줘 보자.

 

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

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

 

이제 메인함수 안에 있던 MaterialApp을 빌드 메소드 안쪽에 넣어주면 메인함수에서 분리가 끝이 난다.

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.amberAccent,
        appBar : AppBar(
          title: Text('Strong Guy'),
        ),
        body: Center(
          child: Image(
            image: AssetImage('assets/images/strong_guy.jpg'),
          ),
        ),
      ),
    );
  }
}

 

위젯에서 return 뒤로 있는 내용에 MaterialApp을 넣어주면 된다. 괄호 수를 적정하게 수정해줘야 에러가 안 뜬다.

이제 메인함수에서 비어버린 runApp의 자리에는 Myapp을 넣어놓으면 된다.

 

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.amberAccent,
        appBar : AppBar(
          title: Text('Strong Guy'),
        ),
        body: Center(
          child: Image(
            image: AssetImage('assets/images/strong_guy.jpg'),
          ),
        ),
      ),
    );
  }
}

 

이것이 리팩토링 된 코드이다. 앞서 만든 것과 기능은 동일하지만 스테이트레스 위젯으로 분리된 것이다. 

 

 

핫 리로드가 잘 먹히는지 테스트하기 위해서, 최초로 리팩토리된 앱을 런을 해 준 뒤에

백그라운드 색상을 amberAccent에서 black으로 바꿔봤더니, 코드를 저장만 하면 새로 런을 하지 않아도 앱 배경색이 검은색으로 바로 바뀌었다. UI적인 부분이기 때문에 핫 리로드 된 것이다.

한번 더 테스트를 위해 appBar에도 backgroundColor를 Grey로 주었더니 동일하게 잘 변경되었다.

 

 

 

2. 컨테이너 위젯

Container Widget은 사각형의 투명한 박스로 화면 구성에서 많이 사용되는 가장 기초적인 위젯이다. 

이 사각형의 위젯은 여러 영역을 갖고 있다.

margin - Border 기준 바깥쪽 여백 공간. 컨테이너의 크기에 포함되지 않는 영역

border - 컨테이너의 경계를 나타내는 모서리. 컨테이너의 크기에 포함되기 시작하는 영역

padding - Border 기준 안쪽 여백 공간

contents - 내용이 들어가는 공간

 

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(color: Colors.yellow),
      ),
    );
  }
}

 

컨테이너 실습을 위해, 코드를 작성했다.

메인 함수 아래에 stl을 쳐서 StatelessWidget을 불러온 뒤, 이름을 MyContainer로 주었다.

그 뒤에 앱의 기본인 MaterialApp으로 입력후, Scaffold로 페이지를 구성했다.

appBar는 따로 하지 않고, body에서 노란 배경만 만들었다.

그리고 메인함수의 runApp을 처음에 만들었던 MyApp에서 이번에 만든 MyContainer로 바꾸었다.

 

Container는 기본적으로 child가 없을 때 부모의 크기까지 확장한다. 여기서는 부모가 Scaffold이므로 노란 배경이 그 영역까지 확장되어 있음을 볼 수 있다.

 

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          color: Colors.yellow,
          child: Text('Hello Flutter'),
        ),
      ),
    );
  }
}

 

반면 컨테이너에 차일드를 주고, 그 안에 텍스트를 주면 영역이 차일드영역에 따라서 이렇게 변하게 된다. 좌상단 조그맣게 노란 박스와 짤린 텍스트를 볼 수 있을 것이다. 이렇게 수축된 모양을 쉬링크되었다고 표현한다고 한다. 컨테이너는 기본적으로 이런 특성이 있다. 물론 예외사항도 있다.

 

핸드폰의 상태바까지 컨텐츠가 올라가버려있다. 앱이 실사용 불가능한 영역에 컨텐츠가 있어버리니 보기 좋지 않다. 스테이터스바까지 못올라가게 하는 방법이 safe area이다. 

컨테이너에 safe area를 parents로 두려면 아래 코드와 같이, Safe Area를 먼저 주고, 그 안에 child를 하나 만든 뒤 Container의 내용을 밑으로 두면 된다. 

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Container(
            color: Colors.yellow,
            child: Text('Hello Flutter'),
          ),
        ),
      ),
    );
  }
}

기존에 body영역은 바로 Contaner 내용이 나왔지만, 부모영역으로 SafeArea를 주고, 그 뒤에 child를 Container내용으로 넣었다. 그러자 시계 등 상태바가 정상적으로 나오고 그 밑에 컨테이너 영역이 노란색으로 표시됨을 알 수 있다.

 

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Container(
            margin: EdgeInsets.only(left:40),
            width: 200,
            height: 200,
            color: Colors.yellow,
            child: Text('Hello Flutter'),
          ),
        ),
      ),
    );
  }
}

컨테이너의 크기를 지정해주는것도 가능하다. width, height로 사이즈를 지정해주면 차일드영역이 아닌 정해진 사이즈로 변한다. margin이라는 속성을 주면 border기준으로 바깥쪽 영역을 띄울 수 있다. width와 height를 각 200씩 줬더니 그 만큼 노란색 크기가 커졌으며, 왼쪽 여백을 40 줬더니 왼쪽에 딱 붙어있던 컨테이너가 옆으로 좀 움직였음을 알 수 있다.

 

이 때 EdgeInsets.only는 내가 정한 방향에만 마진을 주겠다는 의미이다. 이 외에도 다양한 방법이 있다.

-all : 마진 줄 픽셀 숫자가 일괄 적용됨

-symmetric : 좌우, 상하 각각 지정 가능

-fromLTRB : 왼쪽,위,오른쪽,아래 각각 픽셀 지정가능

-only : 특정 방향만 정해서 지정가능

 

마진을 눈으로 보고 싶다면 flutter inspector 도구를 통해 확인할 수 있다. 안드로이드 스튜디오 오른쪽에 돋보기모양 아이콘을 클릭하면 나타난다. 이 때, 정보가 내 생각보다 많다는 것을 알 수 있다. 나는 왼쪽 40만 여백을 준 것 같은데 이게 무슨 소리야? 싶겠지만, 이것이 parent와 chlid의 관계까지 고려한 가장 정확한 정보이다.

또한 사진에 노란 원으로 표시해둔 저 아이콘을 클릭하면, 시뮬레이터에서 직관적으로 마진 영역을 확인할 수 있다. 아래 화면에서 파란색으로 된 영역이 마진이 들어간 영역이다.

 

 

 

3. Column, Row Widget

Column은 이 위젯이 가진 children들을 세로로 표시하고, Row는 가로로 표시하는 위젯이다.

이 위젯들도 컨테이너 못지 않게 많이 쓴다.

얘들은 하부 위젯을 여러개 갖는다. 그래서 child가 아닌 children이라고 표현한다.

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Column(
            children: [
              Container(
                width: 40,
                height: 100,
                color: Colors.red,
              ),
              Container(
                width: 40,
                height: 100,
                color: Colors.yellow,
              ),
              Container(
                width: 40,
                height: 100,
                color: Colors.green,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

Column 실습을 위해 원래 코드에서 child 를 Container가 아닌 Column으로 변경해주었다.

그리고 Column의 children(child 아님) 에 Container를 넣었다. 이 때 칠드런은 대괄호([)를 열어서 컨테이너를 넣어준다.

칠드런은 여러 개를 넣을 수 있으므로 세 개를 넣고, 각기 빨간, 노란, 초록색 배경으로 설정했더니 위 화면과 같이 배치되었다.

 

Row는 Column과 가로, 세로방향만 다르고 나머지 속성은 모두 같다. 위 코드에서 Column을 Row로 바꾸면 위와 같은 화면이 출력된다.

 

 

MainAxisAlignment는 컬럼(또는 로우)를 정렬하는 속성이다. 정렬속성은 여러가지가 있다

-start : 디폴트속성으로 상단에 붙여서 정렬된다

-end : 끝에 맞춰 정렬

-center : 가운데 정렬

-spaceEvenly : 위젯 사이의 공간을 동일하게 띄움

-spaceAround : 가운데 위젯들을 넓게, 위젯들의 양 끝은 그 절반으로 띄운다

-spaceBetween : 위젯의 양 끝은 화면에 붙고, 그 사이를 띄운다

 

crossAxisAlignment는 일단 이름만 보았을 때는, 가로 기준으로 정렬이 이루어질 것이라고 기대할 수 있다. 그러나, crossAxis를 center 속성을 줘봐도 화면은 스타트 위치(좌상단)에 그대로 붙어있을 것이다.

왜냐하면 Column에서 crossAxis(가로)의 크기는 기본적으로 children 중 가장 큰 것의 크기를 기준으로 그 안에서 이루어진다. 그렇기 때문에 모든 가로 width가 40으로 맞추어진 현재의 컨테이너 안에서 더 움직일 수 없게 되는 것이다.

이를 해결하기 위해서는 가로 사이즈가 커져야 할 것이다. 그래서 칠드런에 가장 큰 위젯을 하나 추가하면 이것이 해결이 된다. 그래서 마지막줄에 Sizedbox를 이용해서 기다란 가로 위젯을 넣었다. double.infinity로 사이즈를 가능한 한 크게 늘릴 수 있도록 잡았다. (컨테이너로도 가능하지만, SizedBox를 씀으로써 컨텐츠 없는 가로 길이 잡는 용으로 구분 가능)

 

CrossAxisAlignment.center

 

컬럼의 크로스액시스에도 다양한 속성이 있으며, 아래와 같다.

-center : 중앙

-start : 왼쪽

-end : 오른쪽

-stretch : 가로기준으로 최대치로 늘려 칠드런들이 가로사이즈로 가득 차게 된다

728x90
반응형

댓글