Flutter UI Tree에 관련된 Flutter의 3가지 주요 트리구조 글을 읽었다.
이 글에서는 Flutter의 UI가 단순히 Widget Tree 하나로 구성되는 것이 아니라, 내부적으로 서로 다른 역할을 가진 세 가지 Tree 구조를 통해 구성되고 관리된다고 설명하고 있다.
Flutter UI는 다음과 같은 세 가지 Tree로 구성된다:
Widget Tree
Element Tree
Render Tree
이 세 Tree는 각각 역할이 명확히 나뉘어 있으며, UI 생성, 상태 유지, 렌더링 과정에서 서로 연결되어 동작한다.
전체적인 구조는 다음과 같다:
Widget
↓
Element
↓
RenderObject
Widget Tree는 개발자가 작성하는 UI 코드 자체이다.
Widget의 특징:
Widget은 UI를 구성하는 실제 객체라기보다는,
UI를 어떻게 구성할지를 설명하는 설정 객체(config)의 역할을 한다.
Element Tree는 Widget Tree와 Render Tree 사이에서 중간 역할을 수행한다.
Element의 주요 역할:
Flutter에서 중요한 구조 중 하나는 state가 Widget이 아니라 Element에 저장된다는 점이다.
이 때문에 Widget이 rebuild되어도 state는 유지된다.
또한 Flutter에서 사용하는 BuildContext는 실제로 Element 객체를 참조한다. (BuildContext = Element)
즉, context는 Tree 상에서의 위치를 나타내는 객체이다.
Render Tree는 실제 화면 렌더링을 담당하는 객체 트리이다.
RenderObject의 역할:
대표적인 RenderObject:
RenderBox → 기본 layout 단위
RenderFlex → Row / Column layout 처리
RenderParagraph → Text 렌더링
RenderObject는 Element에 의해 생성되고 관리되며, 실제로 화면에 UI를 그리는 역할을 담당한다.
초기 생성:
Widget 생성
→ Element 생성
→ RenderObject 생성
→ 화면 렌더링
rebuild 발생 시:
새 Widget 생성
→ 기존 Element와 비교(diff)
→ 필요한 경우만 RenderObject update
→ 화면 업데이트
이 구조를 통해 Flutter는 rebuild가 자주 발생하더라도 전체 UI를 다시
그리지 않고, 변경된 부분만 업데이트하여 높은 성능을 유지할 수 있다.
Flutter에서 state는 Widget이 아니라 Element에 저장된다.
구조:
StatefulWidget
↓
StatefulElement
↓
State
rebuild 발생 시:
새 Widget 생성
기존 Element 재사용
기존 State 유지
create
mount
update
unmount
dispose
runtimeType 동일
key 동일
조건이 다르면 Element는 재사용되지 않고 새로 생성된다.
Flutter에서 BuildContext는 Element 객체이다.
BuildContext = Element
Element가 unmount되면 해당 context도 더 이상 유효하지 않다.
예:
await Future.delayed(...);
Navigator.of(context).pop();
해결:
if (!context.mounted) return;
또는
if (!mounted) return;
RenderObject는 실제 화면 렌더링을 담당한다.
주요 역할:
대표:
RenderBox\
RenderFlex\
RenderParagraph
GlobalKey는 Element를 전역적으로 식별한다.
가능:
주의:
비용이 크므로 필요한 경우에만 사용
initState에서는 InheritedWidget 의존 관계가 안정되지 않은 상태이다.
권장 사용 위치:
didChangeDependencies
build
postFrameCallback
didChangeDependencies는 InheritedWidget 변경 시 호출된다.
Widget → UI 정의
Element → lifecycle + state 관리
RenderObject → 실제 렌더링
중요 포인트:
Flutter는 단순히 Widget rebuild로 UI를 다시 그리는 구조가 아니라,
Element Tree를 통해 변경 사항을 추적하고, 필요한 RenderObject만 업데이트하는 구조이다.
이 구조 덕분에 Flutter는 rebuild가 자주 발생해도 높은 성능을 유지할 수 있다.