스터디/Flutter+Dart

Flutter Hello Widget! Stateful, Stateless + Text, RichText, Button Example

Dalmangyi 2019. 7. 23.

지난 게시글 

https://dalgonakit.tistory.com/99

 

Flutter 프로젝트 만들고 실행하기 | VSCode

지난번 게시글 Flutter 시작하기 | MacOS에서 VSCode로.. 안드로이드와 아이폰은 개발한지도 오래 됬는데.. 최근 회사 업무가 빠르게 구현해야 될 것들이 생겨서 안그래도 눈 여겨 보던 Flutter를 해보려 합니다 F..

dalgonakit.tistory.com

 

 


 

 

위젯

Flutter에서 위젯은 현재의 구성과 상태를 가지고 있는 UI 단위를 말합니다. 

(안드로이드의 런처화면에 있는 상태를 저장하고 상태를 보여주는 위젯과 어찌보면 비슷하다고 생각됩니다. 아직 저도 배우는 단계다 보니 정확한 표현일지는 모르겠네요)

습관적으로 View로 생각하게 됩니다

 

 

 

 

가장 간단한 예제 | Hello World

Flutter는 Java 처럼 main() 함수가 가장 처음으로 호출 됩니다. 

Dart 언어 2.0 부터는 new를 사용하여 인스턴스를 만들지 않아도 되므로 new Center()가 아니고 Center()으로만 사용되었습니다. Text 도 마찬가지 입니다.

import 'package:flutter/material.dart';

void main() {
  runApp(Center(
        child: Text(
          'Hello world!', textDirection: TextDirection.ltr
        ),
      ),
    );
}

runApp() 함수를 통해서 위젯을 실행 시킬 수 있습니다. 

Flutter에서는 Widget를 표시할때 트리(Tree)구조로 많이 사용되게 됩니다. 위 예제에서는 Center Widget이 트리구조의 가장 최상위인 Root Widget으로 설정되어 실행되게 됩니다. Root Widget(Center Widget)은 자식 Widget으로 Text Widget을 가지고 있고, Text Widget은 String 'Hello, world!' 속성을 가지고 있습니다.

 

그렇다면 Widget들은 어디에 구현되어 있을까?

바로 import된 flutter/material.dart 라이브러리 안에는 Material이 적용된 Widget 클래스들이 포함되어 있습니다.

위에서 사용한 Center Widget과 Text Widget와 같은 UI용 Widget은 라이브러리가 import되어 있지 않으면 사용을 할 수 없습니다. 

 

특이하게도 Dart 언어에서는 추가적인 파라매터를 포함하지 않고 ,(콤마)만 적혀 있어도 문법적 오류가 나지 않습니다.

그래서 위 예제에서는 'Hello, world!' 다음에 콤마가 있고, Text와 Center Widget뒤에도 콤마를 적어보았습니다.

이와같은 문법에 대해서는 차차 말씀드리겠습니다.

 

상하 여백이 잘린 스크린샷 : 콘솔로 Log를 찍은것도 아닌, UI 화면을 이렇게 간단하게 만들 수 있다니!

실행하게 되면, Center Widget이 Text Widget을 감싸고 있기 때문에 (부모로 존재하기 때문에), 'Hello world!' 문구가 화면 가운데 존재하는 것을 볼 수 있습니다.

 

 

 

RichText

Text를 이어 붙일땐 RichText Widget으로 감싸고, String형을 써 줍니다. 

글자와 글자를 더할땐, TextSpan을 써서 children으로 여러 String을 더할 수 있다

Align(
      alignment: Alignment.center,
      child: RichText(
        text: TextSpan(
          text:'text1', 
          style: TextStyle(color: Colors.red), 
          children: [
            TextSpan(text: 'child1')
          ]
        )
      ),
    );

이쁘게 보이기 위해 Align으로 묶어줬습니다

RichText는 Widget이고, TextSpan은 Widget이 아니기 때문에. RichText없이는 TextSpan을 화면에 출력할 수 없습니다.

문자열과 문자열을 더할땐, RichText와 TextSpan.

 

트리 (Tree)

Flutter를 개발하다보면 트리는 말이 정말 많이 나오게 됩니다. 

그래서 간단하게 설명하고 넘어갈까 합니다

트리는 우리가 흔히 볼 수 있는 나무에서 따온 그래프 방식을 의미합니다. 

나무에선 뿌리(root)부터 나무 위까지 갈 수록 많은 나무가지가 나오는데 여기서 보는 트리는 하향방식으로 그려지고 있습니다.

 

출처 : wiki

위 그림처럼 자동차 안에는 엔진과 차체가 있고, 엔진 안에는 스크류와 파이프 등이 있는 구조. 하위로 갈수록 더 세세한 부분을 표현하는 방법입니다. 

이 처럼 Flutter에서는 Widget을 Root부터 계속해서 하위로 추가해서 트리구조로 만들게 됩니다. 

 

 

 

 

기본 Widget

더 많은 Widget이 있지만 정말 간단하게 화면을 꾸밀 수 있는 Widget만 나열해봅니다

1. Text

: 글자를 표현할 수 있습니다

2. Column, Row
: 가로 방향 또는 세로 방향으로 레이아웃을 만들 수 있습니다. 이 레이아웃의 하위 child를 포함할 수 있습니다. (웹 환경에선 flexbox 기반으로 구현된다고 합니다)

3. Stack
: child를 순서대로 쌓을 수 있는 레이아웃 입니다. 

4. Container

: 사각형 레이아웃입니다. 패딩, 마진, 테두리, 그림자 와 같이 꾸밀 수 있습니다. 

 

 

 

 

Widget의 기본 종류 | Stateless, Stateful

성능 이슈로 인해서 Widget의 개념을 나눴다고 합니다. 

(Stateless는 변경할 필요가 없으니 메모리가 고정해서 올려놓고, Stateful은 변경이 있을 수 있으니 메모리에 접근해서 신경을 써야하는게 아닐까요)

1. StatelessWidget

상태를 갖지 않는 위젯을 말합니다. 

생성자의 파라매터들로 인해서 생성이 되고나면, 이 위젯이 메모리에서 삭제되기 전까진 그 상태를 유지하는 Widget을 말합니다

새로운 상태를 만들기 위해선 트리에서 제거하고 다시 추가하는 방법밖에 없다고 합니다. 

 

2. StatefulWidget

위젯의 생명주기 동안 변할 수 있는 위젯을 말합니다. 

StatelessWidget하고는 모두 동일하지만, State Class가 추가되었을 뿐입니다.

 

setState() 함수를 호출해서 StatefulWidget에 상태를 변화시켜주고,

상태 변화를 감지한 Widget은 build() 함수를 호출해서 화면을 갱신하게 됩니다.

 

 

 

 

간단한 예제  | StatefulWidget 

버튼 하나를 추가해서 버튼의 색상을 바꾸는 예제를 해보겠습니다

import 'package:flutter/material.dart';

void main() {
    runApp(MaterialApp( 
        home: Center(child:MyButton()),
    ));
}

class MyButton extends StatefulWidget {

    @override
    State createState() {
        return MyButtonState();
    }
}

class MyButtonState extends State<MyButton> {
    var bgColor = Colors.white;
    
    @override
    Widget build(BuildContext context) {
        return RaisedButton(
                child: Text('State'),
                onPressed: () {
                    setState(() {
                        bgColor = (bgColor == Colors.white) ? Colors.lightBlue : Colors.white;
                    });
                },
                textColor: Colors.purple,
                color: bgColor,
            );
    } 
}

출처 파이썬킴https://pythonkim.tistory.com/112?category=696641

 

 

0) 코드가 적용된 앱 화면

상하 여백이 잘린 스크린샷 : 실행시 화면(왼), 버튼을 클릭한 화면(오)

 

 

1) 메인

import 'package:flutter/material.dart';

void main() {
    runApp(MaterialApp( 
        home: Center(child:MyButton()),
    ));
}

간단하게 메인에서는 MaterialApp을 이용해서 버튼을 중앙에 배치 하였습니다

 

 

2) 버튼

class MyButton extends StatefulWidget {

    @override
    State createState() {
        return MyButtonState();
    }
}

중앙에 배치된 MyButton은 상태가 변할 수 있게 StatefulWidget으로 상속받아서 만듭니다

생성과 동시에 createState가 호출되는데요. 이때 State를 상속받은 클래스를 넣어주시면 됩니다 

앞서 StatefulWidget에 대해서 설명할때 나온 build() 함수는 State 클래스 안에서 호출 되게 됩니다

 

 

3) 버튼 상태

버튼의 상태를 나타내는 코드는 긴 소스코드는 차근히 보면 별거 없습니다.

class MyButtonState extends State<MyButton> {
    var bgColor = Colors.white;
    
    @override
    Widget build(BuildContext context) {
        return RaisedButton(
                child: Text('State'),
                onPressed: () {
                    setState(() {
                        bgColor = (bgColor == Colors.white) ? Colors.lightBlue : Colors.white;
                    });
                },
                textColor: Colors.purple,
                color: bgColor,
            );
    } 
}

MyButtonState를 State로 부터 상속받게 되는데, 이때 제네릭 타입으로 State가 적용될 StatefulWidget 클래스(MyButton)를 적어줍니다. 버튼 상태를 저장할 backColor 변수를 만듭니다. build() 함수에서는 Metarial 특성을 가진 RaisedButton Widget을 생성하고 return 합니다. 

 

RaisedButton의 매개변수

  • RaisedButton에 text 매개변수가 없고, 꼭 위젯형태만 추가 할 수 있게 되어 있습니다. 그래서 Text Widget을 child로 추가하고 있습니다.
  • onPressed : 매개변수로 지정된 함수를 버튼이 클릭되면 호출합니다
  • textColor : child로 추가된 Text Widget의 색상을 설정합니다
  • color : RaisedButton의 전체 색상을 변경합니다. 

RaisedButton을 클릭하면 실행할 함수를 적었지만 함수 이름이 없어서 당황하지 말자 

onPressed: () {
 ...
}

onPressed에 등록해 놓은 함수 내부에서는 setState()에 등록한 함수를 호출해서 상태를 바꾸고 있습니다. 

setState(() {
	bgColor = (bgColor == Colors.white) ? Colors.lightBlue : Colors.white;
});

setState()에 등록한 함수 내용에는 배경 색상이 whie면, lightBlue를 세팅하고, 그게 아니면 white를 설정해라라는 간단한 3항 연산자 코드가 들어 있습니다.

 

MyButton 클릭시 흐름

 

 

 

 

 

마치며

사실 별거 아닌 내용인데, 처음 접하게 될 땐 괜히 어렵고 더 힘든거 같습니다

저도 처음이기 때문에 꾀 혼란스럽습니다. 잘못 된 부분이나 추가 설명이 필요한신분은 댓글로 달아주시면 감사하겠습니다.

 

 

 

 

 

 

댓글