UI Framework #

Flutter bukan sekadar library atau kumpulan komponen — ia adalah UI framework lengkap yang membawa pendekatan baru dalam membangun antarmuka pengguna. Untuk benar-benar memahami Flutter, kita perlu memahami cara ia berpikir tentang UI: bagaimana widget disusun, bagaimana perubahan state mempengaruhi tampilan, dan bagaimana semua itu akhirnya menjadi piksel di layar.

Imperatif vs Deklaratif #

Sebelum Flutter, kebanyakan framework UI — termasuk Android native (View system) dan iOS UIKit — menggunakan pendekatan imperatif: developer secara eksplisit memanipulasi objek UI yang sudah ada.

// Pendekatan IMPERATIF (Android/iOS style)
// Kamu memegang referensi ke objek UI dan mengubahnya langsung

TextView myText = findViewById(R.id.my_text);
myText.setText("Halo");
myText.setTextColor(Color.RED);
myText.setVisibility(View.GONE);

Flutter menggunakan pendekatan deklaratif: kamu mendeskripsikan seperti apa UI seharusnya terlihat berdasarkan state saat ini — bukan bagaimana cara mengubahnya.

// Pendekatan DEKLARATIF (Flutter style)
// Kamu mendeskripsikan UI berdasarkan state, Flutter yang mengurus perubahannya

bool isVisible = true;
String label = "Halo";

Widget build(BuildContext context) {
  return Visibility(
    visible: isVisible,
    child: Text(
      label,
      style: TextStyle(color: Colors.red),
    ),
  );
}

Ketika state berubah, Flutter tidak memanipulasi widget yang ada — ia membangun ulang deskripsi UI, lalu secara cerdas menentukan apa yang perlu diperbarui di layar.

💡 Analogi Deklaratif

Pendekatan imperatif seperti memberitahu koki: “Ambil piring, buang sayurnya, tambahkan nasi, taruh di meja nomor 3.” Pendekatan deklaratif seperti memberikan menu baru: “Meja 3 seharusnya mendapat nasi goreng.” — koki yang tahu cara mencapainya.


Widget: Unit Dasar UI Flutter #

Di Flutter, semua adalah widget. Teks adalah widget. Tombol adalah widget. Padding adalah widget. Bahkan aplikasi itu sendiri adalah widget. Widget adalah deskripsi immutable (tidak bisa diubah) dari sebuah elemen UI.

// Semua ini adalah widget:
Text('Halo Flutter')
Icon(Icons.star)
Padding(padding: EdgeInsets.all(16))
Center(child: Text('Tengah'))
Column(children: [...])
MaterialApp(home: ...)

Widget bersifat immutable — artinya setelah dibuat, propertinya tidak bisa diubah. Ketika UI perlu berubah, Flutter tidak mengubah widget yang ada, melainkan membuat widget baru yang menggantikannya. Proses ini sangat efisien karena widget hanyalah objek konfigurasi ringan — blueprint UI, bukan UI itu sendiri.

Komposisi Widget #

Kekuatan Flutter ada pada komposisi — membangun UI kompleks dari widget-widget sederhana yang digabungkan:

// UI kompleks dari komposisi widget sederhana
Card(
  elevation: 4,
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Row(
      children: [
        CircleAvatar(
          backgroundImage: NetworkImage('https://example.com/foto.jpg'),
        ),
        SizedBox(width: 12),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Budi Santoso',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            Text(
              'Flutter Developer',
              style: TextStyle(color: Colors.grey),
            ),
          ],
        ),
      ],
    ),
  ),
)

Tidak ada komponen “UserCard” yang khusus — kamu merakit Card, Padding, Row, CircleAvatar, dan Column menjadi sesuatu yang lebih besar. Ini adalah filosofi komposisi Flutter.


Tiga Pohon Flutter #

Di balik widget yang kamu tulis, Flutter sebenarnya memaintain tiga pohon (tree) terpisah yang bekerja bersama:

Yang kamu tulis:          Yang Flutter kelola:
                          
Widget Tree               Element Tree          Render Object Tree
     |                         |                       |
  (blueprint)             (instansi)              (layout & paint)
  immutable               mutable                 mutable
  ringan                  perantara               berat
  dibuat ulang            diperbarui              diperbarui
  tiap build              seperlunya              seperlunya

Widget Tree #

Pohon yang kamu tulis dalam kode. Widget bersifat immutable dan ringan. Setiap kali build() dipanggil, widget tree dibuat ulang — tapi ini tidak mahal karena widget hanya objek konfigurasi.

Element Tree #

Flutter memaintain element tree sebagai “instansi” dari widget tree. Element bersifat mutable dan bertahan selama widget tetap ada di pohon. Element bertanggung jawab mencocokkan widget lama dengan widget baru — proses yang disebut reconciliation.

Render Object Tree #

Inilah yang benar-benar melakukan pekerjaan berat: layout (menghitung ukuran dan posisi) serta painting (menggambar ke canvas). Render object hanya dibuat dan diperbarui ketika benar-benar diperlukan — bukan setiap kali widget tree di-rebuild.

Mengapa tiga pohon?

Pemisahan ini adalah kunci performa Flutter. Widget bisa dibuat ulang ribuan kali per detik dengan biaya murah. Element dan Render Object — yang lebih mahal — hanya diperbarui ketika ada perubahan yang benar-benar mempengaruhi tampilan. Flutter cukup cerdas untuk membedakan keduanya.


Rendering Pipeline Flutter #

Setelah widget tree dibangun, Flutter menjalankan serangkaian proses untuk mengubahnya menjadi piksel:

  State berubah
       |
       v
  1. BUILD      --> Widget tree dibangun ulang (build() dipanggil)
       |
       v
  2. LAYOUT     --> Render objects menghitung ukuran & posisi
       |             (constraints diturunkan dari parent ke child)
       v
  3. PAINT      --> Render objects menggambar ke canvas
       |             (painting instructions direkam)
       v
  4. COMPOSITE  --> Layer-layer digabungkan
       |             (GPU compositing)
       v
  5. RASTERIZE  --> Instruksi gambar dikonversi ke piksel
                    (Skia / Impeller)

Phase 1: Build #

Flutter memanggil build() pada widget yang perlu diperbarui. Build phase menghasilkan widget tree baru yang kemudian dicocokkan dengan element tree yang ada.

Phase 2: Layout #

Flutter menurunkan constraints dari parent ke child. Setiap render object menerima constraint (min/max lebar dan tinggi), menghitung ukurannya sendiri, lalu meneruskan constraint yang sesuai ke child-nya.

Parent memberi constraint: "kamu boleh maksimal 300x200 piksel"
     |
     v
Child menghitung: "saya butuh 150x50 piksel"
     |
     v
Parent mencatat posisi child dan melanjutkan layout

Phase 3: Paint #

Setiap render object memanggil method paint() yang menghasilkan drawing instructions — bukan langsung piksel. Instruksi ini direkam dalam sebuah struktur bernama DisplayList.

Phase 4 & 5: Composite & Rasterize #

Layer-layer digabungkan (compositing) dan instruksi gambar dikonversi menjadi piksel aktual oleh GPU melalui Skia atau Impeller.


Desain System: Material vs Cupertino #

Flutter menyediakan dua design system lengkap yang siap pakai:

AspekMaterial DesignCupertino
AsalGoogle (Android)Apple (iOS)
Importmaterial.dartcupertino.dart
AppBarAppBarCupertinoNavigationBar
ButtonElevatedButtonCupertinoButton
SwitchSwitchCupertinoSwitch
DialogAlertDialogCupertinoAlertDialog
TampilanMaterial YouiOS native feel
// Menggunakan Material Design
import 'package:flutter/material.dart';

MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
    useMaterial3: true,  // Material 3 / Material You
  ),
  home: Scaffold(
    appBar: AppBar(title: Text('Material App')),
    body: ElevatedButton(
      onPressed: () {},
      child: Text('Tombol Material'),
    ),
  ),
)
// Menggunakan Cupertino (iOS style)
import 'package:flutter/cupertino.dart';

CupertinoApp(
  home: CupertinoPageScaffold(
    navigationBar: CupertinoNavigationBar(
      middle: Text('Cupertino App'),
    ),
    child: CupertinoButton(
      onPressed: () {},
      child: Text('Tombol iOS'),
    ),
  ),
)

Pilih yang Mana?

Untuk mayoritas aplikasi, Material Design adalah pilihan terbaik — ekosistemnya lebih lengkap dan didukung penuh oleh Google. Gunakan Cupertino hanya jika kamu membutuhkan tampilan yang benar-benar mengikuti konvensi iOS secara ketat.


Flutter vs Framework UI Lain #

Bagaimana pendekatan UI Flutter dibandingkan dengan yang lain?

React / React Native:
  JSX (deklaratif) --> Virtual DOM --> Diff --> DOM/Native update

Flutter:
  Widget tree (deklaratif) --> Element tree (reconciliation)
                           --> Render tree --> Skia/Impeller --> Piksel

SwiftUI:
  View (deklaratif) --> Attribute Graph --> UIKit/AppKit --> Piksel

Jetpack Compose:
  Composable (deklaratif) --> Slot Table --> Android Canvas --> Piksel

Pola deklaratif sudah menjadi standar industri modern. Flutter, SwiftUI, Jetpack Compose, dan React semuanya mengadopsi paradigma yang sama — mendeskripsikan apa yang harus tampil, bukan bagaimana cara mengubahnya.

Yang membedakan Flutter adalah ia tidak bergantung pada rendering stack platform sama sekali. Semuanya dirender oleh Flutter sendiri, dari widget hingga piksel — inilah yang memberikan konsistensi dan kontrol penuh.


Ringkasan #

  • Flutter menggunakan pendekatan deklaratif — kamu mendeskripsikan UI berdasarkan state, Flutter yang mengurus perubahannya.
  • Widget adalah unit dasar UI Flutter: immutable, ringan, dan dapat dikomposisikan secara bebas.
  • Flutter memaintain tiga pohon secara internal: Widget Tree (blueprint), Element Tree (instansi), dan Render Object Tree (layout & paint).
  • Rendering pipeline Flutter terdiri dari 5 fase: Build → Layout → Paint → Composite → Rasterize.
  • Flutter menyediakan dua design system siap pakai: Material Design (Android/Google) dan Cupertino (iOS/Apple).
  • Tidak seperti framework lain, Flutter tidak menggunakan rendering stack platform — semua digambar sendiri oleh Flutter Engine.

← Sebelumnya: AOT vs JIT   Berikutnya: Dart Language →

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