Sintaks Utama

Sintaks Utama #

Dart dirancang dengan filosofi yang jelas: sintaks harus familiar bagi developer yang sudah kenal C#, Java, atau JavaScript, tapi dengan perbaikan-perbaikan yang menghilangkan jebakan umum dari bahasa-bahasa tersebut. Artikel ini bukan sekadar daftar fitur — setiap konstruksi sintaks dijelaskan dalam konteks masalah yang ia pecahkan, lengkap dengan pola yang benar dan pola yang harus dihindari. Tujuannya adalah memberikan peta mental yang solid sebelum kamu masuk ke topik-topik yang lebih dalam di artikel berikutnya.

Anatomi Program Dart #

Setiap program Dart yang bisa dieksekusi memiliki satu titik masuk: fungsi main(). Memahami struktur dasarnya akan membuat semua kode yang kamu tulis setelahnya terasa koheren.

// File: hello.dart

// Import library standar (opsional, hanya jika dibutuhkan)
import 'dart:math';

// Fungsi utama — entry point wajib
void main() {
  var pesan = 'Halo, Dart!';
  print(pesan);

  // Dart adalah bahasa strongly typed
  int angka = 42;
  double phi = 3.14159;
  bool aktif = true;

  print('Akar dari $angka: ${sqrt(angka.toDouble())}');
}

Beberapa hal yang langsung terlihat dari contoh ini: tipe data ditulis sebelum nama variabel, string interpolation menggunakan $ dan ${}, dan setiap statement diakhiri titik koma. Ini adalah tiga aturan sintaks paling mendasar di Dart.

flowchart TD
    A[File .dart] --> B[import statements]
    B --> C[Deklarasi top-level: variabel, fungsi, kelas]
    C --> D[void main]
    D --> E[Statement pertama]
    E --> F[...]
    F --> G[Program selesai]

Komentar #

Komentar bukan sekadar catatan pasif — mereka adalah bagian dari komunikasi kode. Dart mendukung tiga jenis komentar dengan tujuan yang berbeda, dan memilih yang salah bisa membuat tooling seperti dart doc melewatkan dokumentasi pentingmu.

// Komentar satu baris — untuk penjelasan singkat di dalam fungsi

/*
  Komentar multi-baris — untuk penjelasan panjang
  yang membutuhkan lebih dari satu baris.
  Jarang digunakan di dalam fungsi; lebih sering
  untuk menonaktifkan blok kode sementara.
*/

/// Komentar dokumentasi — ini yang diproses oleh dart doc
/// untuk menghasilkan dokumentasi HTML otomatis.
///
/// Gunakan format ini untuk mendokumentasikan:
/// - Fungsi dan method publik
/// - Kelas dan propertinya
/// - Typedef dan extension
///
/// [parameter] adalah nama parameter yang akan di-link otomatis.
/// Kembalikan [String] berisi pesan yang diformat.
String formatPesan(String parameter) {
  return 'Pesan: $parameter';
}
// ANTI-PATTERN: komentar yang hanya mengulang kode
int umur = 25; // set umur ke 25

// BENAR: komentar yang menjelaskan MENGAPA, bukan APA
int umur = 25; // usia minimum untuk mendaftar sebagai pemateri
Jalankan dart doc di direktori project untuk menghasilkan dokumentasi HTML dari komentar ///. Hasilnya akan tersimpan di folder doc/api/ dan bisa langsung dibuka di browser.

Variabel dan Tipe Data #

Dart adalah bahasa sound null safety — artinya compiler bisa menjamin bahwa variabel non-nullable tidak akan pernah bernilai null saat runtime. Ini menghilangkan seluruh kelas bug NullPointerException yang umum di bahasa lain.

Cara Mendeklarasikan Variabel #

Ada empat cara utama mendeklarasikan variabel di Dart, masing-masing dengan trade-off yang berbeda:

// 1. var — tipe diinferensikan, bisa diubah nilainya
var nama = 'Budi';       // inferensi: String
var umur = 25;           // inferensi: int
nama = 'Siti';           // ✓ boleh diubah

// 2. Tipe eksplisit — lebih verbose tapi lebih jelas
String kota = 'Jakarta';
int populasi = 10_000_000; // underscore sebagai pemisah ribuan, valid di Dart

// 3. final — tipe diinferensikan, nilai hanya bisa di-set sekali
final tanggalLahir = DateTime(1998, 5, 20);
// tanggalLahir = DateTime(2000, 1, 1); // ✗ error: tidak bisa diubah

// 4. const — nilai harus diketahui saat compile time
const pi = 3.14159265358979;
const appName = 'MyApp';
// const waktuSekarang = DateTime.now(); // ✗ error: DateTime.now() bukan compile-time constant
// ANTI-PATTERN: menggunakan var untuk semua variabel tanpa pertimbangan
var x = hitungTotal();   // apa tipe kembalian hitungTotal()? tidak jelas
var y = x * 1.1;         // sekarang y bertipe apa?

// BENAR: gunakan tipe eksplisit ketika tipe tidak obvious dari nilai awalnya
double subtotal = hitungTotal();
double totalDenganPajak = subtotal * 1.1;

Null Safety #

Null safety adalah salah satu fitur terpenting Dart modern. Secara default, semua variabel tidak boleh null — kamu harus secara eksplisit menandai variabel yang bisa null dengan tanda ?.

// Variabel non-nullable — compiler menjamin tidak pernah null
String nama = 'Budi';
// nama = null; // ✗ error kompilasi

// Variabel nullable — harus ditangani sebelum digunakan
String? namaOptional;
namaOptional = null; // ✓ valid

// Operator null-aware untuk menangani nullable dengan aman
String tampilan = namaOptional ?? 'Tamu';          // fallback jika null
int? panjang = namaOptional?.length;               // akses aman, hasilnya int?
namaOptional ??= 'Default';                        // assign hanya jika null

// Late initialization — non-nullable tapi diinisialisasi belakangan
late String koneksiDatabase;
// koneksiDatabase diisi sebelum diakses pertama kali
// ANTI-PATTERN: menggunakan nullable di mana non-nullable sudah cukup
String? kota = 'Jakarta'; // mengapa nullable? kota sudah pasti ada nilainya

// BENAR: nullable hanya untuk data yang memang bisa tidak ada
String kota = 'Jakarta';
String? kotaKelahiran; // mungkin pengguna tidak mengisi ini

Tipe Data Dasar #

Tipe Deskripsi Contoh literal
int Bilangan bulat, tidak terbatas ukurannya 42, -7, 0xFF
double Bilangan desimal 64-bit 3.14, -0.5, 1.0e10
num Supertype dari int dan double bisa menyimpan keduanya
String Teks Unicode, immutable 'hello', "world"
bool Nilai benar/salah true, false
List<T> Array berurutan [1, 2, 3]
Set<T> Koleksi unik tanpa urutan {1, 2, 3}
Map<K,V> Pasangan kunci-nilai {'a': 1}
dynamic Tipe apa saja, tidak dicek compiler hindari jika bisa
Object Supertype semua tipe non-nullable

Operator #

Dart mewarisi sebagian besar operator dari C-family, tapi menambahkan beberapa operator null-aware yang sangat berguna. Memahami grup operator ini akan membuat kode kamu jauh lebih ringkas tanpa kehilangan kejelasan.

Operator Aritmatika dan Perbandingan #

int a = 17;
int b = 5;

// Aritmatika standar
print(a + b);   // 22
print(a - b);   // 12
print(a * b);   // 85
print(a / b);   // 3.4 — selalu double
print(a ~/ b);  // 3   — pembagian integer (floor division)
print(a % b);   // 2   — modulo/sisa bagi

// Perbandingan — selalu mengembalikan bool
print(a == b);  // false
print(a != b);  // true
print(a > b);   // true
print(a >= b);  // true
// ANTI-PATTERN: menggunakan / untuk pembagian integer
int total = 100;
int bagian = total / 3; // ✗ error: double tidak bisa diassign ke int

// BENAR: gunakan ~/ untuk hasil integer
int bagianBenar = total ~/ 3; // ✓ hasilnya 33

Operator Null-Aware #

Ini adalah salah satu fitur sintaks Dart yang paling membedakannya dari bahasa lain:

String? input = dapatkanInput(); // mungkin null

// ?? — gunakan nilai kanan jika kiri null
String teks = input ?? 'nilai default';

// ??= — assign nilai jika variabel masih null
input ??= 'default';

// ?. — panggil method/akses properti hanya jika tidak null
int? panjang = input?.length;

// !. — paksa non-null (gunakan hati-hati, bisa throw jika null)
int panjangPasti = input!.length; // pastikan input tidak null sebelum ini
// ANTI-PATTERN: cek null manual yang verbose
String hasil;
if (input != null) {
  hasil = input;
} else {
  hasil = 'default';
}

// BENAR: gunakan operator ??
String hasil = input ?? 'default';

Operator Cascade (..) #

Operator cascade memungkinkan memanggil beberapa method pada objek yang sama secara berantai tanpa mengulang nama variabel:

// ANTI-PATTERN: mengulang nama variabel untuk setiap operasi
var buffer = StringBuffer();
buffer.write('Halo');
buffer.write(', ');
buffer.write('Dart');
buffer.writeln('!');

// BENAR: gunakan cascade operator
var buffer = StringBuffer()
  ..write('Halo')
  ..write(', ')
  ..write('Dart')
  ..writeln('!');

print(buffer.toString()); // Halo, Dart!

Kontrol Aliran #

Dart memiliki semua konstruksi kontrol aliran yang kamu harapkan, dengan beberapa tambahan yang membuatnya lebih ekspresif.

Percabangan: if dan switch #

int nilai = 85;

// if-else klasik
if (nilai >= 90) {
  print('A');
} else if (nilai >= 80) {
  print('B');
} else if (nilai >= 70) {
  print('C');
} else {
  print('Di bawah standar');
}

// Conditional expression — untuk ekspresi sederhana
String grade = nilai >= 90 ? 'Lulus dengan pujian' : 'Lulus';

Dart 3 memperkenalkan switch expression yang jauh lebih powerful dari switch statement klasik:

// switch statement klasik (Dart lama)
String deskripsi;
switch (nilai) {
  case >= 90:
    deskripsi = 'Sangat Baik';
    break;
  case >= 80:
    deskripsi = 'Baik';
    break;
  default:
    deskripsi = 'Cukup';
}

// switch expression (Dart 3+) — lebih ringkas dan harus exhaustive
String deskripsi = switch (nilai) {
  >= 90 => 'Sangat Baik',
  >= 80 => 'Baik',
  >= 70 => 'Cukup',
  _     => 'Di bawah standar',  // _ adalah wildcard/default
};

Perulangan #

// for klasik — ketika butuh index
List<String> buah = ['apel', 'jeruk', 'mangga'];
for (int i = 0; i < buah.length; i++) {
  print('$i: ${buah[i]}');
}

// for-in — ketika hanya butuh elemennya
for (String b in buah) {
  print(b);
}

// forEach dengan lambda — gaya fungsional
buah.forEach((b) => print(b));

// while — ketika kondisi berhenti tidak diketahui di awal
int n = 1;
while (n < 100) {
  n *= 2;
}
print(n); // 128

// do-while — body dijalankan minimal sekali
int input;
do {
  input = bacaInput();
} while (input < 0);
// ANTI-PATTERN: menggunakan for klasik ketika tidak butuh index
for (int i = 0; i < buah.length; i++) {
  print(buah[i]); // i tidak digunakan untuk apapun selain akses elemen
}

// BENAR: gunakan for-in
for (String b in buah) {
  print(b);
}

break, continue, dan Label #

// break — hentikan perulangan
for (int i = 0; i < 10; i++) {
  if (i == 5) break;
  print(i); // mencetak 0 sampai 4
}

// continue — lewati iterasi ini, lanjut ke berikutnya
for (int i = 0; i < 10; i++) {
  if (i % 2 == 0) continue;
  print(i); // mencetak 1, 3, 5, 7, 9
}

// label — untuk break/continue di nested loop
luarLoop:
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    if (j == 1) break luarLoop; // keluar dari kedua loop
    print('$i,$j');
  }
}

Fungsi #

Fungsi di Dart adalah first-class citizen — artinya fungsi bisa disimpan di variabel, dijadikan parameter, dan dikembalikan dari fungsi lain. Ini membuka pintu untuk gaya pemrograman fungsional yang sangat powerful.

Deklarasi dan Parameter #

// Fungsi dengan tipe kembalian eksplisit
int tambah(int a, int b) {
  return a + b;
}

// Arrow function — untuk fungsi satu ekspresi
int kali(int a, int b) => a * b;

// Parameter wajib positional
void sapa(String nama, int umur) {
  print('Halo $nama, umur $umur tahun');
}

// Parameter opsional positional — dibungkus []
void sapaDenganGelar(String nama, [String? gelar]) {
  print('Halo ${gelar != null ? "$gelar " : ""}$nama');
}

// Parameter named (bernama) — dibungkus {}
void buatProfil({
  required String nama,   // required: wajib diisi
  int umur = 0,           // default value: opsional
  String? kota,           // nullable: opsional
}) {
  print('$nama, $umur tahun, ${kota ?? "kota tidak diketahui"}');
}

void main() {
  sapa('Budi', 25);
  sapaDenganGelar('Siti', 'Dr.');
  buatProfil(nama: 'Andi', umur: 30, kota: 'Bandung');
  buatProfil(nama: 'Rini'); // umur = 0, kota = null
}
// ANTI-PATTERN: parameter positional untuk fungsi dengan banyak argumen
void buatOrder(String nama, String alamat, int qty, double harga, bool express) {
  // Saat memanggil: buatOrder('Budi', 'Jl. Merdeka 1', 2, 150000, true)
  // Tidak jelas argumen mana itu express dan mana qty
}

// BENAR: gunakan named parameters untuk kejelasan
void buatOrder({
  required String nama,
  required String alamat,
  required int qty,
  required double harga,
  bool express = false,
}) {}
// Pemanggilan: buatOrder(nama: 'Budi', alamat: 'Jl. Merdeka 1', qty: 2, harga: 150000, express: true)
// Setiap argumen jelas tujuannya

Fungsi sebagai Nilai #

// Menyimpan fungsi di variabel
int Function(int, int) operasi = tambah;
print(operasi(3, 4)); // 7

operasi = (a, b) => a * b; // ganti ke fungsi perkalian
print(operasi(3, 4)); // 12

// Fungsi sebagai parameter (higher-order function)
List<int> angka = [1, 2, 3, 4, 5];

// map: transformasi setiap elemen
List<int> dikuadratkan = angka.map((n) => n * n).toList();
print(dikuadratkan); // [1, 4, 9, 16, 25]

// where: filter elemen
List<int> genap = angka.where((n) => n % 2 == 0).toList();
print(genap); // [2, 4]

// reduce: agregasi
int total = angka.reduce((acc, n) => acc + n);
print(total); // 15

Kelas dan Objek #

Dart adalah bahasa berorientasi objek secara menyeluruh — bahkan int dan bool adalah kelas. Ini memberikan konsistensi yang tidak ditemukan di bahasa seperti Java yang membedakan primitive type dan object type.

Mendefinisikan Kelas #

class Produk {
  // Properti (fields)
  final String id;
  String nama;
  double harga;
  int stok;

  // Constructor utama
  Produk({
    required this.id,
    required this.nama,
    required this.harga,
    this.stok = 0,
  });

  // Named constructor — untuk skenario pembuatan yang berbeda
  Produk.kosong()
      : id = '',
        nama = '',
        harga = 0,
        stok = 0;

  // Factory constructor — untuk logika pembuatan yang kompleks
  factory Produk.dariJson(Map<String, dynamic> json) {
    return Produk(
      id: json['id'] as String,
      nama: json['nama'] as String,
      harga: (json['harga'] as num).toDouble(),
      stok: json['stok'] as int? ?? 0,
    );
  }

  // Getter — properti yang dihitung
  bool get tersedia => stok > 0;
  String get ringkasan => '$nama (Rp${harga.toStringAsFixed(0)})';

  // Method
  void tambahStok(int jumlah) {
    if (jumlah <= 0) throw ArgumentError('Jumlah harus positif');
    stok += jumlah;
  }

  // Override toString untuk debugging
  @override
  String toString() => 'Produk($id: $nama, stok: $stok)';
}

Pewarisan dan Polimorfisme #

abstract class Bentuk {
  // Method abstrak — wajib diimplementasikan subclass
  double hitungLuas();
  double hitungKeliling();

  // Method konkret — bisa diwarisi langsung
  void tampilkan() {
    print('Luas: ${hitungLuas()}, Keliling: ${hitungKeliling()}');
  }
}

class Persegi extends Bentuk {
  final double sisi;
  Persegi(this.sisi);

  @override
  double hitungLuas() => sisi * sisi;

  @override
  double hitungKeliling() => 4 * sisi;
}

class Lingkaran extends Bentuk {
  final double jariJari;
  Lingkaran(this.jariJari);

  @override
  double hitungLuas() => 3.14159 * jariJari * jariJari;

  @override
  double hitungKeliling() => 2 * 3.14159 * jariJari;
}

void main() {
  List<Bentuk> bentuk = [Persegi(5), Lingkaran(3)];
  for (var b in bentuk) {
    b.tampilkan(); // polimorfisme: method yang tepat dipanggil otomatis
  }
}
classDiagram
    class Bentuk {
        <<abstract>>
        +hitungLuas() double
        +hitungKeliling() double
        +tampilkan() void
    }
    class Persegi {
        +sisi double
        +hitungLuas() double
        +hitungKeliling() double
    }
    class Lingkaran {
        +jariJari double
        +hitungLuas() double
        +hitungKeliling() double
    }
    Bentuk <|-- Persegi
    Bentuk <|-- Lingkaran

Collections #

Dart menyediakan tiga tipe collection utama — List, Set, dan Map — yang semuanya generic dan null-safe. Memilih tipe yang tepat untuk kebutuhan data adalah keputusan desain penting.

// List — koleksi berurutan, elemen bisa duplikat
List<String> kota = ['Jakarta', 'Bandung', 'Surabaya'];
kota.add('Medan');
kota.insert(1, 'Yogyakarta');       // sisipkan di index 1
kota.removeWhere((k) => k.length > 7); // hapus kota dengan nama > 7 karakter
print(kota.first);                  // elemen pertama
print(kota.last);                   // elemen terakhir
print(kota.length);

// Set — koleksi unik, tidak berurutan, operasi himpunan
Set<String> tags = {'dart', 'flutter', 'google'};
tags.add('dart');   // tidak ada duplikasi, tetap 3 elemen
Set<String> lebih = {'dart', 'mobile', 'web'};

print(tags.intersection(lebih)); // {dart}
print(tags.union(lebih));        // {dart, flutter, google, mobile, web}
print(tags.difference(lebih));   // {flutter, google}

// Map — pasangan kunci-nilai
Map<String, int> skorSiswa = {
  'Budi': 90,
  'Siti': 85,
  'Andi': 92,
};
skorSiswa['Rini'] = 88;
print(skorSiswa['Budi']);              // 90
print(skorSiswa['Tono'] ?? 0);        // 0 (default jika tidak ada)
print(skorSiswa.keys.toList());       // semua nama
print(skorSiswa.values.toList());     // semua skor
print(skorSiswa.entries.toList());    // semua pasangan

// Iterasi Map
skorSiswa.forEach((nama, skor) {
  print('$nama: $skor');
});

Collection If dan Collection For #

Dart memiliki sintaks khusus untuk membangun collection secara kondisional atau iteratif:

bool tampilkanBonus = true;
List<String> menu = [
  'Nasi Goreng',
  'Mie Goreng',
  if (tampilkanBonus) 'Es Krim Gratis',  // collection if
];

List<int> angka = [1, 2, 3];
List<int> dikali = [
  for (int n in angka) n * 10,  // collection for: [10, 20, 30]
];
Collection Urutan Duplikat Akses Cocok untuk
List<T> Index Urutan penting, akses cepat
Set<T> Iterasi Keunikan, operasi himpunan
Map<K,V> Key unik Key Pencarian cepat lewat kunci

Exception Handling #

Exception di Dart menggunakan pola try-catch-finally yang familiar, tapi dengan tambahan blok on untuk menangkap tipe exception spesifik secara terpisah — lebih rapi dari menggunakan instanceof di dalam catch.

// Hierarki exception dasar Dart
// Object
//   └── Exception (dilempar oleh kondisi runtime)
//         ├── FormatException
//         ├── IOException
//         └── ...
//   └── Error (bug programmer, seharusnya tidak ditangkap)
//         ├── ArgumentError
//         ├── RangeError
//         └── ...

double bagi(double a, double b) {
  if (b == 0) throw ArgumentError('Pembagi tidak boleh nol');
  return a / b;
}

void main() {
  try {
    print(bagi(10, 2));   // 5.0
    print(bagi(10, 0));   // melempar ArgumentError
  } on ArgumentError catch (e) {
    // tangkap tipe spesifik
    print('Argumen salah: ${e.message}');
  } on FormatException catch (e, stackTrace) {
    // e adalah exception, stackTrace adalah call stack
    print('Format salah: $e');
    print(stackTrace);
  } catch (e) {
    // tangkap semua tipe lain
    print('Error tidak dikenal: $e');
  } finally {
    // selalu dijalankan, baik ada exception atau tidak
    print('Blok try-catch selesai');
  }
}
// ANTI-PATTERN: menangkap semua exception dengan catch kosong
try {
  prosesData();
} catch (e) {
  // diam — ini menyembunyikan bug yang seharusnya diketahui
}

// ANTI-PATTERN: menangkap Error (bukan Exception)
try {
  var list = [1, 2, 3];
  print(list[10]); // RangeError
} catch (e) {
  print('Tangkap semua termasuk Error'); // Error seharusnya tidak ditangkap
}

// BENAR: tangkap Exception spesifik, biarkan Error naik ke atas
try {
  prosesData();
} on FormatException catch (e) {
  // tangani format yang salah
  print('Data tidak valid: ${e.message}');
} on IOException catch (e) {
  // tangani error I/O
  print('Gagal membaca file: $e');
}
Jangan tangkap Error (seperti RangeError, ArgumentError, StackOverflowError) — ini adalah sinyal bahwa ada bug di kode, bukan kondisi runtime yang bisa dipulihkan. Biarkan ia crash agar bisa diperbaiki. Yang layak ditangkap adalah Exception.

Pemrograman Asinkron #

Dart menggunakan model single-threaded event loop — mirip JavaScript. Semua operasi I/O (baca file, HTTP request, database query) berjalan asinkron agar tidak memblokir thread utama. Future dan Stream adalah dua abstraksi utama untuk bekerja dengan kode asinkron.

Future dan async/await #

// Future<T> mewakili nilai yang tersedia di masa depan
Future<String> ambilData(String url) async {
  // Simulasi HTTP request
  await Future.delayed(Duration(seconds: 2));
  return 'Data dari $url';
}

// async/await membuat kode asinkron terbaca seperti sinkron
void main() async {
  print('Mulai mengambil data...');

  // Menunggu satu Future
  String hasil = await ambilData('https://api.example.com/data');
  print(hasil);

  // Menjalankan beberapa Future secara paralel
  List<Future<String>> semuaRequest = [
    ambilData('https://api.example.com/user'),
    ambilData('https://api.example.com/produk'),
    ambilData('https://api.example.com/order'),
  ];

  List<String> semuaHasil = await Future.wait(semuaRequest);
  semuaHasil.forEach(print);

  print('Selesai.');
}
// ANTI-PATTERN: await berurutan ketika bisa paralel
String user    = await ambilUser();     // tunggu selesai dulu
String produk  = await ambilProduk();   // baru ini dijalankan
String order   = await ambilOrder();    // total waktu = jumlah semua waktu

// BENAR: jalankan paralel dengan Future.wait
var results = await Future.wait([
  ambilUser(),
  ambilProduk(),
  ambilOrder(),
]);
// Total waktu = waktu terlama di antara ketiganya

Stream #

Stream adalah urutan event asinkron — seperti Future tapi bisa mengirimkan banyak nilai seiring waktu:

// Stream sederhana dengan generator
Stream<int> hitungMundur(int dari) async* {
  for (int i = dari; i >= 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i; // emit satu nilai
  }
}

void main() async {
  // Mendengarkan Stream dengan await for
  await for (int angka in hitungMundur(5)) {
    print(angka); // 5, 4, 3, 2, 1, 0 — satu per detik
  }

  print('Selesai!');
}
sequenceDiagram
    participant Main
    participant Future
    participant EventLoop

    Main->>EventLoop: await ambilData()
    Note over Main: Main "tidur", tidak blocking
    EventLoop->>Future: jalankan operasi asinkron
    Future-->>EventLoop: selesai, data tersedia
    EventLoop-->>Main: resume, data diterima
    Main->>Main: lanjutkan eksekusi

Import dan Modularisasi #

Dart mengorganisasi kode ke dalam library — setiap file .dart secara otomatis adalah sebuah library. import digunakan untuk menggunakan kode dari library lain.

// Import library standar Dart
import 'dart:math';           // fungsi matematika
import 'dart:convert';        // JSON, UTF-8, dll.
import 'dart:io';             // file, socket, HTTP server

// Import package dari pub.dev
import 'package:http/http.dart' as http;    // alias untuk menghindari konflik
import 'package:path/path.dart';

// Import file lokal
import 'produk.dart';
import '../utils/formatter.dart';

// Import dengan show — hanya import yang dibutuhkan
import 'dart:math' show sqrt, pi;

// Import dengan hide — import semua kecuali yang disebutkan
import 'dart:math' hide Random;

void main() async {
  // Menggunakan alias untuk menghindari ambiguitas nama
  var response = await http.get(Uri.parse('https://api.example.com'));
  var data = jsonDecode(response.body);
  print(sqrt(pi)); // dari dart:math
}
// ANTI-PATTERN: import tanpa alias ketika ada potensi konflik nama
import 'package:http/http.dart';
import 'package:dio/dio.dart';

// Sekarang 'Response' bisa ambigu — dari http atau dio?
Response r = await get(Uri.parse('...')); // ✗ error kompilasi

// BENAR: gunakan alias
import 'package:http/http.dart' as http;
import 'package:dio/dio.dart' as dio;

http.Response r1 = await http.get(Uri.parse('...'));
dio.Response r2 = await Dio().get('...');

Ringkasan #

  • Entry point setiap program Dart adalah void main() — tanpa ini, program tidak bisa dijalankan.
  • Null safety adalah fitur inti Dart modern — semua variabel non-nullable secara default, gunakan ? hanya untuk variabel yang memang bisa null, dan operator ??, ?., ??= untuk menanganinya dengan aman.
  • var vs tipe eksplisit — gunakan var ketika tipe obvious dari nilai awalnya, tipe eksplisit ketika tidak obvious atau untuk API publik.
  • final vs constfinal untuk nilai yang tidak berubah setelah di-set (runtime), const untuk nilai yang sudah diketahui saat compile time.
  • Named parameters lebih disukai untuk fungsi dengan lebih dari dua parameter — pemanggilan jadi jauh lebih mudah dibaca.
  • Collection if dan collection for membuat pembuatan List/Set/Map secara kondisional menjadi sangat ringkas dan ekspresif.
  • Tangkap Exception, biarkan Error — Error adalah bug programmer yang seharusnya diperbaiki, bukan kondisi yang perlu ditangani di runtime.
  • Future.wait() untuk paralelisme — jangan await satu per satu ketika beberapa operasi bisa berjalan bersamaan.
  • import ... as alias untuk menghindari konflik nama antar library, terutama ketika menggunakan banyak package eksternal.

← Sebelumnya: Instalasi   Berikutnya: Komentar →

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