Topik ini memberi pratinjau singkat bahasa Dart bagi pembaca yang pernah menyentuh Java atau JavaScript. Pembahasan mencakup tipe statis dan null safety, kedudukan objek dan fungsi, pemrograman asinkron, serta dua model kompilasi Dart, dan ditutup dengan satu program kecil yang memperagakan semuanya. Setelah topik ini, struktur dasar program Dart dan istilah pokoknya sudah dikenal, sehingga kode Flutter yang menggunakan fitur tersebut dapat dibaca tanpa terbentur istilah.
Posisi Dart di antara Java dan JavaScript#
Dart adalah bahasa pemrograman open source yang dikembangkan Google dan menjadi bahasa utama Flutter. Desainnya diarahkan pada pengembangan aplikasi klien (client), yaitu aplikasi berantarmuka yang berjalan di perangkat pengguna. Arah desain tersebut terlihat pada banyak fitur bahasanya. Sintaksnya mewarisi keluarga bahasa C: blok kode diapit kurung kurawal, pernyataan diakhiri titik koma, dan struktur programnya berbasis kelas.
Pembaca yang terbiasa dengan Java menemukan tipe, kelas, dan pewarisan yang dikenalnya, sedangkan pembaca JavaScript menemukan pola async/await yang bentuknya hampir sama. Perbedaan paling menentukan ada pada tiga hal. Null safety pada Dart adalah bagian tata bahasanya, bukan pemeriksaan yang ditambahkan dari luar. Fungsi dapat ditulis jauh lebih ringkas dengan sintaks panah. Terakhir, Dart memiliki dua model eksekusi, yaitu kompilasi saat pengembangan dan kompilasi penuh saat rilis, dan keduanya dibahas lebih lanjut di bawah.
Tipe statis yang kuat#
Dart bertipe statis (statically typed), yaitu tipe setiap variabel diperiksa compiler (penerjemah program ke bentuk yang dapat dijalankan) sebelum program dieksekusi. Perilaku ini menyerupai Java dan berbeda dari JavaScript yang menentukan tipe saat program berjalan. Kesalahan tipe tertangkap saat kode dikompilasi, bukan saat aplikasi sudah berjalan di tangan pengguna, sehingga kelas kesalahan yang paling umum tidak sempat sampai ke perangkat.
Penulisan tipe tidak harus eksplisit di semua tempat. Compiler menyimpulkan tipe dari nilai awalnya, sehingga var jumlah = 3 menetapkan tipe int tanpa pernyataan tipe, sedangkan final String nama = 'aplikasi_catatan' menuliskannya secara eksplisit. Koleksi bawaan Dart bersifat generik (generic), misalnya List<String> untuk daftar string, dan isinya ikut diperiksa. Tipe dynamic tersedia untuk melepas pemeriksaan tipe, tetapi pemakaiannya dianggap pilihan terakhir, bukan gaya bawaan bahasa.
Null safety bawaan bahasa#
Dart menegakkan sound null safety, yaitu sistem tipe yang membedakan tipe yang boleh bernilai null dan yang tidak. Secara bawaan setiap tipe bersifat non-nullable: variabelnya wajib berisi nilai dan tidak boleh diisi null. Tipe yang boleh bernilai null ditandai tanda tanya, misalnya String?, dan compiler menolak pemakaian nilai null pada tipe yang tidak mengizinkannya. Kesalahan klasik pada Java berupa NullPointerException, beserta kekacauan nilai null dan undefined pada JavaScript, dicegah sebelum program dijalankan.
// tidak-dijalankan
String kota = 'Bandung'; // non-nullable: wajib berisi nilai
String? alamat; // nullable: boleh null, nilai awalnya nullPemeriksaan itu dilakukan dengan analisis alur (flow analysis). Apabila variabel nullable telah diuji tidak bernilai null pada suatu cabang, variabel itu diperlakukan non-nullable di dalam cabang tersebut. Operator ?? memberikan nilai bawaan apabila nilai di sebelah kirinya null, dan bentuk inilah yang digunakan program pratinjau di bawah. Null safety bukan anotasi opsional yang ditambahkan belakangan, melainkan bagian tata bahasa Dart, sehingga pustaka bawaan dan ekosistem Flutter pun menaatinya.
Segala nilai adalah objek#
Seluruh nilai pada Dart adalah objek, termasuk bilangan, boolean, fungsi, bahkan nilai null itu sendiri. Bilangan bulat int memiliki metode sendiri, misalnya isEven untuk menguji kegenapannya, dan semua nilai tersebut diturunkan dari Object. Model ini berbeda dari Java yang memisahkan tipe primitif dan kelas pembungkusnya, seperti int dan Integer. Karena tidak ada pemisahan semacam itu pada Dart, nilai apa pun dapat disimpan dalam koleksi generik, dan metodenya dapat dipanggil tanpa konversi khusus.
Program pratinjau memperlihatkan sisi ini melalui jumlahCatatan.runtimeType, properti yang mengembalikan tipe dari objek bilangan tersebut. Keluarannya berupa int, satu tipe utuh tanpa pemisahan primitif dan pembungkus. Sifat yang sama berlaku pada string, boolean, list, dan fungsi, sehingga satu mekanisme tipe melayani seluruh nilai dalam bahasa.
Fungsi sebagai warga kelas satu#
Fungsi pada Dart adalah first-class citizen, yaitu fungsi diperlakukan sebagai nilai sebagaimana bilangan atau string. Tipenya dapat dinyatakan secara eksplisit, misalnya int Function(int) untuk fungsi yang menerima int dan mengembalikan int. Fungsi dapat disimpan di variabel, dikirim sebagai argumen ke fungsi lain, dan dikembalikan sebagai hasil fungsi. Tubuh fungsi yang hanya berisi satu ekspresi ditulis dengan sintaks panah =>, dan fungsi tanpa nama (anonymous function atau closure) dapat ditulis langsung di tempat argumen. Fungsi anonim menangkap variabel di sekitarnya, sehingga variabel tersebut masih dapat digunakan saat fungsi anonim itu dipanggil, dan kemampuan menangkap variabel itulah makna kata closure.
Pola ini menjadi tulang punggung kode Flutter. Callback (fungsi yang dipanggil balik saat suatu peristiwa terjadi) muncul sebagai parameter seperti onPressed pada tombol, sedangkan metode koleksi seperti map dan where menerima fungsi untuk mengubah atau menyaring isi koleksi. Bagian tengah program pratinjau memperagakan keduanya dalam bentuk paling sederhana.
Asinkron dengan async dan await#
Operasi yang menunggu hasil di kemudian hari, seperti membaca berkas atau meminta data melalui jaringan, dimodelkan dengan Future, objek yang mewakili nilai yang datang nanti. Kata kunci async menandai fungsi yang berjalan asinkron, sedangkan await menunggu hasil sebuah Future tanpa membekukan aplikasi. Kode yang ditulis dengan gaya ini terbaca berurutan seperti kode sinkron, tanpa rantai callback yang bersarang.
Bagi pembaca JavaScript, pola async/await terasa akrab karena bentuknya hampir sama, dan model eksekusinya pun mirip: Dart menjalankan kode pada event loop (antrian peristiwa) dalam satu jalur eksekusi secara bawaan. Perbedaannya ada pada tipe. Future<String> menyatakan dengan jelas tipe hasil yang ditunggu, dan pemeriksaannya diurus compiler sehingga kesalahan tipe pada kode asinkron tertangkap lebih awal. Selain Future, Dart menyediakan Stream untuk rangkaian nilai yang datang bertahap, misalnya rangkaian peristiwa sentuhan, dan tipe ini menjadi dasar banyak API pada Flutter.
Dua model kompilasi: JIT dan AOT#
Dart mengeksekusi kode melalui dua model yang dipilih sesuai kebutuhan. Saat pengembangan, kode dikompilasi JIT (just-in-time, dikompilasi saat program berjalan), model yang memungkinkan hot reload karena kode yang baru diubah dapat disuntikkan ke aplikasi yang masih berjalan tanpa memulai ulang dari awal. Untuk rilis, kode dikompilasi AOT (ahead-of-time, dikompilasi penuh sebelum aplikasi disebarkan) menjadi kode mesin native, sehingga aplikasi tidak membawa compiler JIT saat berjalan di perangkat dan waktu mulainya cepat. Untuk target web, kode Dart diterjemahkan menjadi JavaScript atau WebAssembly.
Dua kebutuhan yang berbeda dilayani dua mode yang berbeda pula: iterasi yang cepat saat kode ditulis, dan kinerja yang tinggi saat aplikasi dirilis. Model ganda ini pula yang menjelaskan dua kebiasaan kerja pada Flutter. Proses pengembangan terasa gesit karena hot reload, sedangkan aplikasi hasil rilis berjalan sebagai kode mesin tanpa interpreter.
Pratinjau dalam satu program#
Program berikut merangkum fitur yang baru dibahas ke dalam satu berkas bernama kilat.dart: variabel final, interpolasi string, objek beserta tipenya, fungsi panah, fungsi sebagai argumen, null safety, collection-for (perulangan di dalam literal koleksi), serta async/await. Program ini murni Dart tanpa Flutter, sehingga dapat dijalankan dengan perintah dart kilat.dart pada terminal setelah SDK tersedia. Kode ini bahkan tidak memuat satu pun pernyataan impor, karena Future dan Duration sudah disediakan pustaka inti bahasa. Keluarannya deterministik karena tidak bergantung pada waktu atau keacakan.
// Pratinjau fitur bahasa Dart dalam satu program.
Future<void> main() async {
// Variabel final: nilai ditetapkan sekali dan tidak dapat diganti.
final String namaAplikasi = 'aplikasi_catatan';
var jumlahCatatan = 3; // tipe int disimpulkan otomatis dari nilai awal.
// Interpolasi string: ekspresi Dart disisipkan langsung ke dalam string.
print('$namaAplikasi memuat $jumlahCatatan catatan');
// Seluruh nilai adalah objek, termasuk bilangan bulat.
print('Tipe jumlahCatatan: ${jumlahCatatan.runtimeType}');
// Fungsi panah: tubuh fungsi berupa satu ekspresi.
int dobel(int n) => n * 2;
print('Dobel dari 21: ${dobel(21)}');
// Fungsi sebagai argumen: map menerima fungsi anonim.
final panjangNama = ['Ali', 'Budi', 'Citra']
.map((nama) => nama.length)
.toList();
print('Panjang tiap nama: $panjangNama');
// Null safety: String? boleh bernilai null, sedangkan String tidak.
String? kategori;
print('Kategori: ${kategori ?? "tanpa kategori"}');
kategori = 'kerja';
print('Kategori: $kategori');
// Collection-for: perulangan ditulis di dalam literal list.
final nomorHalaman = [for (var i = 1; i <= 3; i++) i * 10];
print('Nomor halaman: $nomorHalaman');
// async/await: kode asinkron terbaca seperti kode berurutan.
final status = await prosesSinkronisasi();
print('Status: $status');
}
Future<String> prosesSinkronisasi() async {
await Future<void>.delayed(const Duration(milliseconds: 10));
return 'selesai';
}aplikasi_catatan memuat 3 catatan
Tipe jumlahCatatan: int
Dobel dari 21: 42
Panjang tiap nama: [3, 4, 5]
Kategori: tanpa kategori
Kategori: kerja
Nomor halaman: [10, 20, 30]
Status: selesaiUrutan keluaran mengikuti urutan pemanggilan print pada program. Bagian terakhir menunggu hasil prosesSinkronisasi selama 10 milidetik, tetapi struktur kodenya tetap terbaca sebagai kode berurutan biasa. Bentuk itulah khas async/await: penantian dinyatakan dengan satu kata kunci, sedangkan sisanya tetap kode biasa. Pola ini akan muncul berulang kali pada kode Flutter yang menunggu hasil dari jaringan atau berkas.
Ringkasan#
- Dart bertipe statis dengan pemeriksaan pada saat kompilasi, dan sound null safety mencegah akses ke nilai null sebelum program berjalan.
- Seluruh nilai adalah objek, sedangkan fungsi adalah nilai yang dapat dikirim sebagai argumen dan ditulis ringkas dengan sintaks panah.
- Future dengan async/await membuat kode asinkron terbaca seperti kode sinkron, dengan tipe hasil yang tetap diperiksa compiler.
- Dart berjalan dengan JIT saat pengembangan sebagai dasar hot reload, dan dengan AOT yang menghasilkan kode mesin native saat rilis.
Referensi#
- Dart overview — dart.dev — Ringkasan bahasa Dart beserta model eksekusinya, termasuk JIT, AOT, dan target web.
- Introduction to Dart — dart.dev — Referensi fitur bahasa Dart: variabel, fungsi, tipe, dan asinkron.
- Sound null safety — dart.dev — Penjelasan semantik null safety dan tipe nullable pada Dart.