Framework Layer #
Framework Layer adalah lapisan arsitektur teratas dalam Flutter yang paling sering kita sentuh sehari-hari sebagai pengembang. Seluruh kode di dalam lapisan ini ditulis 100% menggunakan bahasa pemrograman Dart, bersifat sepenuhnya open-source, dan dapat kita baca serta telusuri langsung kode sumber aslinya dari IDE pilihan kita. Framework Layer bertindak sebagai antarmuka pemrograman aplikasi (API) tingkat tinggi yang menyederhanakan interaksi kompleks dengan C++ Engine di bawahnya. Artikel ini akan membedah secara mendalam setiap sub-lapisan di dalam Framework Layer — dari Foundation yang bertindak sebagai fondasi dasar hingga Material dan Cupertino yang menyajikan komponen siap pakai.
Struktur Framework Layer #
Secara arsitektural, Framework Layer tidak dibangun sebagai satu blok monolitik tunggal, melainkan sebagai tumpukan sub-lapisan (sub-layers) yang disusun secara hierarkis dari bawah ke atas. Hierarki ini mengikuti prinsip abstraksi progresif: semakin ke bawah sub-lapisan tersebut, semakin dekat posisinya dengan Engine, semakin rendah level API-nya, dan semakin besar kendali tingkat rendah yang kita miliki. Sebaliknya, semakin ke atas sub-lapisan tersebut, semakin abstrak kodenya, semakin ramah pengembang (developer-friendly), dan semakin cepat pula kita dapat membangun antarmuka pengguna.
flowchart TD
subgraph Framework["Flutter Framework (Lapisan Dart)"]
direction TB
MaterialCupertino["Material & Cupertino (UI Kit & Design Language)"]
Widgets["Widgets Layer (Abstraksi Deklaratif & State)"]
Rendering["Rendering Layer (RenderObject Tree, Layout & Paint)"]
subgraph Services["Services (Layanan Sistem Inti)"]
direction LR
Animation["Animation (Ticker & Interpolasi)"]
Painting["Painting (Canvas & Path)"]
Gestures["Gestures (Gesture Arena)"]
end
Foundation["Foundation Layer (Konektor & Binding dart:ui)"]
MaterialCupertino --> Widgets
Widgets --> Rendering
Rendering --> Services
Services --> Foundation
end
style MaterialCupertino stroke:#0288d1,stroke-width:2px
style Widgets stroke:#388e3c,stroke-width:2px
style Rendering stroke:#f57c00,stroke-width:2px
style Services stroke:#7b1fa2,stroke-width:2px
style Foundation stroke:#d32f2f,stroke-width:2pxAturan dasar yang mengikat arsitektur ini adalah setiap lapisan hanya bergantung pada lapisan di bawahnya. Sebagai pengembang, kita tidak diwajibkan untuk selalu menggunakan lapisan paling atas (Material/Cupertino). Kita diberikan kebebasan penuh untuk melewati lapisan teratas dan berinteraksi langsung dengan lapisan Widgets atau bahkan menulis komponen visual kustom langsung di atas lapisan Rendering jika membutuhkan optimasi performa ekstrem atau efek visual yang tidak biasa.
Foundation #
Foundation adalah sub-lapisan paling dasar dalam arsitektur Framework. Lapisan ini menyediakan kelas-kelas utilitas, struktur data dasar, dan layanan sistem inti yang digunakan oleh seluruh sub-lapisan di atasnya. Foundation bertindak sebagai pembungkus (wrapper) ramah Dart untuk API tingkat rendah yang diekspos oleh C++ Engine melalui pustaka dart:ui.
1. ChangeNotifier & ValueNotifier #
Foundation menyediakan kelas-kelas penting untuk mendukung pemrograman reaktif dan pola pengamatan (observer pattern):
ChangeNotifier: Implementasi dari antarmukaListenableyang memelihara daftar pendengar (listeners). Ketika state internal kelas kita berubah, kita memanggilnotifyListeners(), yang kemudian secara otomatis mengiterasi dan memberi tahu semua objek terdaftar (seperti UI widget) untuk memperbarui diri. Kompleksitas operasi notifikasi ini adalah $O(N)$ di mana $N$ adalah jumlah pendengar yang aktif.ValueNotifier: Sub-kelas dariChangeNotifieryang dioptimalkan untuk memelihara satu nilai data tunggal. Ketika propertivaluemiliknya diubah menggunakan operator penugasan, kelas ini secara otomatis memicu notifikasi perubahan tanpa mengharuskan kita memanggilnotifyListeners()secara manual.
import 'package:flutter/foundation.dart';
// BENAR: Menggunakan ValueNotifier untuk melacak perubahan state tunggal secara reaktif
final ValueNotifier<String> appThemeStatus = ValueNotifier<String>('light');
void initThemeListener() {
appThemeStatus.addListener(() {
print('Tema aplikasi berubah menjadi: ${appThemeStatus.value}');
});
// Mengubah nilai secara otomatis memicu callback pendengar di atas
appThemeStatus.value = 'dark';
}
2. Diagnostics & Debugging #
Foundation menyediakan kelas Diagnosticable dan mixin DiagnosticableTreeMixin. Setiap kelas widget di dalam Flutter mewarisi properti ini untuk mengekspos representasi metadata struktural dari widget tersebut. Metadata ini dikumpulkan oleh pustaka diagnostics untuk menyusun pohon visual di dalam Flutter DevTools, memungkinkan kita memeriksa hierarki widget, ukuran layout, dan properti visual secara real-time selama proses pengembangan.
3. Build Mode Flags & Optimization #
Sub-lapisan ini mendefinisikan konstanta boolean global yang sangat penting bagi proses kompilasi aplikasi:
kDebugMode: Bernilaitruesaat aplikasi berjalan dalam mode pengembangan (JIT compilation).kProfileMode: Bernilaitruesaat aplikasi dijalankan untuk analisis performa (profiling).kReleaseMode: Bernilaitrueketika aplikasi dikompilasi secara penuh untuk didistribusikan ke pengguna (AOT compilation).
Selama proses kompilasi rilis, AOT compiler menggunakan konstanta ini untuk melakukan eliminasi kode mati (dead code elimination). Seluruh kode yang dibungkus dalam blok if (kDebugMode) atau pernyataan assert akan dihapus total dari biner akhir aplikasi, sehingga ukuran berkas menjadi lebih kecil dan kinerja runtime menjadi lebih cepat tanpa menyisakan overhead debugging.
Animation #
Sub-lapisan Animation menyediakan sistem animasi yang independen dari mekanisme bawaan sistem operasi native. Seluruh proses perhitungan nilai perantara animasi (interpolation) dilakukan langsung di dalam thread UI menggunakan kode Dart yang sangat cepat dan disinkronkan secara presisi dengan perangkat keras layar.
Komponen Utama Sistem Animasi #
Untuk membangun animasi yang mulus di Flutter, kita mengoordinasikan kolaborasi antara empat kelas inti berikut:
Ticker: Komponen paling bawah yang bertindak sebagai “detak jantung” mekanis. Ticker mendaftarkan diri ke engine untuk mendengarkan sinyal VSync (Vertical Synchronization) dari hardware layar. Setiap kali layar siap menggambar frame baru (misalnya setiap 16,6ms pada layar 60Hz), Ticker memicu callback yang mengirimkan durasi waktu berjalan saat itu.AnimationController: Pengelola siklus hidup animasi yang mengontrol durasi (misalnya 500ms), arah pemutaran (maju/mundur), dan status eksekusi (mulai, henti, ulangi). Kelas ini membutuhkan parametervsyncyang menerima objekTickerProvider(sepertiSingleTickerProviderStateMixin) untuk memastikan bahwa Ticker hanya berdetak ketika widget tampil di layar dan secara otomatis berhenti ketika widget keluar dari layar guna menghemat konsumsi baterai perangkat.Tween: Singkatan dari Between. Kelas ini mendefinisikan pemetaan rentang nilai awal (begin) dan nilai akhir (end) dari animasi. Tween tidak memiliki pengetahuan tentang waktu; ia hanya menerima pecahan masukan dari angka0.0hingga1.0lalu mengembalikan nilai interpolasi yang sesuai (misalnya, interpolasi warna dari merah ke biru).Curve: Menentukan laju perubahan kecepatan animasi dari waktu ke waktu (tidak linier). Contohnya adalahCurves.easeInOutyang memberikan akselerasi lambat di awal, cepat di tengah, dan deselerasi halus di akhir animasi.
// BENAR: Menggunakan AnimatedBuilder untuk performa animasi yang optimal
class FadeTransitionWidget extends StatefulWidget {
final Widget child;
const FadeTransitionWidget({super.key, required this.child});
@override
State<FadeTransitionWidget> createState() => _FadeTransitionWidgetState();
}
class _FadeTransitionWidgetState extends State<FadeTransitionWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this, // Menghubungkan ke ticker untuk sinkronisasi VSync
);
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeIn),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose(); // Wajib membuang controller untuk menghindari kebocoran memori
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _opacityAnimation,
builder: (context, child) {
return Opacity(
opacity: _opacityAnimation.value,
child: child, // Parameter child dilewatkan untuk mencegah pembangunan ulang widget statis di dalamnya
);
},
child: widget.child,
);
}
}
Painting #
Sub-lapisan Painting menyediakan abstraksi tingkat tinggi untuk operasi grafis 2D. Lapisan ini membungkus fungsi kanvas biner tingkat rendah yang disediakan oleh engine menjadi objek Dart yang aman, mudah dikelola, dan ekspresif.
Painting menyediakan kelas inti seperti:
Canvas: Objek yang menerima instruksi penggambaran. Kita dapat menggunakan Canvas untuk menggambar garis (drawLine), bentuk kotak (drawRect), lingkaran (drawCircle), jalur kustom (drawPath), gambar bitmap (drawImage), hingga teks terformat.Paint: Objek konfigurasi gaya yang menentukan bagaimana bentuk visual akan digambar. Kita dapat mengatur warna (color), ketebalan garis (strokeWidth), gaya isi atau garis saja (style), efek blur, filter warna, serta shader gradien.Path: Kumpulan segmen garis dan kurva (Bezier) yang membentuk satu jalur grafis kompleks tertutup atau terbuka.
Seluruh operasi penggambaran visual kustom di dalam Flutter dilakukan dengan menurunkan kelas CustomPainter yang mengimplementasikan metode paint di bawah ini:
class RadarGridPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint()
..color = const Color(0xFF4CAF50).withOpacity(0.5)
..style = PaintingStyle.stroke
..strokeWidth = 1.5;
// Menggambar lingkaran konsentris bertingkat
for (double r = 40.0; r <= size.width / 2; r += 40.0) {
canvas.drawCircle(center, r, paint);
}
}
@override
bool shouldRepaint(covariant RadarGridPainter oldDelegate) => false;
}
Sub-lapisan ini juga menangani aspek dekoratif lainnya seperti konversi gambar (ImageProvider), pemotongan area visual (ClipRect, ClipPath), pemformatan bayangan (BoxShadow), serta kalkulasi geometri vektor untuk rotasi, translasi, dan penskalaan matriks 3D.
Gestures #
Sub-lapisan Gestures bertanggung jawab untuk mengubah aliran koordinat koordinat sentuhan jari mentah (raw pointer events) dari sistem operasi perangkat menjadi konsep interaksi fisik yang bermakna bagi aplikasi.
Mekanisme ini bekerja melalui alur dua tahap:
- Hit Testing: Ketika pengguna menekan layar pada koordinat $(X, Y)$, Flutter melakukan penelusuran pohon rendering dari atas ke bawah untuk mengumpulkan semua widget yang areanya mencakup titik koordinat tersebut. Setiap widget ini diberikan kesempatan untuk mendaftarkan objek
GestureRecognizermereka. - Gesture Arena: Karena layar sentuh bersifat mendua (misalnya, gesekan jari bisa berarti menggulir halaman secara vertikal atau menyeret slider secara horizontal), Flutter menggunakan sistem Gesture Arena untuk menyelesaikan konflik kompetisi gestur secara adil.
flowchart TD
Touch["User Touch Event"] -->|"1. Hit Testing"| HitTest["Cari Widget di Koordinat"]
HitTest -->|"2. Daftarkan Recognizers"| Arena["Gesture Arena (Kompetisi)"]
Arena -->|"3. Analisis Pergerakan Jari"| Competition{"Kondisi Sentuhan?"}
Competition -->|"Jari diam & lepas cepat"| TapWins["Tap Recognizer Menang"]
Competition -->|"Jari bergerak melintasi threshold"| ScrollWins["Scroll/Drag Recognizer Menang"]
Competition -->|"Jari ditahan lama"| LongPressWins["LongPress Recognizer Menang"]
TapWins -->|"Picu onTap()"| ExecuteTap["Eksekusi Aksi Tap"]
ScrollWins -->|"Picu onDragUpdate() & batalkan Tap"| ExecuteDrag["Eksekusi Geser/Scroll"]
LongPressWins -->|"Picu onLongPress() & batalkan Tap"| ExecuteLong["Eksekusi Aksi Tahan"]
style Arena stroke:#7b1fa2,stroke-width:2px
style Competition stroke:#0288d1,stroke-width:2pxDi dalam Arena, setiap recognizer mengamati koordinat sentuhan selanjutnya. Jika gerakan sentuhan jari melewati batas ambang gulir (slop threshold) sebelum jari dilepas, recognizer gulir akan memenangkan kompetisi, secara otomatis membatalkan recognizer ketukan (tap), dan menutup arena. Desain ini memastikan aplikasi kita memberikan respon gestur yang presisi dan bebas lag.
Rendering #
Lapisan Rendering adalah mesin pemroses tata letak (layout engine) yang sangat efisien di Flutter. Di lapisan inilah pohon objek rendering (RenderObject Tree) dibuat dan dipelihara. Setiap objek di dalam pohon ini diturunkan dari kelas RenderObject (atau yang paling umum, RenderBox), yang secara aktif melakukan perhitungan ukuran fisik koordinat layar dan menggambar grafis visual.
Layout Constraints: Constraints Go Down, Sizes Go Up #
Proses tata letak Flutter berjalan dalam satu kali lintasan (single-pass layout) dengan kompleksitas waktu $O(N)$ yang sangat cepat. Proses ini diatur oleh aturan baku:
flowchart LR
Parent["Induk (Parent)"] -->|"1. Kirim Constraints (Min/Max Width/Height)"| Child["Anak (Child)"]
Child -->|"2. Hitung & Kembalikan Ukuran (Size)"| Parent
Parent -->|"3. Tentukan Posisi Child (Offset)"| LayoutDone["Tata Letak Selesai"]
style Parent stroke:#0288d1,stroke-width:2px
style Child stroke:#388e3c,stroke-width:2px- Constraints Go Down: Induk melewatkan objek
BoxConstraints(lebar minimum/maksimum dan tinggi minimum/maksimum) ke bawah kepada anaknya. - Sizes Go Up: Anak menghitung ukuran fisiknya sendiri berdasarkan batasan tersebut, lalu mengembalikan objek
Size(lebar dan tinggi aktual) kembali ke atas kepada induknya. - Parent Sets Position: Induk menentukan letak koordinat anak (
Offset) di layar berdasarkan ukuran yang dikembalikan. Anak tidak diperbolehkan menentukan posisinya sendiri di layar perangkat.
RepaintBoundary: Isolasi Rendering #
Salah satu fitur optimasi terbesar di lapisan Rendering adalah RepaintBoundary. Secara default, jika satu widget dalam aplikasi kita mengalami gambar ulang (misalnya ada ikon pemutar musik yang berputar terus-menerus), Flutter akan menggambar ulang seluruh kanvas halaman tersebut dari awal. Hal ini boros daya GPU.
Dengan menyelimuti widget dinamis tersebut menggunakan RepaintBoundary, kita menginstruksikan sistem rendering untuk membuat lapisan tampilan terpisah (separate display list layer). Ketika widget dinamis tersebut berputar, hanya lapisan kecil itu saja yang digambar ulang oleh GPU, sementara widget statis lainnya di sekitarnya tetap menggunakan memori gambar cache yang sudah ada tanpa digambar ulang sama sekali.
Widgets #
Sub-lapisan Widgets menyediakan struktur deklaratif di atas lapisan Rendering. Sebagai pengembang, kita sangat jarang membuat atau mengelola instansi RenderObject secara langsung karena strukturnya sangat imperatif dan memerlukan ratusan baris kode pengaturan state manual. Pustaka Widgets membungkus kerumitan ini menjadi sistem dekoratif yang ramah pengembang melalui pembagian Widget, Element, dan RenderObject.
InheritedWidget: Mekanisme State Sharing Global #
Salah satu kelas paling krusial di sub-lapisan ini adalah InheritedWidget. Kelas ini dirancang khusus untuk membagikan data dari leluhur pohon widget (ancestor) kepada keturunannya (descendants) yang berada sangat dalam di bawah pohon widget tanpa harus meneruskan parameter data secara manual melewati setiap tingkat widget (props drilling).
Mari kita pelajari cara kerja internal InheritedWidget melalui contoh berikut:
// BENAR: Menggunakan InheritedWidget untuk membagikan konfigurasi data secara efisien
class ConfigurationProvider extends InheritedWidget {
final String apiBaseUrl;
const ConfigurationProvider({
super.key,
required this.apiBaseUrl,
required super.child,
});
// Metode untuk diakses oleh widget anak
static ConfigurationProvider of(BuildContext context) {
// dependOnInheritedWidgetOfExactType mendaftarkan BuildContext pemanggil sebagai dependent
return context.dependOnInheritedWidgetOfExactType<ConfigurationProvider>()!;
}
@override
bool updateShouldNotify(ConfigurationProvider oldWidget) {
// Rebuild widget anak hanya jika nilai data berubah
return oldWidget.apiBaseUrl != apiBaseUrl;
}
}
Ketika widget anak memanggil ConfigurationProvider.of(context), Flutter di balik layar tidak hanya mencari objek tersebut secara linier di atas pohon widget. Flutter langsung mengambil referensi yang disimpan dalam peta hash internal milik BuildContext. Selain itu, metode dependOnInheritedWidgetOfExactType secara otomatis mendaftarkan Element anak tersebut ke dalam daftar ketergantungan InheritedWidget.
Ketika properti apiBaseUrl diperbarui dan updateShouldNotify mengembalikan nilai true, Flutter secara otomatis menandai seluruh Element anak terdaftar sebagai kotor (dirty) dan menjadwalkan pembangunan ulang (rebuild) pada frame berikutnya secara otomatis.
Material & Cupertino #
Material dan Cupertino adalah sub-lapisan paling atas dalam arsitektur Flutter Framework. Lapisan ini murni merupakan lapisan presentasi yang mengimplementasikan bahasa desain visual tertentu menggunakan komposisi widget-widget dasar dari lapisan di bawahnya.
1. Material Library #
Material Library mengimplementasikan panduan desain Material Design 3 (Material You) milik Google. Lapisan ini menyediakan komponen UI lengkap dengan sistem pewarnaan dinamis (dynamic color seeding), efek visual elevasi bayangan, transisi layar, serta animasi sentuhan riak air (ink ripples). Komponen populernya meliputi Scaffold, AppBar, NavigationBar, Card, dan FloatingActionButton.
2. Cupertino Library #
Cupertino Library mengimplementasikan bahasa desain iOS Human Interface Guidelines milik Apple. Lapisan ini dirancang agar aplikasi Flutter terlihat dan terasa seperti aplikasi iOS native asli. Komponennya memiliki ciri khas efek visual latar belakang buram translusen (frosted glass effect), animasi gulir pegas yang khas (bounce scroll), serta transisi halaman geser horizontal. Komponen populernya meliputi CupertinoPageScaffold, CupertinoNavigationBar, CupertinoSwitch, dan CupertinoActivityIndicator.
Kedua pustaka ini dibangun di atas fondasi yang sama (lapisan Widgets dan Rendering), sehingga kita dapat dengan bebas mencampurkan penggunaan komponen Material dan Cupertino di dalam satu aplikasi yang sama, atau bahkan membuat sistem desain hibrida kustom sendiri.
Ringkasan #
- Hierarki Framework — Terdiri dari 7 sub-lapisan: Foundation → Animation → Painting → Gestures → Rendering → Widgets → Material/Cupertino, di mana setiap lapisan atas dibangun di atas lapisan bawahnya.
- Foundation Inti — Menyediakan konektivitas dasar ke engine, sistem pelacakan state seperti
ChangeNotifierdanValueNotifier, serta penanganan diagnostic untuk debugging.- Animasi Mandiri — Menghitung nilai interpolasi secara dinamis pada thread UI menggunakan kelas
Ticker,AnimationController,Tween, danCurvetanpa dependensi pada OS native.- Painting & Canvas — Membungkus API grafis mentah C++ menjadi kelas Dart seperti
Canvas,Paint, danPathyang aman dan terstruktur untuk rendering grafis 2D kustom.- Gesture Arena — Memecahkan kompetisi antar recognizer gestur secara presisi melalui hit testing koordinat sentuhan mentah dan menutup arena saat threshold slop terlewati.
- Aturan Tata Letak Rendering — Mengikuti alur satu kali lintasan (single-pass): “constraints go down, sizes go up, parent sets position” melalui pohon
RenderObject.- Isolasi RepaintBoundary — Mengoptimalkan performa penggambaran ulang GPU dengan memisahkan widget animasi ke dalam lapisan layer tersendir untuk menghindari paint ulang global.
- Abstraksi Widgets & InheritedWidget — Menyederhanakan manipulasi UI deklaratif dan mengelola diseminasi data lintas pohon widget secara reaktif dan efisien menggunakan hash-map internal.
- Material & Cupertino — Menyajikan komponen antarmuka siap pakai yang mengimplementasikan panduan desain Google dan Apple secara konsisten.
← Sebelumnya: Architecture Overview Berikutnya: Engine Layer →