반응형
기본적으로 플러터 UI 는 ListView, Column, Text 등 기본 위젯을 이용해서 구성한다. 하지만, 어플리케이션의 구현이 복잡해 질수록 재사용 가능한 커스텀 위젯의 개발이 불가피하다. 이번 포스트에서는 어떻게 커스텀 위젯을 만들 수 있는지 간략히 설명하도록 하겠다.
1. 배경
플러터는 기본적으로 렌더링을 실제로 수행하는 RenderObjectWidget 류의 위젯도 제공하지만, 여러 위젯을 조합해서 새로운 위젯을 만들 수 있는 방식도 제공을 하고 있다. 조합 가능한 위젯의 대표적인 것이 Stateless, Stateful 위젯이며, 이 컨셉을 앱 내에서 커스텀 위젯 개발 시에 활용 가능하다.
2. Stateless 위젯
Stateless 위젯은 한마디로 상태 변경이 불가능한 (immutable) 위젯이다. 따라서,
- 클래스 내부 변수는 모두 final 변수고,
- build() 함수를 구현 해서 위젯을 조합한다.
아래 예제는, 'hello' 라는 텍스트 화면 가운데 출력하는 위젯이다.
class Sample extends StatelessWidget {
final String txt = 'hello';
@override
Widget build(BuildContext context) {
return Center(child: Text(txt));
}
}
3. Stateful 위젯
반면, Stateful 위젯은 상태 변경이 가능한 (mutable) 위젯이다. 약간 복잡하지만 핵심은,
- Stateful 위젯 클래스 내에서 State 클래스를 생성해야 한다. (createState() 오버라이드)
- State 클래스 내에서 initState(), dispose() 등 오버라이드 함수를 활용하여 상태를 관리하고,
- State 클래스 내에서 setState() 함수 호출을 통해 변경된 내용을 UI에 반영한다. (내부적으로 build 호출됨)
- State 클래스 내에서 build() 함수를 구현하여 위젯을 조합한다.
State 클래스의 주요 함수를 조금만 더 설명하자면,
함수 이름 | 호출 되는 시점 |
initState() | 위젯이 최초 생성 될 때 한번 불린다. |
dispose() | 위젯이 제거 될 때 불린다. |
build() | 커스텀 위젯을 조합하여 return 하는 함수로 아래 두 상황에서 호출된다. - initState() 호출 후 - setState() 호출 후 |
어렵다.. 예제를 보자..
아래 예제는, 10초 마다 'hello' 와 'bye' 라는 글자를 번갈아 가면서 화면 중앙에 보여주는 위젯이다. _SampleState 클래스 내부를 간략히 설명하면,
- initState() 에서 10초 짜리 주기적인 타이머를 초기화 했고, 만기시 마다 txt 라는 변수의 값을 'hello' 혹은 'bye'로 변경후 setState() 를 호출한다.
- dispose() 에서는 타이머를 취소 시켰다.
- build() 에서 현재 설정되어 있는 txt 변수의 텍스트를 화면 중앙에 보여준다.
결국, initState 에서 등록한 타이머가 만기 될때 마다 txt 변수를 변경하고 setState()를 호출하게 되고, setState() 호출에 의해서 build() 가 불리게 되어 UI가 업데이트 된다.
import 'dart:async';
import 'package:flutter/material.dart';
class Sample extends StatefulWidget {
@override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
String txt = 'hello';
Timer timer;
@override
void initState() {
timer = Timer.periodic(Duration(seconds: 10), (timer) {
setState(() {
if (txt == 'hello')
txt = 'bye';
else
txt = 'hello';
});
});
super.initState();
}
@override
void dispose() {
timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(child: Text(txt));
}
}
반응형
댓글