Skip to Content
Flutter⚡ Nâng cao🌳 Widget, Element & Render Trees

Widget, Element và Render Trees

Flutter quản lý UI thông qua ba loại tree hoạt động song song. Hiểu rõ chúng giúp bạn viết code hiệu quả và debug dễ dàng hơn.


1. Tổng quan ba Trees

┌─────────────────────────────────────────────────────────────────────┐ │ Flutter's Three Trees │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ WIDGET TREE ELEMENT TREE RENDER TREE │ │ (Configuration) (Lifecycle) (Layout & Paint) │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Widget │ creates │ Element │ creates │ Render │ │ │ │ A │ ───────▶ │ A │ ───────▶ │ Object │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ │ │ Widget │ │ Element │ │ Render │ │ │ │ B │ │ B │ │ Object │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ Immutable Mutable Handles │ │ Lightweight Long-lived Painting │ │ Cheap to create Expensive Layout │ │ │ └─────────────────────────────────────────────────────────────────────┘

2. Widget Tree - Bản thiết kế

Widgetimmutable configuration - mô tả UI trông như thế nào.

┌──────────────────────────────────────────────────┐ │ Widget Tree │ ├──────────────────────────────────────────────────┤ │ │ │ MaterialApp │ │ │ │ │ Scaffold │ │ │ │ │ ┌──────────┼──────────┐ │ │ │ │ │ │ │ AppBar Body FAB │ │ │ │ │ │ Text Column │ │ │ │ │ ┌──────┴──────┐ │ │ │ │ │ │ Text Container │ │ │ │ │ Text │ │ │ ├──────────────────────────────────────────────────┤ │ Đặc điểm: │ │ • Immutable (bất biến) │ │ • Lightweight (nhẹ) │ │ • Được tạo mới mỗi khi rebuild │ │ • Mô tả "WHAT" không phải "HOW" │ └──────────────────────────────────────────────────┘

Widget chỉ là configuration

// Widget KHÔNG phải UI thật // Widget chỉ là "công thức" để tạo UI Container( width: 100, // Chỉ là config height: 100, // Chỉ là config color: Colors.red // Chỉ là config )

3. Element Tree - Người quản lý

Elementcầu nối giữa Widget và RenderObject. Element:

  • Giữ vị trí trong tree
  • Quản lý lifecycle
  • Quyết định rebuild hay reuse
┌──────────────────────────────────────────────────┐ │ Element Tree │ ├──────────────────────────────────────────────────┤ │ │ │ Widget (new) Element (persistent) │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ │ │ │Container│──creates──▶│ Element │ │ │ │ w=100 │ │(giữ ref │ │ │ └─────────┘ │to Widget│ │ │ │ │& Render)│ │ │ │ └─────────┘ │ │ │ rebuild │ │ │ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ │ │ │Container│──updates──▶│ Element │ (REUSED!) │ │ │ w=200 │ │(cập nhật│ │ │ └─────────┘ │ config) │ │ │ └─────────┘ │ │ │ ├──────────────────────────────────────────────────┤ │ Đặc điểm: │ │ • Mutable (có thể thay đổi) │ │ • Long-lived (tồn tại lâu) │ │ • Giữ reference đến Widget và RenderObject │ │ • Quyết định update hay recreate RenderObject │ └──────────────────────────────────────────────────┘

Element Types

┌────────────────────────────────────────────┐ │ Element Types │ ├────────────────────────────────────────────┤ │ │ │ ComponentElement │ │ └── Cho StatelessWidget, StatefulWidget │ │ └── Không có RenderObject riêng │ │ └── Ủy quyền cho children │ │ │ │ RenderObjectElement │ │ └── Cho RenderObjectWidget │ │ └── Có RenderObject riêng │ │ └── Container, Row, Column, etc. │ │ │ └────────────────────────────────────────────┘

4. Render Tree - Người vẽ

RenderObject thực hiện layout và painting thực sự.

┌──────────────────────────────────────────────────┐ │ Render Tree │ ├──────────────────────────────────────────────────┤ │ │ │ RenderView (root) │ │ │ │ │ RenderPositionedBox │ │ │ │ │ RenderDecoratedBox │ │ │ │ │ RenderPadding │ │ │ │ │ RenderFlex (Column) │ │ │ │ │ ┌─────────┴─────────┐ │ │ │ │ │ │ RenderParagraph RenderConstrainedBox │ │ (Text widget) (Container) │ │ │ ├──────────────────────────────────────────────────┤ │ Đặc điểm: │ │ • Thực hiện LAYOUT (tính toán size, position) │ │ • Thực hiện PAINT (vẽ pixels lên canvas) │ │ • Expensive (tốn tài nguyên) │ │ • Được reuse càng nhiều càng tốt │ └──────────────────────────────────────────────────┘

Layout Protocol: Constraints & Sizes

CONSTRAINTS GO DOWN ┌─────────────────────────────────────────────────┐ │ Parent: "Con có thể rộng 0-300, cao 0-∞" │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────┐ │ │ │ Child │ │ │ │ "OK, tôi sẽ rộng 200, cao 100" │ │ │ └─────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Parent nhận size và đặt position │ └─────────────────────────────────────────────────┘ SIZES GO UP

5. Luồng hoạt động khi Rebuild

┌─────────────────────────────────────────────────────────────────┐ │ Rebuild Process │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. setState() called │ │ │ │ │ ▼ │ │ 2. Widget được tạo MỚI (immutable) │ │ │ │ │ ▼ │ │ 3. Element so sánh old Widget vs new Widget │ │ │ │ │ ┌────┴────┐ │ │ │ │ │ │ SAME DIFFERENT │ │ TYPE? TYPE? │ │ │ │ │ │ ▼ ▼ │ │ UPDATE CREATE │ │ existing new Element │ │ Element & RenderObject │ │ │ │ │ ▼ │ │ 4. RenderObject được update (nếu cần) │ │ │ │ │ ▼ │ │ 5. Layout phase (nếu constraints thay đổi) │ │ │ │ │ ▼ │ │ 6. Paint phase (nếu visual thay đổi) │ │ │ └─────────────────────────────────────────────────────────────────┘

6. Tại sao thiết kế này hiệu quả?

Widget nhẹ, tạo nhanh

┌──────────────────────────────────────────┐ │ Widget = Configuration │ ├──────────────────────────────────────────┤ │ │ │ class Text { │ │ final String data; // Nhẹ │ │ final TextStyle? style; // Nhẹ │ │ } │ │ │ │ → Tạo hàng ngàn widgets = OK │ │ → Tạo mới mỗi frame = OK │ │ │ └──────────────────────────────────────────┘

Element & RenderObject được tái sử dụng

┌──────────────────────────────────────────┐ │ Element = Persistent │ ├──────────────────────────────────────────┤ │ │ │ Frame 1: Widget A(color: red) │ │ │ │ │ ▼ │ │ Element A ←── RenderObject A │ │ │ │ Frame 2: Widget A(color: blue) │ │ │ │ │ ▼ │ │ Element A ←── RenderObject A │ │ (REUSED!) (UPDATED!) │ │ │ │ → Không cần tạo lại từ đầu │ │ → Chỉ update thuộc tính thay đổi │ │ │ └──────────────────────────────────────────┘

7. Điều kiện để Element được tái sử dụng

┌─────────────────────────────────────────────────────────────┐ │ Element Reuse Conditions │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Element CŨ có thể reuse NẾU: │ │ │ │ 1. widget.runtimeType == old.runtimeType │ │ → Cùng loại widget │ │ │ │ 2. widget.key == old.key │ │ → Cùng key (hoặc cả hai đều null) │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Container(color: red) → Container(color: blue) │ │ │ │ │ │ │ │ Same type? ✓ │ │ │ │ Same key? ✓ (both null) │ │ │ │ Result: REUSE Element, UPDATE RenderObject │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Container(color: red) → Text('Hello') │ │ │ │ │ │ │ │ Same type? ✗ │ │ │ │ Result: CREATE new Element & RenderObject │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘

8. Debug với Flutter DevTools

┌─────────────────────────────────────────────────────────────┐ │ Flutter DevTools │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Widget Inspector: │ │ └── Xem Widget Tree │ │ └── Highlight repaints │ │ └── Show guidelines │ │ │ │ Performance View: │ │ └── Frame timing │ │ └── Build/Layout/Paint phases │ │ └── Identify expensive rebuilds │ │ │ └─────────────────────────────────────────────────────────────┘

📝 Tóm tắt

TreeVai tròĐặc điểm
WidgetConfigurationImmutable, lightweight, cheap
ElementLifecycle managerMutable, persistent, bridges W↔R
RenderLayout & PaintExpensive, reused aggressively

Quy tắc vàng:

Widget được tạo mới liên tục → Element quyết định có reuse RenderObject hay không → RenderObject thực hiện công việc nặng.

Hệ quả cho developer:

  1. Đừng sợ tạo nhiều widgets - chúng rất nhẹ
  2. Dùng const widgets khi có thể
  3. Hiểu khi nào cần Key để Element matching đúng
Last updated on