Engine, Framework & Embedder #

Dalam proses pengembangan aplikasi menggunakan Flutter, kita sering kali mengagumi bagaimana kode Dart yang kita tulis dapat berjalan dengan sangat mulus dan konsisten di berbagai sistem operasi yang berbeda seperti Android, iOS, Windows, macOS, Linux, hingga Web. Keajaiban ini bukanlah hasil dari trik penerjemahan kode sederhana, melainkan buah dari arsitektur perangkat lunak yang dirancang dengan sangat matang dan terstruktur. Arsitektur Flutter dibangun di atas tiga lapisan utama yang saling berkolaborasi erat: Framework (ditulis dalam Dart), Engine (ditulis dalam C++), dan Platform Embedder (ditulis dalam bahasa native masing-masing platform). Dengan memahami peran, batasan, dan cara kerja dari ketiga lapisan ini, kita akan mampu mendiagnosis masalah performa secara lebih akurat, merancang integrasi native yang optimal, serta mengapresiasi efisiensi di balik rendering visual Flutter yang luar biasa cepat.

Gambaran Besar: “Layer Cake” Flutter #

Struktur arsitektur Flutter sering kali dianalogikan sebagai kue lapis (layer cake) di mana setiap lapisan memiliki tanggung jawab yang terdefinisi dengan jelas dan hanya bergantung pada lapisan di bawahnya secara langsung. Prinsip desain ini sangat krusial karena memastikan bahwa perubahan pada satu lapisan tidak akan merusak lapisan lainnya secara tidak sengaja.

Berbeda dengan framework lintas platform tradisional yang bertindak sebagai pembungkus (wrapper) atau jembatan (bridge) untuk widget native bawaan sistem operasi, Flutter mengambil pendekatan radikal: Direct Rendering. Flutter memintas seluruh widget native dan menggambar setiap piksel UI-nya sendiri di atas kanvas kosong yang disediakan oleh platform. Oleh karena itu, arsitektur di bawah ini dirancang untuk mendukung rendering mandiri tersebut secara konsisten di semua lingkungan sistem operasi.

flowchart TD
    subgraph AppLayer["Lapisan Aplikasi (Dart)"]
        App["Aplikasi Kita"]
    end
    subgraph FrameworkLayer["Flutter Framework (Dart)"]
        direction TB
        Material["Material & Cupertino (UI Kit)"]
        Widgets["Widgets (Elemen UI)"]
        Rendering["Rendering (Layout & Paint)"]
        Services["Services (Animation, Painting, Gestures)"]
        Foundation["Foundation (Utilitas & Binding)"]
        Material --> Widgets
        Widgets --> Rendering
        Rendering --> Services
        Services --> Foundation
    end
    subgraph EngineLayer["Flutter Engine (C++)"]
        direction TB
        DartRun["Dart Runtime & VM Isolate"]
        RenderEngine["Rendering Engine (Impeller / Skia)"]
        TextLayout["Text Layout (SkParagraph / HarfBuzz)"]
        PlatformChannels["Platform Channels Binding"]
        IOServices["System Services (File & Network I/O)"]
    end
    subgraph EmbedderLayer["Platform Embedder (Native)"]
        direction TB
        AndroidEmb["Android Embedder (Java / Kotlin + C++)"]
        IOSEmb["iOS Embedder (Swift / Obj-C)"]
        DesktopEmb["Desktop Embedder (C++)"]
        WebEmb["Web Embedder (JS / WebAssembly)"]
    end
    
    AppLayer --> FrameworkLayer
    FrameworkLayer -->|"dart:ui API"| EngineLayer
    EngineLayer --> EmbedderLayer
    EmbedderLayer -->|"Sistem Operasi (Android, iOS, macOS, Windows, Linux, Web)"| OS["OS & Hardware"]

    style FrameworkLayer stroke:#0288d1,stroke-width:2px
    style EngineLayer stroke:#388e3c,stroke-width:2px
    style EmbedderLayer stroke:#f57c00,stroke-width:2px

Arsitektur berlapis ini memberikan fleksibilitas yang luar biasa. Sebagai contoh, jika kita ingin menjalankan aplikasi Flutter di perangkat jam tangan pintar berbasis Tizen atau sistem tertanam (embedded system) pada dasbor otomotif, kita tidak perlu memodifikasi Framework (Dart) maupun Engine (C++). Kita hanya perlu menulis Platform Embedder baru yang mampu menginisialisasi Engine dan menyediakan permukaan rendering (rendering surface) yang sesuai untuk perangkat tersebut.


Lapisan 1: Flutter Framework (Dart) #

Flutter Framework adalah lapisan teratas yang berisi pustaka-pustaka yang kita gunakan sehari-hari untuk menyusun antarmuka aplikasi. Lapisan ini ditulis 100% menggunakan bahasa Dart dan berjalan sepenuhnya di atas Dart VM (dalam mode debug) atau biner mesin hasil kompilasi AOT (dalam mode rilis). Karena ditulis dalam Dart, kita dapat dengan mudah menelusuri kode sumber internal framework langsung dari editor kita (IDE) untuk mempelajari cara kerjanya, melakukan sub-classing, atau bahkan mengganti komponen tertentu dengan implementasi kustom jika diperlukan.

Framework ini dibagi menjadi beberapa sub-lapisan penting dari bawah ke atas:

Foundation #

Ini adalah fondasi terbawah dari framework yang menyediakan utilitas, kelas dasar, dan abstraksi fundamental untuk seluruh pustaka di atasnya. Di dalam Foundation, kita akan menemukan kelas-kelas penting seperti ChangeNotifier dan ValueNotifier untuk manajemen state berbasis notifikasi perubahan, Listenable untuk objek yang dapat diobservasi, serta DiagnosticsNode yang memungkinkan Flutter Inspector mengekspos struktur pohon widget secara visual untuk mempermudah debugging. Foundation juga menyediakan pengikatan layanan (service bindings) seperti WidgetsBinding yang menghubungkan siklus hidup framework dengan engine.

Animation, Painting & Gestures #

Satu tingkat di atas Foundation adalah kumpulan layanan sistem inti yang menangani aspek animasi, penggambaran grafis, dan interaksi pengguna:

  • Animation: Menyediakan kelas AnimationController, Tween, dan Curve. Siklus pembaruan nilai animasi dikendalikan oleh objek Ticker, yang menyinkronkan dirinya secara presisi dengan sinyal laju penyegaran layar (refresh rate) perangkat melalui sinyal VSync.
  • Painting: Menyediakan abstraksi tingkat tinggi untuk menggambar di atas layar, termasuk definisi kelas Canvas, Paint, Border, Decoration, dan TextStyle. Kelas ini mempermudah proses penggambaran elemen UI tanpa harus berurusan dengan instruksi piksel mentah.
  • Gestures: Menangani konversi koordinat sentuhan mentah menjadi event interaktif. Di sini terdapat sistem Gesture Arena, sebuah protokol pencocokan pola gestur yang memecahkan konflik saat ada beberapa gestur yang berkompetisi secara bersamaan (misalnya, menentukan apakah sentuhan jari pengguna merupakan ketukan biasa (tap), geseran vertikal untuk menggulir halaman, atau cubitan untuk memperbesar gambar).

Rendering #

Lapisan Rendering bertanggung jawab penuh atas tata letak (layout) dan penggambaran (painting) elemen-elemen visual. Di lapisan inilah pohon objek rendering (RenderObject Tree) dibangun dan dikelola. Setiap RenderObject memiliki tugas menghitung ukuran fisiknya sendiri berdasarkan batasan tata letak yang diberikan oleh induknya (layout constraints), menentukan posisi koordinatnya, dan menggambar dirinya sendiri ke kanvas. Lapisan ini dikelola oleh PipelineOwner, yang mengatur alur eksekusi rendering mulai dari fase tata letak, komposisi layer, hingga penggambaran aktual.

Widgets #

Widgets adalah lapisan deklaratif yang menyederhanakan interaksi kita dengan lapisan Rendering. Sebagai pengembang, kita jarang berinteraksi langsung dengan RenderObject karena kodenya sangat imperatif dan kompleks. Sebagai gantinya, kita menggunakan Widget — sebuah deskripsi konfigurasi UI yang bersifat kekal (immutable) dan sangat ringan. Lapisan Widgets bertanggung jawab mengelola siklus hidup widget dan menerjemahkannya menjadi pohon elemen (Element Tree), yang bertindak sebagai jembatan logis sebelum instansi RenderObject dibuat atau diperbarui di memori.

Material & Cupertino #

Ini adalah lapisan teratas dari framework yang menyediakan pustaka widget siap pakai dengan panduan desain visual tertentu. Material mengimplementasikan bahasa desain Material Design milik Google, sementara Cupertino mengimplementasikan Human Interface Guidelines milik Apple. Di sini kita dapat menggunakan widget kompleks seperti Scaffold, AppBar, CupertinoNavigationBar, dan ElevatedButton secara instan dengan transisi visual dan efek animasi yang sesuai dengan standar platform.

Berikut adalah contoh penggunaan CustomPainter yang memanfaatkan API dari lapisan painting dan rendering secara langsung untuk menggambar elemen visual kustom:

import 'package:flutter/widgets.dart';

// BENAR: Menggunakan CustomPainter untuk menggambar tingkat rendah dengan Canvas
class CustomCirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = const Color(0xFF0288D1)
      ..style = PaintingStyle.fill;
    
    // Menggambar lingkaran di tengah kanvas
    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      size.width / 4,
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant CustomCirclePainter oldDelegate) => false;
}
Semua Lapisan Framework Bersifat Opsional Sebagai pengembang, kita bebas untuk mengganti atau memodifikasi setiap bagian dari framework ini. Jika kita tidak menyukai pustaka widget Material atau Cupertino, kita dapat membuat pustaka widget kita sendiri langsung di atas lapisan Widgets. Bahkan kita dapat memintas lapisan Widgets seluruhnya dan menggambar UI secara langsung menggunakan lapisan Rendering atau Painting jika diperlukan.

Lapisan 2: Flutter Engine (C++) #

Jika Flutter Framework adalah otak yang memproses struktur visual aplikasi, maka Flutter Engine adalah jantung mekanis yang memompa instruksi-instruksi tersebut menjadi piksel nyata pada perangkat keras. Engine ditulis hampir seluruhnya menggunakan bahasa C++ (dengan beberapa bagian kecil dalam Rust, Assembly, dan Objective-C) dan dikompilasi secara spesifik untuk arsitektur target masing-masing CPU perangkat (seperti ARMv7 atau ARM64 untuk ponsel cerdas, serta x64 atau ARM64 untuk komputer desktop).

Engine ini membungkus dan mengelola beberapa sub-sistem vital berikut:

Dart Runtime & Compile Toolchain #

Engine bertanggung jawab untuk membungkus dan menjalankan Dart VM (Virtual Machine) yang mengeksekusi kode Dart aplikasi kita. Dart VM menyediakan runtime lengkap yang mencakup alokasi memori dinamis dan Garbage Collector (GC) generasi baru yang sangat cepat, yang secara khusus dioptimalkan untuk menangani pembuatan dan penghancuran ribuan objek widget pendek yang terjadi setiap detik dalam siklus render Flutter. Selain itu, compiler JIT (Just-In-Time) berjalan di dalam engine selama fase pengembangan untuk memicu Hot Reload, sedangkan runtime AOT (Ahead-Of-Time) mengeksekusi biner mesin hasil kompilasi statis ketika aplikasi berjalan dalam mode produksi.

Rendering Subsystem (Skia vs Impeller) #

Tugas paling berat dari Engine adalah menerjemahkan instruksi visual tingkat tinggi dari Framework menjadi piksel aktual pada layar GPU. Selama bertahun-tahun, Flutter menggunakan Skia sebagai engine grafis 2D default-nya. Namun, mulai versi Flutter modern, arsitektur rendering ini digantikan oleh Impeller, yang dirancang khusus dari nol untuk mengatasi kelemahan mendasar Skia.

Masalah Utama Skia: Shader Compilation Jank #

Skia menggunakan pendekatan Immediate Mode Rendering dan mengandalkan OpenGL atau Vulkan melalui lapisan abstraksi yang dinamis. Ketika aplikasi pertama kali menampilkan animasi tertentu (misalnya transisi halaman baru), Skia harus menulis dan mengompilasi program grafis kecil untuk GPU yang disebut Shader secara dinamis saat aplikasi berjalan (runtime compilation). Proses kompilasi ini membutuhkan waktu sekitar 10 hingga 50 milidetik. Karena target rendering kita untuk 60 FPS adalah 16,6 milidetik per frame (dan hanya 8,3 milidetik untuk 120 FPS), jeda kompilasi shader ini akan melampaui anggaran waktu frame, menyebabkan layar tersendat (stuttering atau jank) yang sangat mengganggu kenyamanan pengguna.

Solusi Revolusioner Impeller: Kompilasi AOT Shader #

Impeller memecahkan masalah ini dengan memindahkan seluruh proses kompilasi shader dari fase runtime ke fase kompilasi aplikasi (build time). Dengan menggunakan compiler shader khusus bernama impellerc, Impeller menerjemahkan semua shader GLSL menjadi biner shader GPU (MSL untuk iOS/Metal, SPIR-V untuk Android/Vulkan) sebelum paket aplikasi didistribusikan. Ketika aplikasi dijalankan, Impeller tinggal memuat biner shader yang sudah siap pakai tersebut tanpa melakukan kompilasi dinamis lagi.

Selain itu, Impeller memanfaatkan API grafis modern secara langsung (seperti Metal di iOS dan Vulkan di Android) untuk meminimalkan overhead driver GPU, mendukung rendering multi-thread yang efisien, dan mengadopsi model Retained Mode Rendering yang melacak serta memperbarui perubahan status visual secara cerdas.

Parameter PerbandinganSkiaImpeller
Fase Kompilasi ShaderDinamis saat runtime (memicu jank)Ahead-Of-Time (AOT) saat build time (bebas jank)
API Grafis UtamaOpenGL (Legacy), Vulkan, MetalMetal (iOS/macOS), Vulkan & GLES (Android)
Model RenderingImmediate Mode RenderingRetained Mode Rendering
Pemanfaatan Multi-threadingTerbatas pada thread raster tunggalSkalabel di berbagai thread GPU
Dukungan Platform DefaultWeb (CanvasKit)iOS (Flutter 3.29+) & Android API 29+ (Flutter 3.27+)
flowchart TD
    subgraph SkiaPipeline["Alur Rendering Skia (Runtime Compilation)"]
        direction TB
        SkiaShader["Shader GLSL (Runtime)"] -->|"Kompilasi GPU Dinamis"| GPUCompile["Kompilasi di Perangkat (Runtime)"]
        GPUCompile -->|"Jeda Frame (Jank)"| GPUExecSkia["Eksekusi GPU"]
    end
    subgraph ImpellerPipeline["Alur Rendering Impeller (Build-time Compilation)"]
        direction TB
        ImpellerShader["Shader GLSL (Build Time)"] -->|"Kompilasi AOT (Impellerc)"| Precompiled["Pre-compiled Shader (MSL/SPIR-V)"]
        Precompiled -->|"Distribusi dalam APK/IPA"| LoadReady["Muat Instan ke GPU"]
        LoadReady -->|"Eksekusi Mulus (Bebas Jank)"| GPUExecImpeller["Eksekusi GPU"]
    end
    
    style SkiaPipeline stroke:#f44336,stroke-width:2px
    style ImpellerPipeline stroke:#4caf50,stroke-width:2px

Komponen Engine Lainnya #

  • Text Layout & Shaping: Menggunakan subsistem teks modern bernama SkParagraph yang mengintegrasikan perpustakaan HarfBuzz untuk shaping teks unicode dan ICU untuk analisis ortografi. Subsistem ini memastikan bahwa font dan aksara kompleks (seperti Arab, Jepang, atau Devanagari) dirender dengan benar.
  • File & Network I/O: Menyediakan API I/O asinkron tingkat rendah untuk Dart melalui perpustakaan internal C++.
  • Accessibility (A11y): Menyediakan jembatan untuk mengekspos struktur pohon semantik (Semantic Tree) dari Framework ke sistem pembaca layar (screen reader) native platform seperti TalkBack di Android atau VoiceOver di iOS.

dart:ui: Jembatan Menuju Framework #

Engine mengekspos seluruh fungsionalitas C++ miliknya ke kode Dart melalui perpustakaan bawaan bernama dart:ui. Perpustakaan ini berisi binding tingkat rendah untuk Canvas, Paragraph, dan PictureRecorder. Meskipun kita sebagai pengembang aplikasi disarankan untuk selalu menggunakan Widgets bawaan Framework, kita dapat mengakses dart:ui langsung jika kita ingin membangun sistem UI eksperimental atau melakukan manipulasi grafis ekstrem di luar batas normal.

Mari kita lihat bagaimana dart:ui dapat digunakan secara langsung tanpa bergantung pada sistem widget sama sekali:

import 'dart:ui' as ui;

// BENAR: Menggunakan API dart:ui secara langsung untuk menggambar ke objek Scene
void drawRawScene(ui.Canvas canvas) {
  final paint = ui.Paint()
    ..color = const ui.Color(0xFFE91E63)
    ..style = ui.PaintingStyle.fill;

  // Menggambar persegi panjang pink langsung di koordinat layar mentah
  canvas.drawRect(
    const ui.Rect.fromLTWH(50.0, 50.0, 200.0, 150.0),
    paint,
  );
}

// Catatan: Ini adalah interaksi tingkat terendah antara Dart dan C++ Engine.
// Framework membungkus kode semacam ini di dalam CustomPainter agar lebih aman dikelola.

Lapisan 3: Platform Embedder (Native) #

Setiap kali aplikasi Flutter dijalankan, sistem operasi perangkat tidak langsung berinteraksi dengan Dart VM atau C++ Engine. Sebagai gantinya, sistem operasi meluncurkan sebuah aplikasi native standar yang bertindak sebagai host aplikasi. Host ini disebut Platform Embedder.

Platform Embedder ditulis menggunakan bahasa pemrograman bawaan dari platform target:

  • Android: ditulis menggunakan Java atau Kotlin, serta lem C++ (JNI).
  • iOS & macOS: ditulis menggunakan Objective-C atau Swift, dengan layer Metal API.
  • Windows: ditulis menggunakan C++ (Win32 API).
  • Linux: ditulis menggunakan C++ (GTK+ API).
  • Web: ditulis menggunakan JavaScript dan WebAssembly (Wasm).

Embedder-lah yang bertindak sebagai jembatan fisik antara Flutter Engine dan perangkat keras, dengan tanggung jawab utama sebagai berikut:

Menyediakan Rendering Surface #

Embedder bertanggung jawab menciptakan area visual terakselerasi perangkat keras (Metal layer di iOS, SurfaceView atau TextureView di Android, GLFW/Win32 Window di desktop) dan menyerahkannya ke Flutter Engine. Piksel yang dirasterisasi oleh Engine akan disajikan ke layar melalui area visual ini.

Mengelola Threading & Task Runners #

Untuk menjaga performa rendering tetap berada di angka 60 FPS or lebih tanpa mengalami lag, Flutter Engine mengandalkan model threading yang sangat ketat. Platform Embedder bertugas mengalokasikan dan mengelola empat jenis Task Runner (thread eksekusi) berikut:

  • UI Task Runner: Digunakan untuk mengeksekusi Dart VM isolate utama kita. Di sinilah pohon widget dibangun, layout dihitung, state diperbarui, dan logika aplikasi berjalan. Thread ini menghasilkan Layer Tree (scene grafis) yang mendeskripsikan apa yang harus digambar.
  • Raster Task Runner (GPU Runner): Thread ini bertugas mengambil Layer Tree dari UI Task Runner dan menerjemahkannya menjadi serangkaian instruksi GPU (Metal/Vulkan commands) menggunakan Impeller/Skia. Thread ini berjalan secara paralel dengan UI Task Runner agar perhitungan UI berikutnya tidak terhambat oleh proses rasterisasi frame sebelumnya.
  • IO Task Runner: Digunakan untuk melakukan operasi berat di latar belakang, seperti memuat dan mendekode gambar dari penyimpanan internal, membaca file besar, atau mengunduh aset dari internet. Setelah aset didekode menjadi tekstur GPU mentah oleh thread ini, tekstur tersebut langsung dikirim ke Raster Task Runner untuk dirender tanpa membebani thread UI.
  • Platform Task Runner: Thread utama dari sistem operasi native (UI thread asli Android/iOS). Thread ini digunakan untuk menangani interaksi dengan API sistem operasi, memproses pesan dari Platform Channels, mengelola event daur hidup (lifecycle), dan menerima event masukan fisik (seperti sentuhan atau ketikan keyboard).
flowchart TD
    subgraph Embedder["Platform Embedder (Pengelola Thread)"]
        direction TB
        PlatformRunner["Platform Task Runner (Main Native Thread)"]
        UIRunner["UI Task Runner (Dart Isolate VM)"]
        RasterRunner["Raster Task Runner (GPU Commands)"]
        IORunner["IO Task Runner (Asset Loading & I/O)"]
    end

    PlatformRunner <-->|"Kirim Sentuhan / Platform Channels"| UIRunner
    UIRunner -->|"Hasilkan Layer Tree (Scene)"| RasterRunner
    RasterRunner -->|"Kirim ke GPU"| GPU["GPU Hardware"]
    IORunner -->|"Unggah Tekstur Siap Pakai"| RasterRunner
    UIRunner <-->|"Delegasi Tugas Berat"| IORunner

    style PlatformRunner stroke:#0288d1,stroke-width:2px
    style UIRunner stroke:#4caf50,stroke-width:2px
    style RasterRunner stroke:#e91e63,stroke-width:2px
    style IORunner stroke:#ff9800,stroke-width:2px

Menerjemahkan Masukan Fisik (Input Handling) #

Embedder menangkap event sentuhan jari, koordinat mouse, gerakan scroll, dan input keyboard dari sistem operasi native. Event ini dikemas menjadi paket data seragam bernama PointerDataPacket lalu dikirim ke Flutter Engine via dart:ui, yang selanjutnya didistribusikan ke lapisan Gestures di Framework.

Mengatur Siklus Hidup Aplikasi (App Lifecycle) #

Ketika sistem operasi memberitahu host bahwa aplikasi berpindah ke latar belakang (backgrounded), Platform Embedder meneruskan sinyal daur hidup ini ke Flutter Engine, memungkinkan Dart VM untuk melakukan pembersihan memori (garbage collection) ekstra atau menjeda timer aplikasi secara otomatis.

Integrasi Modul: Add-to-App & FlutterEngineGroup #

Karena Platform Embedder dirancang sebagai kontainer mandiri, kita tidak perlu memigrasikan seluruh aplikasi native kita ke Flutter sekaligus. Kita dapat membungkus layar Flutter sebagai modul pustaka (.aar atau framework) lalu menambahkannya ke dalam aplikasi Android/iOS native yang sudah ada.

Untuk skenario di mana aplikasi native memiliki beberapa modul Flutter terpisah (misalnya halaman profil dan halaman pembayaran menggunakan Flutter, sementara halaman utama native), instansiasi beberapa Engine Flutter secara mentah akan memakan banyak RAM (sekitar 30-40 MB per engine). Untuk mengatasi ini, Flutter menyediakan FlutterEngineGroup. Melalui FlutterEngineGroup, beberapa instance embedder dapat berbagi VM Dart, cache internal, dan isolates runtime yang sama, sehingga penggunaan memori untuk engine kedua dan seterusnya hanya berkisar 1-2 MB saja. Hal ini memungkinkan integrasi native yang sangat efisien.


Alur Kerja dan Interaksi Antar Lapisan #

Untuk memahami bagaimana ketiga lapisan besar ini menari bersama dalam harmoni yang sinkron, mari kita amati siklus hidup satu buah interaksi sentuhan layar (tap event) dari saat jari pengguna menyentuh layar fisik hingga piksel UI baru digambar di layar:

  1. Sentuhan Fisik: Pengguna menyentuh tombol di layar ponsel pintar.
  2. Deteksi Hardware: Panel digitizer layar mendeteksi sentuhan tersebut dan mengirimkan sinyal interupsi perangkat keras ke Sistem Operasi.
  3. Penyampaian Embedder: Sistem Operasi native (Android/iOS) meneruskan event sentuhan tersebut ke Platform Embedder (dalam bentuk MotionEvent di Android atau UITouch di iOS).
  4. Pengemasan Engine: Platform Embedder menerjemahkan koordinat sentuhan mentah tersebut menjadi objek PointerDataPacket terstandar, lalu mengirimkannya ke Flutter Engine.
  5. Resolusi Gestur: Engine meneruskan paket sentuhan tersebut ke Flutter Framework melalui binding dart:ui. Lapisan Gestures menjalankan hit-testing dan mengaktifkan Gesture Arena untuk menentukan bahwa pengguna sedang melakukan gestur ketukan (tap) pada koordinat tombol tersebut, lalu memicu fungsi callback onPressed yang telah kita tentukan.
  6. Perubahan State: Di dalam fungsi callback onPressed, kode Dart kita mengubah state variabel (misalnya mengubah warna tombol) lalu memicu pembaruan state dengan memanggil setState().
  7. Penjadwalan Frame: Framework menandai elemen widget tersebut sebagai kotor (dirty) dan memanggil WidgetsBinding.scheduleFrame() untuk memberitahu Engine bahwa ada perubahan visual yang perlu digambar ulang.
  8. Sinkronisasi VSync: Engine meminta sinyal VSync dari Platform Embedder. Embedder meneruskan permintaan ini ke OS. Ketika siklus VSync layar berikutnya dipicu oleh OS (misalnya, pada interval 16,6ms untuk layar 60Hz), OS mengirimkan sinyal balik ke Embedder, yang kemudian memicu event render pada Engine.
  9. Callback Menggambar: Engine memanggil fungsi onBeginFrame dan onDrawFrame pada Framework. Framework mengeksekusi proses pembangunan kembali widget (rebuild), melakukan perhitungan tata letak (layout) dari atas ke bawah untuk menghitung dimensi baru tombol, dan merekam instruksi penggambaran visual baru (painting) menjadi objek pohon lapisan (Layer Tree).
  10. Pengiriman Scene: Framework mengirimkan Layer Tree yang berisi koordinat dan instruksi gambar tombol yang baru ke Engine menggunakan kelas ui.SceneBuilder.
  11. Rasterisasi GPU: Engine mengambil Layer Tree tersebut, merujuk pada biner shader yang telah dikompilasi sebelumnya oleh Impeller, lalu merasterisasi instruksi visual tersebut menjadi instruksi GPU mentah (Metal/Vulkan/OpenGL commands) di dalam Raster Task Runner.
  12. Presentasi Layar: Instuksi GPU dieksekusi oleh kartu grafis perangkat untuk menulis frame baru ke dalam framebuffer permukaan rendering. Platform Embedder meminta sistem operasi untuk membalikkan buffer visual (swap buffers), menyajikan piksel baru tombol dengan warna yang telah berubah ke layar fisik pengguna secara instan.
sequenceDiagram
    autonumber
    actor User as Pengguna
    participant OS as Sistem Operasi
    participant Emb as Platform Embedder
    participant Eng as Flutter Engine
    participant FW as Flutter Framework

    User->>OS: Sentuh layar (Tap)
    OS->>Emb: Teruskan event input koordinat
    Emb->>Eng: Konversi koordinat & kirim PointerDataPacket
    Eng->>FW: Kirim event ke Gesture Arena (dart:ui)
    FW->>FW: Deteksi gestur & panggil onTap() callback
    FW->>FW: Update State (setState)
    FW->>FW: Tandai Widget kotor & jadwalkan frame baru
    FW->>Eng: Minta VSync frame baru
    Eng->>Emb: Minta VSync dari OS
    OS-->>Emb: Sinyal VSync (Frame Trigger)
    Emb-->>Eng: Memicu render frame baru
    Eng-->>FW: Callback menggambar (onBeginFrame & onDrawFrame)
    FW->>FW: Rebuild, Layout, & Paint (Hasilkan Layer Tree)
    FW->>Eng: Kirim Scene/Layer Tree via SceneBuilder
    Eng->>Eng: Rasterisasi Scene dengan Impeller/Skia
    Eng->>Emb: Render frame ke platform surface buffer
    Emb->>OS: Sajikan buffer ke layar GPU
    OS-->>User: Tampilan UI berubah (~16.6ms / 60 FPS)

Ringkasan #

  • Tiga Lapisan Utama — Arsitektur Flutter terdiri dari Framework (Dart), Engine (C++), dan Platform Embedder (Native) yang masing-masing memiliki batas tanggung jawab yang terisolasi dengan rapi.
  • Pendekatan Direct Rendering — Berbeda dengan framework cross-platform tradisional, Flutter menggambar setiap piksel antarmuka penggunanya sendiri di atas kanvas kosong tanpa mengonversi kodenya menjadi widget native bawaan sistem operasi.
  • Sub-sistem Framework — Lapisan Framework mengalir dari atas ke bawah: Material/Cupertino (Design System), Widgets (Elemen Deklaratif), Rendering (Layout & Paint), Animation/Painting/Gestures, dan Foundation (Utilitas Dasar).
  • Sub-sistem Engine — Lapisan Engine ditulis dalam C++ untuk mengelola eksekusi Dart VM runtime, rendering piksel (Impeller/Skia), shaping teks (SkParagraph), dan operasi I/O tingkat rendah.
  • Impeller vs Skia — Impeller menggantikan Skia dengan memindahkan proses kompilasi shader dari runtime ke build time (AOT), menyelesaikan masalah shader compilation jank (stuttering) sepenuhnya pada perangkat modern.
  • dart:ui Binding — Engine mengekspos fungsi grafis tingkat rendah miliknya ke framework melalui perpustakaan dart:ui, yang bertindak sebagai jembatan komunikasi utama.
  • Platform Embedder — Lapisan native yang bertindak sebagai host aplikasi, mengelola alokasi thread (UI, Raster, IO, Platform), menangani input sentuhan fisik, dan lifecycle aplikasi.
  • Portabilitas & Modul — Melalui Platform Embedder, Flutter dapat diintegrasikan sebagai modul (Add-to-App) ke aplikasi native yang ada dan dioptimalkan konsumsi memorinya menggunakan FlutterEngineGroup.

← Sebelumnya: Dart Language   Berikutnya: Architecture Overview →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact