[Flutter] 1. Introduction to Widget

위 공식 문서를 바탕으로 작성되었습니다.

Hello World

runApp() 은  Flutter의 뿌리가 되는 함수입니다. Widget을 입력으로 받고 이를 Widget Tree의 뿌리로 삼습니다.
Flutter Framework는 루트 위젯을 디바이스에 완전히 Fit하게 만듭니다.
일반적으로 Flutter의 위젯은 State의 유무에 따라서 StatelessWidget과 StatefulWidget 중 하나의 하위 클래스로 작성됩니다.

기본 위젯들

Text

Styled Text를 생성하는 위젯입니다.

Row, Column

반응형 레이아웃을 생성하는 위젯입니다. 당연히 가로는 Row, 세로는 Column Widget을 사용합니다.

Stack

웹에서 position:absolute처럼 Paint Order에 따라서 위에 배치됩니다.

Container

직사각형의 Visual Element를 생성해주는 위젯입니다. Container는 BoxDecoration 을 이용해 꾸밀 수 있습니다. (배경이라든가, 테두리라든가, 그림자라든가...)

Scaffold Widget

Material Design을 구성하기 위핸 뼈대 Widget입니다.
Constructor의 Parameter에 Widget을 넘겨주면서 작동하고, 입력받은 Parameter들을 Material Design Guide에 맞춰서 적당히 잘 구성해줍니다.
Appbar, Body등 다양한 하위 위젯들이 존재하고... 아래 API 시트를 통해 자세한 정보를 알 수 있습니다.

Appbar

App의 상단에 위치하는 공간으로, 아래와 같은 구조로 구성되어있습니다.

GestureDetector

플러터에서 제스쳐 (onClick, onTap 등등..)를 감지하는 위젯입니다. 형태는 존재하지 않고, JS의 eventListener와 비슷하다고 생각하면 될듯
child Widget과 동작에 해당하는 Callback Function을 Constructor로 받아서 해당 Child Widget에 닿는 동작을 감지하여 Callback Function을 실행하는 구조로 되어있습니다.
Button 같은 특이한 Widget들은 onPressed() 같은 자체 Getsture Detection Option을 갖고 있는 경우도 있습니다. (HTML input처럼..)
import 'package:flutter/material.dart'; class MyButton extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onTap: () { print('MyButton was tapped!'); }, child: Container( height: 50.0, padding: const EdgeInsets.all(8.0), margin: const EdgeInsets.symmetric(horizontal: 8.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5.0), color: Colors.lightGreen[500], ), child: Center( child: Text('Engage'), ), ), ); } } void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: MyButton(), ), ), ), ); }
Dart
복사

Stateless Widgets

Stateless Widget은 Parent Widget으로부터 parameter를 받아서 final 멤버 변수에 저장합니다.
Widget이 build()에 의해 호출되었을 때, 저장된 변수를 가져오므로 Stateless (상태가 없는)라고 할 수 있습니다.

StatefulWidget

Stateless Widget과 다르게 상태가 있는 위젯입니다. State라는 객체를 만들어 상태를 관리하고 사용합니다.
import 'package:flutter/material.dart'; class Counter extends StatefulWidget { _CounterState createState() => _CounterState(); } class _CounterState extends State<Counter> { int _counter = 0; void _increment() { setState(() { _counter++; }); } Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ElevatedButton( onPressed: _increment, child: Text('Increment'), ), SizedBox(width: 16), Text('Count: $_counter'), ], ); } } void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: Counter(), ), ), ), ); }
Dart
복사
여기서 StatefulWidget과 State 객체가 다른 클래스라는 점에 주목해야 합니다. 이런 구조를 갖는 이유는 두 객체가 다른 생애주기(Life Cycle)를 갖기 때문인데,
StatefulWidgetStatelessWidgetbuild() 함수가 한번 실행될때마다 해당 위젯을 화면에 그리게 됩니다.
이때, StatefulWidgetStatelessWidget은 한번 build()되고 끝납니다. 이때 StatelessWidgetState라는 서브 클래스를 선언하고 선언하고, State 에서는 setState() 함수가 실행될 때마다 프레임 워크에서 다시 build() 메서드를 실행하여 위젯을 다시 그리게 됩니다.
이러한 구조를 갖는 이유는 성능 때문이라고 하네요.
아래처럼 build 함수에 StatelessWidget을 집어넣을 수도 있습니다.
import 'package:flutter/material.dart'; class CounterDisplay extends StatelessWidget { CounterDisplay({required this.count}); final int count; Widget build(BuildContext context) { return Text('Count: $count'); } } class CounterIncrementor extends StatelessWidget { CounterIncrementor({required this.onPressed}); final VoidCallback onPressed; Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, child: Text('Increment'), ); } } class Counter extends StatefulWidget { _CounterState createState() => _CounterState(); } class _CounterState extends State<Counter> { int _counter = 0; void _increment() { setState(() { ++_counter; }); } Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CounterIncrementor(onPressed: _increment), SizedBox(width: 16), CounterDisplay(count: _counter), ], ); } } void main() { runApp( MaterialApp( home: Scaffold( body: Center( child: Counter(), ), ), ), ); }
Dart
복사