플러터로 Stateless UI 따라 만들기 : Button, Settings

윤뿔소·2023년 5월 26일
0

Dart / Flutter

목록 보기
13/18
post-thumbnail

이번엔 플러터로 아래 사진에 보이는 버튼을 만들 것이다.

버튼은 이곳 저곳에 쓰이고, 문구와 색만 달라지기 때문에 재사용 가능하게 버튼을 만들어 볼 것이다!

기본 재료 배치하기

이제 전판에 어떻게 하는지 설명을 다 했으니 축약해서 설명하겠다. 이거 인스턴스 만드는 과정 다 기술하면 너무 오래 걸려서,, 사실,,

class App extends StatelessWidget {
  
  ...
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                height: 80,
              ),
              ...
              SizedBox(
                height: 120,
              ),
              Text(
                'Total Balance',
                style: TextStyle(
                  fontSize: 22,
                  color: Colors.white.withOpacity(0.8),
                ),
              ),
              SizedBox(
                height: 5,
              ),
              Text(
                '\$5 194 382',
                style: TextStyle(
                  fontSize: 42,
                  fontWeight: FontWeight.w600,
                  color: Colors.white,
                ),
              ),
              SizedBox(
                height: 20,
              ),
              Row(
                children: [],
              )
            ],
          ),
        ),
      ),
    );
  }
}
  1. SizedBox로 간격 띄우기
  2. Total Balance 문구 추가 및 꾸미기
  3. SizedBox로 간격 띄우기
  4. \$5 194 382문구 추가 및 꾸미기
    • $가 명령어에 들어가기 때문에 \ 추가
  5. SizedBox
  6. 수평으로 버튼이 들어가니 Row를 씀.

이렇게 했다.

버튼 만들기

만들었던 Row안에 버튼을 만들 것이다.

버튼을 만들기 위해 Container라는 위젯을 쓸 것이다.

Container

딱 HTML의 div같은 존재다. child를 가질 수 있고 여러 이벤트와 스타일을 받을 수 있다.

Row(
  children: [
    Container(
      decoration: BoxDecoration(color: Colors.amber),
      child: Text('Transfer'),
    )
  ],
)

이렇게만 해줘도

이렇게 된다.

decoration: BoxDecoration()으로 배경, Radius 등으로 꾸밀 수 있다.

버튼 Padding 넣기

되게 쉽다. 플러터의 Padding은 항상 내용물을 감싸주는 식으로 쓰면 된다.

Row(
  children: [
    Container(
      decoration: BoxDecoration(color: Colors.amber),
      child: Padding(
        padding: EdgeInsets.symmetric(
          horizontal: 40,
          vertical: 15,
        ),
        child: Text(
          'Transfer',
          style: TextStyle(fontSize: 22),
        ),
      ),
    )
  ],
)

ContainerchildPadding을 넣고 EdgeInsetschild를 넣어 내용을 채웠다. 되게 직관적이다.

Border Radius 넣기

그냥 되게 쉽다. BoxDecorationborderRadius 속성이 있다. 그러고 넣으면 된다. 원으로 Radius를 넣을 거기 때문에 circular로 넣을 것이다.

Row(
  children: [
    Container(
      decoration: BoxDecoration(
          color: Colors.amber,
          borderRadius: BorderRadius.circular(45)),
      child: Padding(
        padding: EdgeInsets.symmetric(
          horizontal: 40,
          vertical: 15,
        ),
        child: Text(
          'Transfer',
          style: TextStyle(fontSize: 22),
        ),
      ),
    )
  ],
)

짠!


다른 버튼을 만들고 색과 글씨색도 만들자.

됐다!

근데 보면 버튼이 2개가 생겼다. 이러면 어떻게 해야할까? 바로 재사용가능하게 설정해야겠지?

커스텀 위젯 재사용하기

🚨 주의 : 아래 팁을 먼저 보고 세팅 및 Code Actions에 대한 이해를 먼저 하고 오시오! 🚨

보고 왔다면 이제 만들어보자. Code Actions를 쓰기 위해 위젯을 따로 재사용 가능한 클래스로 만들기 위해 만들고픈 곳에 클릭하고 커맨드 . 을 누르자.

클릭하고 재사용할 수 있는 클래스 이름을 설정하고 나면 아래 사진과 같이 나온다.

이제 나누고, 따로 위젯은 다른 폴더 및 파일에 관리하는 것이 좋으니 /lib/widgets/button.dart 파일을 만들어 따로 관리하자.

붙여넣기하고 나면

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          color: Colors.amber, borderRadius: BorderRadius.circular(45)),
      child: const Padding(
        padding: EdgeInsets.symmetric(
          horizontal: 40,
          vertical: 15,
        ),
        child: Text(
          'Transfer',
          style: TextStyle(fontSize: 22),
        ),
      ),
    );
  }
}

그대로 데이터가 남아있어서 폰트 내용, 색깔, 배경색 등을 조정하지 못한다. 그래서 그런 변경점을 받아줄 수 있는 속성을 만들어보자.

위젯을 변경할 수 있는 프로퍼티 만들기

우선 버튼에 변경해야하는 점들이 뭐가 있는지 생각해보자.

  1. 텍스트 내용
  2. 폰트 색
  3. 배경색

이렇게 나뉜다. 그걸 따라서 필수이자 입력해야되는 변수를 final로 선언해주고, Constructor를 지어보자.

class Button extends StatelessWidget {
  final String text;
  final Color textColor;
  final Color bgColor;

  const Button(
      {super.key,
      required this.text,
      required this.textColor,
      required this.bgColor});
...

이렇게 하면 된다.

꿀팁: Code Actions

여기서 꿀팁이 또 있는데 원래 Constructor를 일일이 써줘야하는데 Code Actions가 또또또!! 도와준다.

constructor를 저렇게 만들어 준다.. 이 미친 DX!!

하드코딩된 값들을 클래스 프로퍼티로 바꿔주기

...
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: bgColor,
        borderRadius: BorderRadius.circular(45),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: 40,
          vertical: 15,
        ),
        child: Text(
          text,
          style: TextStyle(
            fontSize: 22,
            color: textColor,
          ),
        ),
      ),
    );

끝! 주의할 점은 const의 사용 유무이다. 저렇게 text, textColor 등의 변수가 들어가면 Constant가 아니기 때문에 오류를 일으킨다. 그래서 const가 필요없는 곳에는 지워주는 작업이 필요하다.

실사용하기

그 다음 화면을 만드는 main.dart에 가서 써보자.

가서 자동완성을 import 포함 기가 막혀주게 하기 때문에 저렇게 하고 값을 넣어주기만 하면 끝!

const Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Button(
      text: 'Transfer',
      textColor: Colors.black,
      bgColor: Color(0xFFF1B33B),
    ),
    Button(
        text: 'Request',
        textColor: Colors.white,
        bgColor: Color(0xFF1F2123)),
  ],
)

끝!!


다트는 파란줄을 싫어해

위 코드 사진을 다시 보고 오면 파란줄이 겁나 쳐져 있다. 뭐지 하고 마우스 오버를 해보면

Use 'const' with the constructor to improve performance.
Try adding the 'const' keyword to the constructor invocation.

해석 해보면 const 키워드를 생성자 호출에 추가하여 성능을 개선하라는 경고 메시지이다.

플러터에서는 const를 상수 취급, 변하지 않는 변수 취급을 해서 최적화를 해 런타임 시간을 줄여준다. 왜 줄여주는 걸까?

다트는 상수를 좋아해

Dart에서 const 생성자를 사용하여 객체를 상수로 평가하면 런타임 성능이 향상될 수 있다. 그 이유는 아래와 같다.

  1. 컴파일 타임 상수 평가
    const 생성자를 사용하여 객체를 생성하면 해당 객체의 값이 컴파일 타임에 이미 결정된다. 따라서 런타임에서 객체를 생성하거나 초기화할 필요가 없고, 컴파일된 코드에서 상수로 대체될 수 있다. 이로 인해 초기화 작업이 줄어들어 실행 속도가 향상될 수 있다.
  2. 상수 객체 재사용
    const 생성자를 사용하여 객체를 생성할 때 동일한 인수로 다수 호출되면, 동일한 상수 객체를 재사용할 수 있다. 이는 메모리 사용량을 줄이고 객체 생성에 필요한 리소스를 절약할 수 있고, 가비지 컬렉션의 부담을 줄여 성능을 개선할 수 있다.
  3. 컴파일러 최적화
    const 생성자를 사용하면 컴파일러가 추가적인 최적화를 수행할 수 있다. 컴파일러는 const 생성자를 통해 생성된 객체를 컴파일 시간에 알고 있다. 그래서 해당 객체에 대한 연산을 미리 계산하고 필요한 경우 리터럴로 대체할 수 있다. 이는 런타임에서의 연산 부하를 줄이고 코드 실행 속도를 향상시킬 수 있다.

예시

const taxAmount = 15;
const priceAmount = 30;

var finalPrice = taxAmount + priceAmount;

라는 코드가 있을 때, finalPrice연산 실행 시 const로 선언된 taxAmountpriceAmount의 메모리 공간을 따로 만들어 대입하지 않는다.
다트 컴파일러가 이미 알고있는 상수라면 바로 가져다 쓰게끔 프로그래밍 돼있기에 최적화가 된다.


물론, const 생성자를 사용하여 상수로 평가하는 것이 항상 성능 향상을 가져오는 것은 아니다. 작은 규모의 코드에선 그다지 유용하진 않지만 큰 규모로 넘어간다면 최적화를 무시할 수 없기 때문이다.

요약

  • const로 선언된 값은 수정할 수 없고, 컴파일 전에 값을 알 수 있다.
  • Dart 컴파일러는 이러한 값을 미리 알고 있어서 변수 대신 값을 직접 사용하여 메모리 공간을 절약하고 최적화를 수행할 수 있다.
  • 이러한 최적화는 코드를 컴파일할 때 값들을 검토하고, 변수를 값으로 대체해 더 나은 코드를 생성한다.
  • 이러한 최적화는 Flutter에서도 적용될 수 있고, const는 수정할 수 없고 컴파일 전에 값을 알 수 있는 변수로 매우 유용하다!

이래서 다트가 파란줄로 const를 쓰라는, 소리없는 아우성을 치고있었던 것이다.

플러터도 상수를 좋아해

내가 만든 코드를 직접 보면서 설명하겠다.

Color(0xFF181818)가 상수고, 컴파일 후 변하지 않기 때문에 const를 사용해 상수로 평가받게 해야한다. 그래서 const를 붙이면 오류가 없어진다.

그래서 일일이 const를 붙여줘야하는데, 우리의 구세주이자 신이자 구원자인 VSC에 자동으로 달아주는 설정이 있다.

const VSC 설정

명령 팔레트(Cmd + Shift + P)를 누르고 Open user settings를 눌러 세팅 JSON에 들어간다.

거기에 아래와 같이 입력한다.

  "editor.codeActionsOnSave": {
    "source.fixAll.": true
    "source.organizeImports": true
  },

이러면 알아서 자동으로 고쳐준다;; VSC는 신이다. organizeImportsimport 잘못 쓰면 정렬해준다.

또 아래 코드를 추가해주면 부모 자식 간 관계도 보여준다.

  "dart.previewFlutterUiGuides": true,
  "dart.previewFlutterUiGuidesCustomTracking": true,

이렇게 말이다. 훨씬 직관적으로 변했다. child, children의 관계가 명확해진다.

나머지는 짜잘한 것들인데 저거 하나하나 복사하기 귀찮다면 아래 복사해서 VSC 세팅에 넣으면 된다.

  "dart.renameFilesWithClasses": "always",
  "dart.previewFlutterUiGuides": true,
  "dart.previewFlutterUiGuidesCustomTracking": true,
  "[dart]": {
    "editor.formatOnSave": true,
    "editor.formatOnType": true,
    "editor.rulers": [80],
    "editor.selectionHighlight": false,
    "editor.suggest.snippetsPreventQuickSuggestions": false,
    "editor.suggestSelection": "first",
    "editor.tabCompletion": "onlySnippets",
    "editor.wordBasedSuggestions": false,
    "editor.codeActionsOnSave": {
      "source.fixAll": true,
      "source.organizeImports": true
    }
  },

나도 솔직히 하나하나 어떤 의미인지는 모르긴 하다. 쨌든 이렇게 세팅하니 자동으로 잡아주고 좋다!


긴급 꿀팁 : Code Actions

Padding 넣을 때 우리는 어떻게 넣었는가? 잘라내고 Padding 넣고 그 안 괄호에 child를 쓰고 넣지 않았는가? 이제 그렇게 안해도 된다.

  1. 추가하고픈 인스턴스를 클릭
  2. Cmd + . or 왼쪽에 뜬 전구 모양, Code Actions 클릭
  3. 추가할 위젯 등을 고르기

이렇게 하면 정말 쉽게 된다. 짤을 보자.

진짜 플러터의 DX는 미쳤다.. 원하는 게 없다면 widget을 골라 우리가 알아서 위젯을 입력해 사용할 수도 있다!!! 미친 플러터!!!

당연히 삭제하는 것도 가능하다.

내용이 없는 위젯만 가능하니 알아놓자. 다른 기능으로는 위젯 추출도 가능하고, 위젯 전체를 수정없이 바로 위로 옮길 수도 있다. 대박 꿀팁쓰!

profile
코뿔소처럼 저돌적으로

5개의 댓글

comment-user-thumbnail
2023년 5월 26일

어려워서 정신을 못차리겠네요. 전편도 이어서 보고 왔는데, 잘모르다보니 어렵습니다 ㅠㅠ 고생하셨어요

답글 달기
comment-user-thumbnail
2023년 5월 28일

이정도도 엄청 많은데요 ..? 근데 플러터 해본 적이 없어서 이해는 잘 안되네욤 ... 역시 직접 해봐야... ㅋㅋㅋㅋ

답글 달기
comment-user-thumbnail
2023년 5월 28일

정신이 혼미해집니다...

답글 달기
comment-user-thumbnail
2023년 5월 28일

와우 너무 어려운 걸요.. 플러터 강의 저도 하나 가지고있는데 보게되면 다시 와보려구요 :) 고생하셨습니다!

답글 달기
comment-user-thumbnail
2023년 5월 28일

어려운데 신기하네욬ㅋㅋㅋㅋㅋ 잘 읽었습니닷!!

답글 달기