Platform Embedder #

Platform Embedder adalah lapisan terendah di dalam arsitektur sistem Flutter yang langsung berkomunikasi dengan sistem operasi native perangkat. Tanpa adanya Platform Embedder, C++ Engine tidak akan memiliki kemampuan untuk menginisialisasi pustaka grafis, menangkap koordinat sentuhan jari, memutar suara, membaca berkas penyimpanan, atau berinteraksi dengan layanan dasar sistem operasi. Platform Embedder bertugas mendirikan wadah pelindung (host shell) native yang bertindak sebagai induk tempat Flutter Engine diletakkan dan dijalankan. Melalui desain inilah Flutter dapat berjalan secara konsisten di platform apa pun — dari ponsel pintar, komputer desktop, peramban web, hingga sistem tertanam kustom.

Peran Utama Embedder #

Secara fungsional, Platform Embedder memikul tanggung jawab penuh untuk bertindak sebagai perantara fisik antara Flutter Engine yang bersifat universal (platform-agnostic) dengan sistem operasi lokal yang bersifat spesifik.

Ada empat pilar peran utama yang ditangani oleh Platform Embedder:

flowchart TD
    subgraph Engine["Flutter Engine (Platform-Agnostic C++)"]
        EngCore["Core Engine (VM, Rasterizer, Compositor)"]
    end
    subgraph Embedder["Platform Embedder (Native Host)"]
        direction TB
        Surf["1. Rendering Surface (Metal / Vulkan / WebGL)"]
        Input["2. Input Translators (Touch coordinates -> PointerDataPacket)"]
        Life["3. Lifecycle Manager (OS state -> Flutter AppLifecycleState)"]
        Chan["4. Platform Channels Binding (JNI / Swift / C++ Message Router)"]
    end
    subgraph OS["Sistem Operasi & Perangkat Keras"]
        OSCore["Android, iOS, macOS, Windows, Linux, Web"]
    end
        
    Engine <-->|"ABI stabil (C API)"| Embedder
    Embedder <-->|"Sistem Driver & Window API"| OS
        
    style Engine stroke:#388e3c,stroke-width:2px
    style Embedder stroke:#f57c00,stroke-width:2px
  • Menyediakan Rendering Surface: Embedder meminta alokasi memori grafis dari sistem operasi (seperti metal layer di iOS atau window handle di Windows) lalu menyerahkan permukaan rendering tersebut kepada Engine untuk digambar.
  • Menerjemahkan Input & Event: Menangkap event masukan perangkat keras fisik (sentuhan digitizer, pergerakan mouse, penekanan keyboard) dan menerjemahkannya ke dalam koordinat logis terstandarisasi sebelum dikirim ke Engine.
  • Mengelola Siklus Hidup (Lifecycle): Mendengarkan sinyal transisi status aplikasi dari sistem operasi (seperti aplikasi diminimalkan atau ditutup) dan mengonversinya menjadi status daur hidup reaktif Flutter.
  • Menjembatani Layanan Sistem: Menyediakan pustaka binding perute pesan (message routing) asinkron yang melandasi fungsi Platform Channels untuk mengakses hardware native seperti modul GPS, kamera, dan sensor.

Bahasa Embedder per Platform #

Karena Platform Embedder bertindak sebagai host aplikasi native, ia ditulis menggunakan bahasa pemrograman resmi yang didukung secara default oleh ekosistem sistem operasi masing-masing target platform:

Platform TargetBahasa Pemrograman EmbedderRendering Surface Utama
AndroidJava / Kotlin + C++ (JNI)SurfaceView / TextureView
iOSSwift / Objective-C / Objective-C++CAMetalLayer (Metal API)
macOSSwift / Objective-C / Objective-C++NSView + CAMetalLayer (Metal)
WindowsC++ (Win32 API)HWND (Win32 Window Handle)
LinuxC++ (GTK+ / Wayland API)GTK Window / DRM Canvas
WebJavaScript / WebAssembly (Wasm)HTML5 Canvas + WebGL2/WebGPU

Inti penghubung di antara semua bahasa pemrograman native ini dengan C++ Engine adalah Embedder C API yang dideklarasikan secara stabil di dalam berkas header flutter_embedder.h. Pustaka ini menyediakan ABI (Application Binary Interface) C statis, yang menjamin bahwa kita dapat mengembangkan platform embedder kustom tanpa harus membongkar struktur kode internal C++ Engine.


Android Embedder #

Android Embedder adalah salah satu implementasi embedder yang paling matang dan kompleks karena Android merupakan platform target pertama Flutter di industri.

1. FlutterActivity vs FlutterFragment #

Android Embedder menyediakan dua kontainer native utama untuk menampilkan visual Flutter:

  • FlutterActivity: Sub-kelas dari Activity Android native standar. Ini adalah cara paling mudah untuk meluncurkan aplikasi Flutter karena FlutterActivity secara otomatis mengelola seluruh daur hidup inisialisasi engine, pembuatan view grafis, dan pembersihan memori di latar belakang.
  • FlutterFragment: Digunakan ketika kita ingin mengintegrasikan Flutter ke dalam aplikasi native Android yang sudah ada secara bertahap (Add-to-App). FlutterFragment dapat disisipkan ke dalam tata letak XML native berdampingan dengan view native lainnya.
// BENAR: Menggunakan FlutterFragment untuk menyisipkan layar Flutter ke dalam Activity Native
class DashboardActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dashboard)

        // Membuat instance fragment baru dan menyematkannya ke container XML
        val flutterFragment = FlutterFragment.withNewEngine()
            .initialRoute("/cart-summary")
            .build<FlutterFragment>()

        supportFragmentManager.beginTransaction()
            .replace(R.id.flutter_view_container, flutterFragment)
            .commit()
    }
}

2. SurfaceView vs TextureView #

Android Embedder mendukung dua pilihan rendering surface yang memiliki karakteristik performa yang berbeda:

flowchart TD
    subgraph AndroidSurface["Pilihan Surface Rendering Android"]
        direction TB
        SV["SurfaceView (Default)"] -->|"Dedicated Hardware Layer"| SV_P["Performa Tinggi & Tanpa Overdraw"]
        SV -->|"Batasan"| SV_C["Tidak Bisa Tumpang Tindih (Overlay) dengan View Native"]
              
        TV["TextureView (Fallback)"] -->|"Diberlakukan sebagai Regular View"| TV_P["Mendukung Transformasi & Overlay Native View"]
        TV -->|"Batasan"| TV_C["Konsumsi RAM Lebih Tinggi & Copy Texture per Frame"]
    end
          
    style SV stroke:#4caf50,stroke-width:2px
    style TV stroke:#f44336,stroke-width:2px

Secara default, Flutter menggunakan SurfaceView karena performa rendering visualnya jauh lebih cepat dan hemat daya. Kita hanya disarankan beralih ke TextureView secara eksplisit apabila kita perlu menyisipkan widget visual Flutter di bawah atau di atas komponen native lain (seperti menaruh tombol Flutter di atas peta native Google Maps).

3. Daur Hidup Android ke Status Aplikasi Flutter #

Embedder Android secara aktif memantau perubahan status aktivitas OS Android lalu memetakannya menjadi representasi status siklus hidup aplikasi (AppLifecycleState) di sisi Dart:

flowchart TD
    subgraph AndroidOS["Daur Hidup Android Activity"]
        direction TB
        A_Create["onCreate() / onStart()"]
        A_Resume["onResume()"]
        A_Pause["onPause()"]
        A_Stop["onStop()"]
        A_Destroy["onDestroy()"]
    end
    subgraph FlutterOS["Status Aplikasi Flutter (Dart)"]
        direction TB
        F_Resume["AppLifecycleState.resumed (Aktif)"]
        F_Inactive["AppLifecycleState.inactive (Transisi/Split-screen)"]
        F_Paused["AppLifecycleState.paused (Background)"]
        F_Detached["AppLifecycleState.detached (Dimatikan)"]
    end
          
    A_Create --> F_Resume
    A_Resume --> F_Resume
    A_Pause --> F_Inactive
    A_Stop --> F_Paused
    A_Destroy --> F_Detached
          
    style AndroidOS stroke:#0288d1,stroke-width:2px
    style FlutterOS stroke:#388e3c,stroke-width:2px

Di sisi Dart, kita dapat memantau perubahan ini secara asinkron menggunakan mixin WidgetsBindingObserver:

class AppStateObserver extends StatefulWidget {
  const AppStateObserver({super.key});

  @override
  State<AppStateObserver> createState() => _AppStateObserverState();
}

class _AppStateObserverState extends State<AppStateObserver> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this); // Daftarkan sebagai pengamat
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      // BENAR: Menjeda koneksi websocket atau menyimpan data draf ketika aplikasi masuk background
      saveUserSessionDraft();
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this); // Wajib dibersihkan
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => const SizedBox.shrink();
}

iOS Embedder #

iOS Embedder bertindak sebagai host aplikasi iOS native dengan memanfaatkan framework UIKit dan rendering Metal API (melalui subsistem grafis Impeller).

1. Inisialisasi FlutterViewController #

Layar utama Flutter di iOS dikemas dalam objek kelas FlutterViewController. Ketika kita menyajikan FlutterViewController ke layar, controller ini secara dinamis mengoordinasikan pembuatan Metal rendering surface (CAMetalLayer) untuk menampilkan piksel visual dari Engine.

// Swift: Menyajikan layar Flutter dari kode native iOS
import UIKit
import Flutter

class MenuViewController: UIViewController {
    func openSettingsPage() {
        let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
        let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
        
        // Membuka layar Flutter secara transisi modal
        self.present(flutterViewController, animated: true, completion: nil)
    }
}

2. Optimasi cold start: Pre-Warming Engine #

Inisialisasi C++ Engine baru memakan waktu sekitar 100-200ms karena sistem harus mendirikan VM Dart, mengalokasikan memori isolate, dan meluncurkan empat task runner. Jika pengguna menekan tombol lalu harus menunggu 200ms sebelum layar terbuka, aplikasi akan terasa tersendat (laggy).

Solusinya adalah dengan melakukan Pre-warming. Kita membuat instansi FlutterEngine lebih awal saat aplikasi pertama kali terbuka di dalam AppDelegate dan menyimpannya di memori cache, sehingga ketika FlutterViewController dipanggil, ia dapat langsung merender layar seketika tanpa jeda inisialisasi.

// AppDelegate.swift: Melakukan pre-warming Flutter Engine saat startup aplikasi iOS
import UIKit
import Flutter

@main
class AppDelegate: FlutterAppDelegate {
    var flutterEngine = FlutterEngine(name: "cached_app_engine")

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Meluncurkan engine terlebih dahulu di background
        flutterEngine.run()
        GeneratedPluginRegistrant.register(with: self.flutterEngine)
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

Desktop Embedder #

Desktop Embedder dikembangkan menggunakan C++ (untuk Windows dan Linux) serta Objective-C++ (untuk macOS). Lapisan ini bertugas menyelaraskan arsitektur desktop dengan paradigma Flutter.

Sebagai gambaran, alur boot aplikasi desktop Windows berjalan melalui tahapan berikut:

flowchart TD
    WinMain["1. main() C++ Win32 Entry Point"] -->|"2. Inisialisasi API"| WinInit["FlutterDesktopInit()"]
    WinInit -->|"3. Buat Window Shell"| WinVC["FlutterDesktopViewControllerCreate()"]
    WinVC -->|"4. Masuk Event Loop"| WinLoop["Win32 Message Loop (Mendengar Klik, Hover, Resize)"]
    WinLoop -->|"5. Penutupan Aplikasi"| WinDestroy["FlutterDesktopDestroyViewController()"]
    
    style WinLoop stroke:#0288d1,stroke-width:2px

Desktop Embedder menangani beberapa tantangan penting yang membedakannya dengan mobile embedder:

  • Sistem Manajemen Jendela (Windowing): Perangkat mobile berasumsi aplikasi berjalan satu layar penuh (fullscreen). Di desktop, embedder harus merespons perubahan ukuran jendela (window resizing) secara dinamis dan melaporkannya ke engine agar layout disesuaikan seketika.
  • Perbedaan Model Input: Mobile hanya mendeteksi event sentuhan jari. Desktop embedder harus mendeteksi pergerakan mouse pointer, status kursor layang (hover), klik kanan/tengah, gerakan gulir roda mouse (scroll wheel), serta input kombinasi shortcut keyboard fisik.

Kita dapat mengamankan fungsionalitas visual desktop dengan mendeteksi jenis platform target di tingkat Dart:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

Widget buildSystemSpecificButton() {
  // BENAR: Memeriksa jenis platform target secara statis untuk penyesuaian UI
  final isDesktop = defaultTargetPlatform == TargetPlatform.macOS ||
                    defaultTargetPlatform == TargetPlatform.windows ||
                    defaultTargetPlatform == TargetPlatform.linux;

  if (isDesktop) {
    return HoverListenerWidget(
      child: OutlinedButton(onPressed: () {}, child: const Text('Aksi Desktop')),
    );
  }
  return ElevatedButton(onPressed: () {}, child: const Text('Aksi Mobile'));
}

Web Embedder #

Web Embedder memiliki perbedaan struktural yang radikal karena ia berjalan di dalam lingkungan kotak pasir peramban web (browser sandbox) tanpa akses langsung ke sistem operasi mesin.

Saat ini, Web Embedder menyediakan dua pilihan rendering modern:

flowchart TD
    subgraph WebPipelines["Pilihan Rendering Web Flutter"]
        direction TB
        CanvasKit["CanvasKit Mode (Tradisional)"] -->|"Dart ke JS"| JSBundle["JS Bundle + Wasm CanvasKit (Skia)"]
        JSBundle -->|"Render di WebGL Canvas"| CanvasKit_Out["Konsisten tapi Ukuran Besar (~1.5MB)"]
            
        WasmMode["Wasm Mode (Modern)"] -->|"Dart ke Wasm"| WasmBundle["Wasm Bytecode + Skwasm (Impeller Wasm)"]
        WasmBundle -->|"Render via WebGL2/WebGPU"| Wasm_Out["Performa Tinggi, Startup Cepat & Efisien"]
    end
        
    style CanvasKit stroke:#ff9800,stroke-width:2px
    style WasmMode stroke:#4caf50,stroke-width:2px
  • CanvasKit Mode: Mengompilasi library Skia C++ menjadi biner WebAssembly (Wasm) dan merender seluruh elemen UI menggunakan WebGL. Menghasilkan performa rendering visual yang 100% konsisten dengan mobile, namun memakan ukuran unduhan awal berkisar ~1.5MB yang kurang ramah untuk SEO.
  • Wasm Mode: Mengompilasi kode Dart kita secara langsung menjadi biner Wasm (bukan JavaScript). Mode ini menggunakan rendering engine berbasis Skwasm yang memanfaatkan API WebGL2 atau WebGPU untuk performa eksekusi mendekati kecepatan native.

Custom Embedder — Flutter di Mana Saja #

Karena Flutter C++ Engine melepaskan diri dari detail platform dan hanya berkomunikasi menggunakan API C terstandarisasi, siapa pun dapat membangun Platform Embedder baru untuk perangkat keras apa pun.

Berikut adalah gambaran inisialisasi minimal Flutter Engine dari sebuah custom embedder kustom menggunakan C API:

// BENAR: Menginisialisasi C++ Engine menggunakan pointer stabil dari custom embedder
void LaunchCustomFlutterApp() {
    FlutterProjectArgs args = {};
    args.struct_size = sizeof(FlutterProjectArgs);
    args.assets_path = "/var/flutter/my_app/assets";
    args.icu_data_path = "/var/flutter/icudtl.dat"; // File layout teks unicode

    FlutterEngine engine = nullptr;
    FlutterEngineResult result = FlutterEngineRun(
        FLUTTER_ENGINE_VERSION,
        &renderer_config, // Konfigurasi rendering GPU (Vulkan/Metal/GLES)
        &args,
        nullptr,
        &engine
    );

    if (result == kSuccess) {
        printf("Flutter Engine berhasil diluncurkan di perangkat IoT!\n");
    }
}

Beberapa implementasi custom embedder di industri nyata yang sukses saat ini meliputi:

  • Toyota: Membangun custom embedder untuk menjalankan sistem hiburan layar sentuh (infotainment in-dash) di kendaraan Toyota terbaru.
  • Samsung Tizen: Menulis custom embedder agar aplikasi Flutter dapat diinstal dan berjalan di Smart TV berbasis Tizen.
  • Embedded Linux (elinux): Proyek komunitas untuk menjalankan Flutter di sistem perangkat IoT berbasis Raspberry Pi menggunakan rendering DRM tanpa desktop environment.

Platform Channel — Jembatan Dart ke Native #

Platform Embedder bertugas melandasi sistem komunikasi asinkron Platform Channel. Ketika Dart memanggil API sistem native, Embedder bertindak sebagai perute data yang menerjemahkan pesan biner melintasi batas runtime.

sequenceDiagram
    autonumber
    participant Dart as Dart Code (UI Thread)
    participant Engine as Flutter Engine (C++)
    participant Emb as Platform Embedder (Native Host)
    participant Native as Native Code (Kotlin / Swift)

    Dart->>Engine: invokeMethod('getBatteryLevel')
    Engine->>Engine: Serialisasi pesan ke format biner
    Engine->>Emb: Teruskan pesan via BinaryMessenger
    Emb->>Native: Route pesan ke handler MethodChannel terdaftar
    Native->>Native: Panggil API Baterai OS (Android/iOS)
    Native-->>Emb: Kembalikan hasil (Level Baterai / Error)
    Emb-->>Engine: Teruskan respon biner
    Engine-->>Dart: Deserialisasi respon & selesaikan Future<int>

Berikut adalah contoh implementasi nyata Platform Channel:

// Di sisi Dart: Meminta informasi baterai secara asinkron
class BatteryInfoService {
  static const _channel = MethodChannel('flutter.unisbadri.com/battery');

  Future<int> fetchBatteryLevel() async {
    try {
      final int level = await _channel.invokeMethod('getBatteryLevel');
      return level;
    } on PlatformException catch (e) {
      throw Exception('Gagal membaca baterai: ${e.message}');
    }
  }
}

Dan berikut adalah penanganan rute pesan tersebut di sisi Android Platform Embedder (menggunakan Kotlin):

// Di sisi Android (Kotlin): Menerima pesan dan mengeksekusi API native
import android.os.BatteryManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "flutter.unisbadri.com/battery"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if (call.method == "getBatteryLevel") {
                    val level = getAndroidBatteryLevel()
                    if (level != -1) {
                        result.success(level)
                    } else {
                        result.error("UNAVAILABLE", "Sensor baterai tidak merespon", null)
                    }
                } else {
                    result.notImplemented()
                }
            }
    }

    private fun getAndroidBatteryLevel(): Int {
        val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}

Ringkasan #

  • Definisi Jembatan — Platform Embedder adalah shell native yang bertindak sebagai host inisialisasi awal bagi Flutter C++ Engine dan Dart runtime pada platform lokal.
  • Isolasi Tugas Engine — C++ Engine bersifat platform-agnostic; seluruh komunikasi dengan driver grafis, event loop, dan daur hidup OS diatur secara penuh oleh Embedder.
  • Implementasi Bahasa OS — Ditulis menggunakan bahasa pemrograman native target platform, seperti Java/Kotlin (Android), Swift/ObjC (iOS/macOS), C++ (Windows/Linux), dan JS (Web).
  • Mekanisme Pre-Warming — Menghindari jeda cold start (100-200ms) di iOS/Android dengan menginisialisasi instansi FlutterEngine lebih awal di memori cache sebelum dipanggil oleh UI.
  • Dukungan Platform Desktop — Desktop Embedder menangani model input pointer kursor, hovering, klik kanan, layout window resizable, serta keyboard shortcuts fisik.
  • Abstraksi Web Embedder — Menyediakan rendering terakselerasi via CanvasKit (Wasm-Skia) serta Wasm Mode modern (Skwasm-Impeller) yang berjalan langsung di atas WebGL2/WebGPU.
  • Embedder C API — Menggunakan interface C stabil yang dideklarasikan pada flutter_embedder.h untuk memfasilitasi pembuatan platform custom (seperti sistem otomotif Toyota).
  • Platform Channel Routing — Memfasilitasi komunikasi biner asinkron (MethodChannel) yang merutekan request dari Dart UI thread menuju API native OS secara aman.

← Sebelumnya: Engine Layer   Berikutnya: Rendering Pipeline →

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