Widget — apa itu, jenis dan komposisi di Flutter

Penulis: IT Sectr Diterbitkan: 2026-06-30 Waktu membaca: 10 mnt

Widget — adalah konsep sentral dari framework Flutter, yang mendeskripsikan konfigurasi elemen antarmuka pengguna. Setiap komponen visual, dari tombol hingga animasi kompleks, adalah Widget. Tidak seperti framework lain, di mana UI dideskripsikan dengan file XML terpisah atau digambar secara imperatif, Flutter membangun antarmuka melalui komposisi Widget — menggabungkan elemen kecil yang tak terbagi menjadi pohon hierarkis. Menurut Flutter Documentation (2025), pustaka Flutter SDK mencakup lebih dari 260 Widget bawaan, yang mencakup Material Design, Cupertino, dan gaya kustom.

Poin Utama

  • Widget — blok bangunan dasar UI di Flutter, yang mendeskripsikan konfigurasi elemen.
  • Komposisi — UI dibangun dengan menumpuk Widget satu sama lain, bukan melalui pewarisan.
  • StatelessWidget — widget yang tidak berubah setelah rendering (teks, ikon, spasi).
  • StatefulWidget — widget dengan status yang dapat berubah (formulir, animasi, daftar data).
  • Element tree — Flutter memelihara tiga pohon: Widget, Element, dan RenderObject.

Apa itu Widget di Flutter

Widget di Flutter adalah deskripsi yang tidak dapat diubah (immutable) dari bagian antarmuka pengguna. Setiap Widget berisi properti konfigurasi: ukuran, warna, posisi, teks, penangan acara, dan Widget anak. Widget itu sendiri tidak dirender secara langsung — mereka adalah resep (blueprints) yang menjadi dasar Flutter Engine membuat RenderObject — objek grafis nyata di layar.

Filosofi Flutter mengatakan: “Everything is a Widget”. Ini berarti tidak hanya elemen yang terlihat (Text, Image, Button) adalah Widget, tetapi juga blok struktural (Padding, Center, Column, Stack), blok perilaku (GestureDetector, AnimatedBuilder), dan bahkan aplikasi itu sendiri (MaterialApp, CupertinoApp). Pendekatan ini memastikan keseragaman: setiap elemen layar dapat dikombinasikan dengan elemen lain melalui penumpukan sederhana.

Menurut data Google I/O 2024 — Flutter Widgets Deep Dive, aplikasi rata-rata di Flutter mengandung antara 200 hingga 1500 Widget dalam satu waktu. Meskipun jumlah ini besar, Flutter mempertahankan 60 FPS bahkan pada perangkat budget berkat optimalisasi di tingkat mesin C++ Skia/Impeller. Widget adalah objek ringan (40–80 byte masing-masing), sehingga pembuatannya bukanlah hambatan kinerja.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Halo, Flutter!'),
        ),
      ),
    );
  }
}

Tiga pohon Flutter: Widget, Element, dan RenderObject

Untuk memahami cara kerja Widget, Anda perlu mengenal arsitektur Flutter yang terdiri dari tiga pohon yang saling terhubung. Pertama — Widget tree — mendeskripsikan konfigurasi UI. Ini adalah pohon ringan yang dapat dibangun kembali sepenuhnya pada setiap frame (garbage collector menghapus Widget lama dan membuat yang baru). Widget bersifat immutable: jika warna teks berubah — Widget Text baru dengan warna baru dibuat, yang lama dibuang.

Pohon kedua — Element tree — adalah penghubung antara Widget dan RenderObject. Element berisi referensi ke Widget (konfigurasi) dan ke RenderObject (rendering). Ketika Widget berubah, Flutter membandingkan Widget baru dengan Element lama dan memutuskan: memperbarui RenderObject yang ada (jika Widget bertipe sama) atau membuat yang baru (jika tipe Widget berubah). Proses ini disebut Reconciliation dan merupakan analog dari React Virtual DOM.

Pohon ketiga — RenderObject tree — bertanggung jawab untuk rendering aktual di layar. RenderObject berisi ukuran konkret, posisi, dan metode paint. Flutter Engine (C++ Skia atau Impeller) melintasi RenderObject tree dan merender setiap simpul. RenderObject tree adalah pohon terberat, oleh karena itu Flutter meminimalkan perubahannya dengan menggunakan kembali RenderObject saat mengganti Widget dengan tipe yang sama.

PohonTujuanImmutable?Siklus Hidup
WidgetKonfigurasi UI (resep)YaDibuat ulang setiap build
ElementKoneksi Widget ↔ RenderObjectTidakAda selama widget di pohon
RenderObjectRendering dan layoutTidakBerat, digunakan kembali jika memungkinkan

StatelessWidget vs StatefulWidget

Flutter membagi Widget menjadi dua tipe fundamental: StatelessWidget dan StatefulWidget. StatelessWidget adalah widget yang tidak mengandung status yang dapat berubah. Tampilan StatelessWidget sepenuhnya ditentukan oleh konstruktornya dan tidak dapat berubah setelah rendering. Contoh: Text, Icon, Divider, Padding. Semua properti StatelessWidget dideklarasikan sebagai final di konstruktor dan bersifat read-only.

StatefulWidget adalah widget dengan status yang dapat berubah. Terdiri dari dua kelas: Widget itu sendiri (konfigurasi tidak berubah, seperti StatelessWidget) dan State (status yang dapat berubah). Pemisahan Widget dari State adalah keputusan arsitektur kunci Flutter. Widget dibuat ulang setiap build, tetapi objek State tetap hidup sepanjang siklus hidup widget di pohon, mempertahankan statusnya.

Ketika setState() dipanggil, Flutter menandai State sebagai “kotor” dan pada frame berikutnya memanggil metode build() untuk membangun ulang subpohon. Penting: setState() tidak membuat ulang Widget itu sendiri — hanya memicu panggilan build() di State yang ada. Ini berarti StatefulWidget dapat memperbarui UI tanpa kehilangan status Widget anak, selama kunci (Key) elemen anak tetap stabil.

dart
// StatelessWidget — tampilan tidak pernah berubah
class GreetingWidget extends StatelessWidget {
  const GreetingWidget({super.key, required this.name});
  final String name;

  @override
  Widget build(BuildContext context) {
    return Text('Halo, $name');
  }
}

// StatefulWidget — penghitung dengan status yang dapat berubah
class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Hitungan: $_count'),
        ElevatedButton(
          onPressed: () => setState(() => _count++),
          child: const Text('Tambah'),
        ),
      ],
    );
  }
}

Jenis Widget: layout, painting, interactive

Semua Widget di Flutter dapat dibagi menjadi tiga kategori utama berdasarkan tujuan fungsionalnya. Layout Widget — bertanggung jawab atas penempatan elemen anak di layar. Row dan Column menempatkan anak dalam satu baris, Stack menumpuk satu di atas yang lain, Expanded dan Flexible mendistribusikan ruang yang tersedia. Layout Widget tidak memiliki representasi visual sendiri — mereka mengelola posisi dan ukuran widget anak.

Painting Widget — bertanggung jawab atas penampilan visual. Container menggabungkan dekorasi (warna, gradien, bayangan, batas) dengan properti layout. Padding menambahkan spasi, DecoratedBox menggambar latar belakang, Transform menerapkan transformasi (rotasi, penskalaan). Painting Widget adalah blok bangunan gaya visual dan sering digunakan bersama dengan layout Widget untuk mencapai tampilan yang diinginkan.

Interactive Widget — memproses masukan pengguna. GestureDetector mendeteksi gerakan (tap, swipe, pinch), InkWell menambahkan efek ripple Material, TextField menerima masukan teks, Slider dan Switch menyediakan kontrol standar. Interactive Widget memunculkan acara melalui fungsi callback yang diteruskan ke konstruktor atau ditangani melalui penyedia status.

KategoriContoh WidgetTujuan
LayoutRow, Column, Stack, Expanded, Flexible, AlignPenempatan dan ukuran elemen anak
PaintingContainer, Padding, DecoratedBox, RotatedBoxWarna, latar belakang, batas, bayangan, transformasi
InteractiveGestureDetector, InkWell, TextField, SliderPemrosesan sentuhan, masukan, gerakan
PlatformMaterialApp, CupertinoApp, Theme, MediaQueryIntegrasi dengan platform, tema, adaptasi
AsyncFutureBuilder, StreamBuilder, ValueListenableBuilderPembaruan reaktif dari data asinkron

Menurut data Flutter Widget of the Week (Google, 2025), komunitas Flutter secara aktif menggunakan kombinasi layout + painting + interactive Widget untuk membangun hampir semua antarmuka. Misalnya, tombol: InkWell (interactive) + Container (painting) + Text (static) + Padding (layout). Modularitas semacam ini memungkinkan penggunaan kembali blok standar dalam konteks berbeda tanpa duplikasi kode.

Komposisi Widget dan BuildContext

Komposisi Widget adalah proses membangun UI dengan menumpuk Widget satu di dalam yang lain. Berbeda dengan pewarisan klasik (extends), di mana kelas anak mewarisi perilaku induk, Flutter menggunakan agregasi: setiap Widget berisi Widget lain melalui parameter child (untuk satu) atau children (untuk banyak). Pendekatan ini memberikan fleksibilitas dan kemampuan penggunaan kembali yang lebih besar.

BuildContext adalah konsep terpenting kedua setelah Widget. BuildContext adalah deskriptor posisi Widget di pohon elemen. Melalui BuildContext, Widget dapat mengakses Widget leluhur (Theme.of(context), MediaQuery.of(context), Navigator.of(context)). BuildContext diteruskan ke metode build() dan digunakan untuk interaksi dengan elemen induk dan anak. Setiap Widget memiliki tepat satu BuildContext yang secara unik mengidentifikasi posisinya di pohon.

Menurut Flutter Architectural Overview (Google, 2025), BuildContext adalah dasar untuk InheritedWidget — mekanisme yang memungkinkan data diteruskan ke bawah pohon tanpa pengiriman eksplisit melalui konstruktor. Theme, MediaQuery, Navigator, dan Provider menggunakan InheritedWidget di bawah tenda. Widget yang ditumpuk dalam dapat mengakses data leluhur melalui BuildContext.dependOnInheritedWidgetOfExactType, yang menjadikan BuildContext kunci arsitektur reaktif Flutter.

dart
// Komposisi Widget melalui penumpukan
Scaffold(
  appBar: AppBar(title: const Text('Aplikasi Saya')),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Text('Selamat datang di Flutter',
          style: TextStyle(fontSize: 24)),
        const SizedBox(height: 16),
        ElevatedButton(
          onPressed: () { /* nav */ },
          child: const Text('Mulai'),
        ),
      ],
    ),
  ),
)

// Akses tema melalui BuildContext
Text(
  'Teks Bergaya',
  style: Theme.of(context).textTheme.headlineMedium,
)

Kesalahan umum dengan Widget

Kesalahan pertama dan paling umum — menggunakan StatefulWidget di mana StatelessWidget sudah cukup. Banyak pengembang Flutter pemula membuat StatefulWidget untuk semua widget, bahkan jika status disimpan di penyedia eksternal (Provider, Riverpod, BLoC). Ini berlebihan dan menurunkan kinerja. Aturan: gunakan StatelessWidget jika status dikelola dari luar atau jika widget tidak memiliki status yang dapat berubah sendiri.

Kesalahan kedua — membuat Widget di dalam metode build tanpa konstruktor const. Setiap Widget yang dibuat tanpa const dialokasikan ulang setiap build. Jika di dalam build() Widget dibuat dengan konstruktor const, Flutter dapat menggunakan kembali instance yang sama, mengurangi beban pada garbage collector. Tambahkan const di mana pun memungkinkan — terutama untuk Text, Icon, SizedBox, Padding, dan StatelessWidget lainnya.

Kesalahan ketiga — bekerja dengan kunci (Key) yang salah. Flutter menggunakan Key untuk mengidentifikasi Widget saat membangun ulang pohon. Jika daftar Widget dibangun ulang tanpa Key, Flutter dapat mengacaukan urutan elemen, yang menyebabkan animasi yang salah atau hilangnya status. Selalu tambahkan Key (misalnya, ValueKey atau ObjectKey) untuk elemen dalam daftar, terutama saat menggunakan ListView.builder dengan data dinamis.

Pertanyaan Umum

Apa perbedaan antara StatelessWidget dan StatefulWidget?

StatelessWidget — widget tanpa status yang dapat berubah, tampilannya sepenuhnya ditentukan oleh konstruktor. StatefulWidget — widget dengan status yang dapat berubah, yang disimpan di objek State terpisah dan dapat diperbarui melalui setState() tanpa membuat ulang widget itu sendiri. Gunakan StatelessWidget di mana pun memungkinkan, StatefulWidget — ketika status lokal diperlukan.

Mengapa Widget disebut immutable?

Widget immutable — adalah keputusan arsitektur Flutter untuk kinerja. Jika Widget bisa berubah (mutable), Flutter tidak akan dapat membandingkan konfigurasi lama dan baru dengan aman setiap build. Immutability memungkinkan Flutter dengan cepat menentukan apakah Widget telah berubah (melalui operator ==) dan menggunakan kembali RenderObject yang ada, meminimalkan operasi rendering yang mahal.

Apa itu BuildContext dan untuk apa?

BuildContext adalah deskriptor posisi Widget di pohon elemen. Melaluinya, Widget mengakses Widget leluhur (Theme, MediaQuery, Navigator) dan InheritedWidget. BuildContext juga digunakan untuk navigasi (Navigator.of(context)), menampilkan SnackBar, dan berinteraksi dengan Provider. Setiap Widget menerima BuildContext melalui metode build() dan meneruskannya ke penerusnya.

Bagaimana memilih antara Row, Column, dan Stack untuk layout?

Gunakan Row untuk penempatan elemen secara horizontal, Column — untuk vertikal, Stack — untuk menumpuk elemen satu sama lain. Row dan Column bekerja berdasarkan prinsip flexbox: children menempati ruang sesuai mainAxisSize, mainAxisAlignment, dan crossAxisAlignment. Stack menggunakan positioned-children untuk positioning yang presisi relatif terhadap tepi atau pusat.

Bagaimana Flutter mempertahankan 60 FPS dengan ribuan Widget?

Flutter mencapai kinerja tinggi melalui tiga mekanisme: (1) Widget are cheap — objek immutable ringan (40–80 byte), pembuatannya tidak membebani GC. (2) RenderObject reuse — saat mengganti Widget dengan tipe yang sama, RenderObject digunakan kembali, menghindari pembuatan ulang yang mahal. (3) Skia/Impeller engine — rendering di tingkat C++ dengan meminimalkan panggilan menggambar melalui repaint boundaries.

Ringkasan

  • Widget — konfigurasi immutable dari bagian UI di Flutter, blok bangunan dasar yang mendeskripsikan tampilan dan perilaku.
  • Tiga pohon — Flutter menggunakan Widget tree (konfigurasi), Element tree (koneksi), RenderObject tree (rendering) untuk rendering optimal.
  • Stateless vs Stateful — StatelessWidget tanpa status, StatefulWidget dengan State yang dapat berubah dan metode setState() untuk pembaruan UI.
  • Komposisi — UI dibangun melalui penumpukan Widget via child/children, tanpa pewarisan, memberikan fleksibilitas dan penggunaan kembali.
  • BuildContext — deskriptor posisi Widget di pohon untuk akses ke Theme, Navigator, MediaQuery, dan InheritedWidget.
  • Kinerja — Widget dibuat ulang setiap build (60 FPS), tetapi RenderObject digunakan kembali jika tipenya cocok.
  • Kategori — Widget dibagi menjadi layout (Row, Column, Stack), painting (Container, Padding), interactive (GestureDetector, TextField), dan platform (MaterialApp, Theme).

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga