Widgets
개념
정의 및 목적
Everything is a Widget. It’s all Widgets!
UI를 묘사하는 다트의 클래스
화면에 나타날 요소를 결정하는 데이터와 설정
<aside> 💡 JS로 치면 컴포넌트와 비슷!
</aside>
→ 플러터에서는 모든 요소 하나하나가 위젯
→ 위젯이 위젯 포함
→ 위젯들이 모인 Widget Tree를 바탕으로 화면 그림
위젯의 주 목적은 build()함수 구현
기본 예시
import 'package:flutter/material.dart';
void main() {
runApp(
Center(
chlid: Text(
'Hello, world!',
textDirection: TextDirection.lrt,
),
),
);
}
Widget Tree는 Center widget
과 Text widget
으로 구성
플러터의 Root Widget은 화면 전체를 덮어주고 Text widget
에서 Hello world
표시
<aside> 💡 기본적인 widget 예시
- 레이아웃 : Scaffold, Stack, Row, Column
- 구조 : Button, Toast, MenuDrawer
- 스타일 : TextStyle, Color
- 애니메이션 : FadeInPhoto, Transform
- 위치와 정렬 : Center, Padding </aside>
기본 위젯
Text
- 앱에 텍스트 작성
Row, Column
- flexible한 레이아웃 만들어 줌
- Row와 Column 방향으로 각기 설정 가능
Stack
- 수직, 수평으로 linearly하게 위젯 쌓는 것이 아닌 페인트 겹겹이 칠하는 것처럼 위젯 구현 가능
Container
- 직사각형의 visual 요소 생성
- BoxDecoration으로 꾸며질 수 있음, 배경화면이나 경계, 그림자 등 꾸밀 수 있음
- margin, padding, constraint 값을 가질 수 있음
- matrix 이용해 3차원 형태 공간으로 변형 가능
구분
Widget은 State를 관리 여부에 따라 크게 2개로 구분
Stateless Widget
상태가 없는 위젯
변화가 거의 없는 위젯을 선언
Stateful Widget
state라는 데이터 변화를 감지하고, state가 변할 시 위젯을 rebuild하는 위젯
setState 함수를 통해 state 변화를 감지
Stateless Widget
개념
상태가 없는 Widget
변경 가능한 상태가 필요하지 않은 Widget
특징
- 상태(state)가 없기 때문에 상태 관리할 필요가 없음
- 한번 그리고 다시 그려야할 필요가 없을 때 사용
- StatelessWidget의 필드들은 final로 선언
- StatelessWidget은 부모 위젯으로부터 arguments를 final로 전달받음
- build() 함수를 요청받으면 전달받은 arguments를 새로운 위젯을 생성하는 데 전달
❓ StatelessWidget은 결코 변하지 않는 것일까?
결코 변하지 않는다는 의미는 아니다. 외부의 정보에 따라 반응하기 때문이다.
위젯을 언제 tree에서 remove 해야할지, 언제 rebuild 해야할지가 외부로부터 결정된다.
기본 형태
class Example extends StatelessWidget {
const Example({ Key? key }) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(...);
}
}
constructor
기본생성자
class명 Example
과 동일한 이름
뒤에 붙은 : super(key: key)
는 부모인 StatelessWidget의 기본생성자를 호출
데이터를 전달할 파라미터가 없으면 기본생성자는 생략 가능
build
화면을 구성한 UI들을 구현하는 메서드
Widget을 리턴하는 함수 → build가 return하는 Widgets로 View가 그려짐
⇒ 화면이 출력될 때 build 메서드가 호출되면서 build 메서드 내부에 구현한 UI 위젯들이 화면에 출력
모든 위젯 클래스에 포함된 필수 메서드 (SLW과 SFW의 State 클래스에서 구현)
이해를 위한 예시
class SLWdemo extends StatelessWidget {
int _count = 0;
@override
Widget build(BuildContext context) {
print("** build - StatelessWidget Demo");
return Scaffold(
appBar: AppBar(title: Text("Stateless Widget")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"$_count",
style: TextStyle(fontSize: 30),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(Icons.add),
onPressed: () {
_count++;
print("value of _count = $_count");
},
),
FloatingActionButton(
heroTag: null,
child: Icon(Icons.remove),
onPressed: () {
_count--;
print("value of _count = $_count");
},
)
],
)
],
),
),
);
}
}
❓ 제대로 작동해서 화면이 변할까?
버튼 클릭해도 _count값은 증가되지만 화면상으로는 변화가 보이지 않는다.
앞서 설명한 내용을 토대로 SLW은 내부의 모든 UI 위젯이 상태를 가질 수 없다. 상태가 없으니 상태의 변화를 인지할 필요도 없고 할 수도 없다.
그래서 화면이 생성될 때 한번만 build 메서드를 호출해서 화면을 구성한다. 그 이후에는 build 함수가 다시 호출되지 않는다.
따라서, 버튼을 클릭해 _count 값을 변경 시켜도 build 메서드는 호출되지 않으므로 화면 내 Text 위젯의 값도 변경되지 않는다.
사용 예시
class Box extends StatelessWidget {
Color color;
String text;
const Box(this.color, this.text);
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: this.color),
child: Text(this.text)
);
}
}
❗ This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final: Box.color, Box.text
상태가 변경되는 것을 막기 위해 statelessWidget 필드 안 변수들을 final 선언해라!
class Box extends StatelessWidget {
final Color color;
final String text;
const Box(this.color, this.text);
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: this.color),
child: Text(this.text)
);
}
}
class Box extends StatelessWidget {
final Color color;
final String text;
const Box({this.color, this.text});
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(color: this.color),
child: Text(this.text)
);
}
}
// before
Box(Colors.blue, "Hello"),
Box(Colors.pink, "world"),
Box(Colors.amberAccent, "Dart")
// after
Box(color: Colors.blue, text: "Hello"),
Box(color: Colors.pink, text: "world"),
Box(color: Colors.amberAccent, text: "Dart")
named parameter
파라미터는 크게 positional parameter와 named parameter가 존재
positional : 순서에 따라 파라미터 값 넣어주는 것
named : 이 파라미터가 어떤 파라미터인지 명시해주면서 코드 작성하는 것
Reference
Flutter ) StatelessWidget 자세히 살펴보기