Platform Embedder #
Platform Embedder adalah lapisan paling bawah dalam arsitektur Flutter yang langsung bersentuhan dengan sistem operasi. Tanpa Embedder, Flutter Engine tidak tahu bagaimana cara menampilkan piksel ke layar, menerima input dari pengguna, atau berinteraksi dengan layanan OS. Embedder adalah “jembatan” yang membuat Flutter bisa berjalan di platform manapun — bahkan platform yang belum pernah ada sebelumnya.
Peran Utama Embedder #
Secara sederhana, Embedder bertanggung jawab atas empat hal utama:
Flutter Engine (platform-agnostic)
|
| Embedder API (ABI stabil)
v
+------------------------------------------+
| Platform Embedder |
| |
| 1. Rendering Surface --> layar/canvas |
| 2. Input Handling --> sentuhan/kb |
| 3. Lifecycle Mgmt --> pause/resume |
| 4. Platform Services --> kamera/GPS |
+------------------------------------------+
|
v
Sistem Operasi & Hardware
Yang paling penting dipahami: Engine tidak peduli sedang berjalan di platform apa. Engine hanya tahu bahwa ada Embedder yang menyediakan rendering surface dan meneruskan event kepadanya. Embedder yang mengurus semua detail spesifik platform.
Bahasa Embedder per Platform #
Setiap platform memiliki Embedder yang ditulis dalam bahasa native platform tersebut:
| Platform | Bahasa Embedder | Rendering Surface |
|---|---|---|
| Android | Java / Kotlin + C++ | SurfaceView / TextureView |
| iOS | Swift / Obj-C / Obj-C++ | CAMetalLayer (Metal) |
| macOS | Swift / Obj-C / Obj-C++ | NSView + CAMetalLayer |
| Windows | C++ | HWND (Win32 window) |
| Linux | C++ | GTK window / Wayland |
| Web | JavaScript / Wasm | HTML Canvas / WebGL |
Android Embedder #
Android Embedder adalah implementasi yang paling matang karena Android adalah platform target pertama Flutter. Embedder Android menyediakan dua cara untuk menampilkan Flutter:
FlutterActivity dan FlutterFragment #
// Cara paling sederhana: gunakan FlutterActivity langsung
class MainActivity : FlutterActivity() {
// Tidak perlu kode tambahan untuk Flutter dasar
// FlutterActivity mengurus segalanya
}
// Atau gunakan FlutterFragment untuk integrasi ke Activity yang sudah ada
class NativeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_native)
// Embed Flutter sebagai Fragment
val flutterFragment = FlutterFragment
.withNewEngine()
.initialRoute("/flutter-screen")
.build<FlutterFragment>()
supportFragmentManager
.beginTransaction()
.add(R.id.flutter_container, flutterFragment, "flutter_fragment")
.commit()
}
}
Rendering di Android #
Android Embedder mendukung dua mode rendering surface:
SurfaceView (default):
+ Performa lebih baik (dedicated surface)
+ Tidak ada overdraw dengan UI native
- Tidak bisa di-overlap dengan view native biasa
TextureView (fallback):
+ Bisa di-overlay dengan view native
+ Mendukung transformasi (rotate, scale)
- Sedikit lebih lambat (copy texture per frame)
Lifecycle Android → Flutter #
Android Activity Lifecycle Flutter App Lifecycle
| |
onCreate() --> AppLifecycleState.resumed
onStart() --> (tidak ada mapping langsung)
onResume() --> AppLifecycleState.resumed
onPause() --> AppLifecycleState.inactive
onStop() --> AppLifecycleState.paused
onDestroy() --> AppLifecycleState.detached
Di sisi Dart, kamu bisa mendengarkan perubahan lifecycle ini:
class _MyWidgetState extends State<MyWidget>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print('App aktif kembali');
// resume video, koneksi, dll.
case AppLifecycleState.paused:
print('App di background');
// pause video, simpan state, dll.
case AppLifecycleState.detached:
print('App akan ditutup');
default:
break;
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
iOS Embedder #
iOS Embedder menggunakan UIKit sebagai fondasi dan Metal sebagai rendering backend melalui Impeller.
FlutterViewController #
// Cara menggunakan Flutter di iOS Native App
import Flutter
import UIKit
class NativeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Buat FlutterEngine (sebaiknya dibuat sekali dan di-cache)
let flutterEngine = FlutterEngine(name: "my_flutter_engine")
flutterEngine.run(withEntrypoint: nil)
// Buat FlutterViewController
let flutterVC = FlutterViewController(
engine: flutterEngine,
nibName: nil,
bundle: nil
)
// Tampilkan sebagai modal atau push ke navigation stack
present(flutterVC, animated: true)
}
}
Engine Caching di iOS #
Membuat FlutterEngine baru setiap kali Flutter ditampilkan itu mahal — ada overhead cold start sekitar 100–200ms. Solusinya adalah pre-warm engine:
// AppDelegate.swift — inisialisasi engine saat app pertama kali dibuka
@main
class AppDelegate: FlutterAppDelegate {
lazy var flutterEngine = FlutterEngine(name: "shared_engine")
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Pre-warm Flutter engine di background
flutterEngine.run()
GeneratedPluginRegistrant.register(with: flutterEngine)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
// Di ViewController, gunakan engine yang sudah di-warm-up
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let flutterVC = FlutterViewController(
engine: appDelegate.flutterEngine,
nibName: nil,
bundle: nil
)
Desktop Embedder #
Flutter mendukung tiga platform desktop secara official: Windows, macOS, dan Linux. Masing-masing memiliki Embedder yang ditulis dalam C++ (Windows dan Linux) atau Objective-C++ (macOS).
Windows Embedder #
Windows App Structure:
main() -- entry point C++
|
v
FlutterDesktopInit() --> inisialisasi Flutter
|
v
FlutterDesktopViewControllerCreate() --> buat window Flutter
|
v
Win32 message loop --> event loop platform
|
v
FlutterDesktopDestroyViewController() --> cleanup
Perbedaan Desktop vs Mobile #
| Aspek | Mobile | Desktop |
|---|---|---|
| Input | Touch, gestures | Mouse, keyboard, trackpad |
| Window | Fullscreen, fixed size | Resizable, multiple windows |
| Menu | Bottom nav, drawer | Menu bar, context menu |
| Cursor | Tidak ada | Pointer, hover states |
| Scroll | Touch scroll | Mouse wheel, trackpad |
Flutter menyediakan abstraksi untuk semua perbedaan ini:
// Deteksi platform untuk penyesuaian UI
import 'package:flutter/foundation.dart';
Widget buildButton() {
if (defaultTargetPlatform == TargetPlatform.macOS ||
defaultTargetPlatform == TargetPlatform.windows ||
defaultTargetPlatform == TargetPlatform.linux) {
// UI untuk desktop
return TextButton(onPressed: () {}, child: const Text('Desktop Button'));
}
// UI untuk mobile
return ElevatedButton(onPressed: () {}, child: const Text('Mobile Button'));
}
Web Embedder #
Web Embedder sedikit berbeda dari platform lainnya karena tidak ada “sistem operasi” dalam artian tradisional — yang ada adalah browser.
Flutter Web (dua mode rendering):
CanvasKit Mode (default):
Dart --> JavaScript + CanvasKit (Skia compiled to Wasm)
Kelebihan: rendering identik dengan mobile/desktop
Kekurangan: download size lebih besar (~1.5MB)
HTML Mode (legacy, dihapus di Flutter 3.x):
Dart --> JavaScript --> HTML/CSS/DOM
Kelebihan: lebih ringan, SEO-friendly
Kekurangan: rendering tidak selalu identik
Wasm Mode (masa depan):
Dart --> WebAssembly + Impeller (dalam pengembangan)
Kelebihan: performa jauh lebih baik dari JavaScript
Kekurangan: masih eksperimental
Custom Embedder — Flutter di Mana Saja #
Salah satu keunggulan arsitektur Embedder adalah siapapun bisa membuat Embedder baru untuk platform yang belum didukung secara official. Engine Flutter menyediakan Embedder API yang stabil berbasis C:
// Contoh inisialisasi Flutter Engine dari custom embedder (C API)
FlutterEngineResult result = FlutterEngineInitialize(
FLUTTER_ENGINE_VERSION,
&renderer_config, // konfigurasi rendering (OpenGL/Vulkan/Metal/Software)
&project_args, // path ke assets, flags, dll
user_data, // pointer ke data embedder kamu
&engine // output: handle ke engine
);
FlutterEngineRunInitialized(engine);
Beberapa implementasi custom embedder yang sudah ada di komunitas:
- Toyota — sistem infotainment in-dash untuk kendaraan menggunakan Flutter
- Raspberry Pi — beberapa project community untuk embedded Linux
- Flutter-elinux — Embedder untuk embedded Linux (Wayland/DRM)
- Samsung Tizen — Embedder untuk smart TV Samsung
- Meta Quest — eksperimen Flutter di VR headset
Implikasi arsitektur yang luar biasa: karena Engine bersifat platform-agnostic dan Embedder API-nya stabil, jika ada sistem operasi baru yang muncul di masa depan, Flutter hanya perlu satu Embedder baru untuk bisa menjalankan semua aplikasi Flutter yang sudah ada tanpa perubahan apapun di sisi developer.
Platform Channel — Jembatan Dart ke Native #
Embedder juga berperan sebagai router untuk Platform Channel — mekanisme komunikasi antara kode Dart dan kode native platform.
Kode Dart (UI Thread)
|
| MethodChannel / EventChannel / BasicMessageChannel
v
Flutter Engine (serialisasi pesan)
|
v
Platform Embedder (routing)
|
v
Native Code (Android: Kotlin/Java, iOS: Swift/ObjC)
|
v (hasil dikembalikan asinkron)
Kode Dart (callback/Future)
// Sisi Dart: memanggil fungsi native
const platform = MethodChannel('com.example.app/battery');
Future<int> getBatteryLevel() async {
try {
final int level = await platform.invokeMethod('getBatteryLevel');
return level;
} on PlatformException catch (e) {
throw Exception('Gagal mendapat info baterai: ${e.message}');
}
}
// Sisi Android (Kotlin): mengimplementasikan fungsi native
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.app/battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level unavailable", null)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
Ringkasan #
- Platform Embedder adalah lapisan native yang menghubungkan Flutter Engine dengan sistem operasi — ditulis dalam bahasa native masing-masing platform.
- Engine bersifat platform-agnostic dan tidak peduli sedang berjalan di platform apa — Embedder yang mengurus semua detail spesifik platform.
- Android Embedder menggunakan
FlutterActivity/FlutterFragmentdengan SurfaceView atau TextureView sebagai rendering surface.- iOS Embedder menggunakan
FlutterViewControllerdi atas UIKit dengan Metal/Impeller sebagai backend rendering.- Desktop Embedder (Windows, macOS, Linux) menangani perbedaan input model (mouse/keyboard) dan window management yang tidak ada di mobile.
- Web Embedder menggunakan CanvasKit (Skia compiled to Wasm) untuk rendering yang identik dengan platform lain.
- Embedder API Flutter bersifat stabil — siapapun bisa membuat Embedder baru untuk platform baru tanpa harus mengubah Engine atau aplikasi Flutter yang sudah ada.
- Platform Channel dikerjakan oleh Embedder sebagai router pesan antara kode Dart dan kode native platform.