[Flutter] Example
in Devlog on Devlog, ----- Mobile -----, Flutter
Widget
Image, Icon, Text, Row, Column, Padding 모든 것이 Widget으로 시작한다.
Rendering
Flutter는 Widget으로 만들어진 Layer Tree를 Skia 라는 그래픽 라이브러리를 이용하여 화면을 만들어낸다.
Skia는 Android, IOS, Chrome, Firefox 등 다양한 환경에서 공통 API를 가지고 화면을 그릴 수 있도록 도와주는 오픈 소스 2D 그래픽 라이브러리이다.
Skia가 만들어낸 View Data를 가지고 각 디바이스들은 CPU, GPU를 이용해 화면에 그려낸다.
Flutter는 각각의 디바이스에서 렌더링하는 방법을 Skia에게 맞겨 버리기 때문에 각각의 디바이스에 제한 없이 동일한 화면으로 렌더링이 가능하다.
각각 디바이스들의 Native Component를 사용하지 않기 때문에, 동일한 스타일의 화면을 만들어 낼 수 있다.
Flutter는 React와 ReactNative에서 영감을 받았기 때문에, 개발자로 하여금, 구현 코드와 상태 관리에만 신경을 쓰게 하여, 렌더링의 복잡도를 줄이고, 빠른 구현이 가능하게 된다.
Stateless / Stateful Widget
Widget은 StatelessWidget, StatefulWidget을 상속받아 만들 수 있다.
Widget은 Build 메서드를 포함하며, 이 Build 메서드를 이용해서 Layer Tree를 만든다.
StatelessWidget은 단 한번만 Build 과정이 일어난다. 때문에, 한번 그려진 화면은 계속 유지되며, 성능 상 장점이 생긴다.
StatefulWidget은 state를 포함하며, setState가 발생할 때마다 다시 Build 과정이 일어난다. 때문에 동적 화면을 쉽게 구현이 가능하다.
StatelessWidget
정적인 위젯
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MyHomePage(title: 'Fluuter Demo Home Page)
)
}
}
StatefulWidget
동적인 위젯
class MyHomePage extends StatefulWidget{
MyHomePage({Key key, this.title}) : super(key:key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
State lifecyle
StatefulWidget.createState 시점에 state가 생성된다.
- BuildContext와 연결된다.
Widget build(BuildContext context)
State.initState를 딱 한번 호출한다.
State.didChangeDependencie를 호출한다.
setState가 호출될 때마다 State.didUpdateWidget을 호출한다.
widget이 트리에서 제거되는 순간 state.deactivate 호출한다.
- 모든 렌더링이 완전히 끝나고, State.dispose를 호출하여 자원을 해제한다.
State Management
Flutter는 State Management로 Reative Application을 전체 앱의 동작과 상태를 관리한다.
setState로 Widget의 state를 변경하며, 그때 마다 Layer Tree를 다시 만든다.
이 Layer Tree를 가지고 Skia가 화면을 다시 그리기 때문에, UI = f(state)
즉, 화면은 상태와 같다.
https://pub.dev/packages/provider
Provider package 에는 ChangeNotifierProvider를 제공하고 있다.
ChangeNotifier에서 발생한 notify를 구독하여, 각각의 Consumer에게 변경 상태를 전달할 수 있는데, 마치 React의 Context API와 같다.
ChangeNotifier 를 상속받아 Model Class 를 정의한다.
class CounterModel extends ChangeNotifier {
var number = 0;
void add(addNumber) {
number += addNumber;
notifyListeners();
}
}
ChangeNotifierProvider로 Model Class를 정의하고, child에게 Model을 전달한다.
import 'package:provider/provider.dart';
void main() => runApp(
ChangeNotifierProvider(
builder: (context) => CounterModel(),
child: MyApp(),
)
);
Consumer로 Provider로 부터 상태를 구독하고, 변경 상태를 전달 받으면 해당 Widget을 재 랜더링 한다.
@override
Widget build(BuildContext context) {
return Consumer<CounterModel>(
builder: (context, counter, child) {
return Row(
children: <Widget>[
Text(counter.number.toString()),
IconButton(
icon: Icon(Icons.plus_one),
onPressed: () {
counter.add(1);
},
)
],
);
},
);
}