Pemrograman Dart
Menyiapkan Dart SDK & VS Code, serta Mengenal Class dan Object
Mulai perjalanan Object-Oriented Programming di Dart 3: cara menyiapkan proyek multi-file dengan Dart SDK dan VS Code, membuat class, property, method, hingga cascade operator.

Selama dua bab pertama, kita menulis seluruh program di dalam satu file menggunakan DartPad dan menyimpan data menggunakan variabel terpisah atau Map<String, dynamic>. Pendekatan prosedural tersebut cukup untuk program kecil, tetapi cepat menjadi rapuh saat aplikasi membesar: salah mengetik kunci 'harga' menjadi 'hrga' pada Map tidak akan terdeteksi saat kompilasi, dan fungsi-fungsi pengolah data tersebar jauh dari data yang diolahnya. Object-Oriented Programming (OOP) mengatasi masalah ini dengan menyatukan data (property/field) dan perilaku (method) ke dalam satu cetak biru terstruktur bernama Class. Selain itu, mulai bab ini kita akan beralih dari DartPad ke Dart SDK dan Visual Studio Code agar dapat mengorganisasikan kode ke dalam beberapa file layaknya proyek perangkat lunak sesungguhnya.
Yang akan dipelajari
- Menyiapkan lingkungan kerja lokal menggunakan Dart SDK, VS Code, dan perintah
dart create. - Memahami struktur folder standar proyek Dart (
bin/,lib/, danpubspec.yaml). - Membedakan konsep Class (cetak biru) dan Object (instansiasi nyata) beserta field dan method.
- Menggunakan operator cascade (
..dan?..) untuk merangkai operasi pada satu objek secara ringkas.
Menyiapkan proyek Dart SDK di VS Code
Mengapa kita perlu berpindah dari DartPad ke komputer lokal? Di dalam proyek nyata—dan khususnya ketika mempelajari encapsulation (visibilitas privat _) pada materi ke-11 nanti—aturan visibilitas di Dart bekerja pada tingkat file (library), sehingga kita wajib memisahkan definisi class dan fungsi main() ke dalam file yang berbeda.
Ikuti langkah singkat berikut untuk menyiapkan lingkungan kerja Anda:
- Instal Dart SDK: Unduh dan pasang Dart SDK sesuai sistem operasi Anda melalui situs resmi dart.dev/get-dart (atau otomatis terpasang jika Anda sudah menginstal Flutter SDK). Pastikan perintah
dart --versionberjalan di terminal. - Pasang Ekstensi VS Code: Buka Visual Studio Code, masuk ke tab Extensions, lalu pasang ekstensi resmi Dart (dibuat oleh
dart-code.org). - Buat Proyek Baru: Buka terminal, arahkan ke folder tempat Anda menyimpan latihan, lalu jalankan perintah berikut:
dart create -t console belajar_dart_oop
cd belajar_dart_oop
code .
Perintah dart create akan menghasilkan struktur direktori standar yang digunakan di seluruh ekosistem Dart:
| Folder / File | Kegunaan Utama |
|---|---|
pubspec.yaml |
File konfigurasi proyek yang memuat nama paket, versi Dart SDK (^3.0.0), dan daftar dependensi. |
lib/ |
Tempat menyimpan seluruh kode logika dan model class yang dapat diimpor oleh file lain. |
bin/ |
Tempat menyimpan file titik masuk (entry point) aplikasi CLI yang memiliki fungsi main(). |
test/ |
Tempat menyimpan pengujian otomatis (unit test). |
Untuk menjalankan program dari terminal VS Code, Anda cukup mengetikkan perintah dart run.
Dari prosedural ke Object-Oriented: Class dan Object
Bayangkan kita sedang membangun sistem toko daring. Dalam pendekatan OOP, kita membuat Class sebagai cetak biru (blueprint) atau cetakan kue, sedangkan Object adalah kue nyata yang dihasilkan dari cetakan tersebut. Satu class Produk dapat digunakan untuk mencetak ratusan objek produk yang masing-masing memiliki nilai data tersendiri.
| Istilah OOP | Analogi Dunia Nyata | Peran di dalam Kode Dart |
|---|---|---|
Class (PascalCase) |
Desain cetak biru mobil | Mendefinisikan tipe data baru beserta struktur data dan perilakunya. |
Field / Property (camelCase) |
Warna cat, kapasitas baterai, kecepatan | Variabel di dalam class yang menyimpan keadaan (state) milik objek. |
Method (camelCase) |
Maju, mengerem, mengisi daya | Fungsi di dalam class yang dapat membaca dan mengubah field objek tersebut. |
| Object / Instance | Mobil fisik bernomor polisi B 1234 XYZ | Nilai nyata di memori yang dibuat dari sebuah class. |
Mari kita buat file pertama kita di dalam folder lib/rekening_bank.dart:
// File: lib/rekening_bank.dart
class RekeningBank {
// Fields (properti) yang menyimpan state objek
String nomorRekening = '';
String namaPemilik = '';
double saldo = 0.0;
// Method untuk menambah saldo
void setorTunai(double nominal) {
if (nominal <= 0) {
print('Nominal setor harus lebih dari Rp0.');
return;
}
saldo += nominal;
print('Berhasil menyetor Rp${nominal.toStringAsFixed(0)} ke rekening $namaPemilik.');
}
// Method untuk menarik saldo dengan pengecekan kecukupan dana
bool tarikTunai(double nominal) {
if (nominal > saldo) {
print('Gagal menarik Rp${nominal.toStringAsFixed(0)}: Saldo $namaPemilik tidak mencukupi.');
return false;
}
saldo -= nominal;
print('Berhasil menarik Rp${nominal.toStringAsFixed(0)} dari rekening $namaPemilik.');
return true;
}
// Method untuk mencetak ringkasan rekening
void tampilkanInfo() {
print('[$nomorRekening] Pemilik: $namaPemilik | Saldo: Rp${saldo.toStringAsFixed(0)}');
}
}
Selanjutnya, kita impor file tersebut di dalam bin/belajar_dart_oop.dart dan membuat dua objek (instance) yang terpisah:
// File: bin/belajar_dart_oop.dart
import 'package:belajar_dart_oop/rekening_bank.dart';
void main() {
// Instansiasi objek pertama dari class RekeningBank
// (Di Dart, kata kunci 'new' tidak perlu ditulis)
var rekeningBudi = RekeningBank();
rekeningBudi.nomorRekening = '100-201';
rekeningBudi.namaPemilik = 'Budi Santoso';
rekeningBudi.saldo = 500000;
// Instansiasi objek kedua — memiliki state yang sepenuhnya terpisah dari rekeningBudi
var rekeningSiti = RekeningBank();
rekeningSiti.nomorRekening = '100-202';
rekeningSiti.namaPemilik = 'Siti Aminah';
rekeningBudi.setorTunai(250000);
rekeningBudi.tarikTunai(100000);
rekeningSiti.tarikTunai(50000); // Gagal karena saldo awal Siti masih 0
print('\n--- Status Akhir Rekening ---');
rekeningBudi.tampilkanInfo();
rekeningSiti.tampilkanInfo();
}
Jalankan dengan dart run di terminal, maka keluarannya adalah:
Berhasil menyetor Rp250000 ke rekening Budi Santoso.
Berhasil menarik Rp100000 dari rekening Budi Santoso.
Gagal menarik Rp50000: Saldo Siti Aminah tidak mencukupi.
--- Status Akhir Rekening ---
[100-201] Pemilik: Budi Santoso | Saldo: Rp650000
[100-202] Pemilik: Siti Aminah | Saldo: Rp0
Perhatikan bahwa di Dart modern kita memanggil RekeningBank() secara langsung tanpa kata kunci new. Setiap perubahan pada rekeningBudi.saldo sama sekali tidak memengaruhi rekeningSiti.saldo karena keduanya adalah dua objek berbeda di memori.
Merangkai operasi dengan cascade notation (..)
Pada kode di atas, kita mengulang penulisan variabel rekeningBudi. berkali-kali saat mengisi properti dan memanggil method. Dart menyediakan operator khas bernama cascade notation (..) yang memungkinkan kita menjalankan serangkaian pengisian field maupun pemanggilan method pada satu objek yang sama secara berurutan.
Berbeda dengan titik tunggal (.) yang mengembalikan hasil dari method yang dipanggil, operator dua titik (..) selalu mengembalikan objek aslinya, sehingga kita bisa langsung menyambung operasi berikutnya:
// File: bin/belajar_dart_oop.dart
import 'package:belajar_dart_oop/rekening_bank.dart';
void main() {
// Mengisi properti dan memanggil beberapa method sekaligus dengan operator cascade (..)
var rekeningAndi = RekeningBank()
..nomorRekening = '100-203'
..namaPemilik = 'Andi Wijaya'
..saldo = 300000
..setorTunai(150000)
..tampilkanInfo();
// Jika variabel objek bertipe nullable (RekeningBank?), gunakan null-shorting cascade (?..)
RekeningBank? rekeningOpsional;
rekeningOpsional
?..namaPemilik = 'Tamu'
..tampilkanInfo(); // Aman: seluruh rangkaian dilewati jika rekeningOpsional bernilai null
}
Keluaran program:
Berhasil menyetor Rp150000 ke rekening Andi Wijaya.
[100-203] Pemilik: Andi Wijaya | Saldo: Rp450000
Kaitannya dengan Flutter / Dunia Nyata: Pemisahan kode ke dalam folder
lib/dengan pernyataanimport 'package:nama_proyek/...'adalah cara standar yang persis sama saat Anda membangun aplikasi Flutter. Selain itu, operator cascade (..) sangat sering dipakai di Flutter ketika mengonfigurasi objek pengontrol animasi, Paint pada kanvas grafis, atau membangun permintaan jaringan HTTP.
Coba sendiri
Di dalam proyek belajar_dart_oop yang telah Anda buat di VS Code:
- Tambahkan satu method baru pada
class RekeningBankdilib/rekening_bank.dartbernamavoid transferKe(RekeningBank tujuan, double nominal). - Di dalam
transferKe, panggiltarikTunai(nominal). JikatarikTunaimengembalikantrue, panggiltujuan.setorTunai(nominal)dan cetak pesan bahwa transfer berhasil. - Di dalam
bin/belajar_dart_oop.dart, lakukan transfer sebesarRp200.000darirekeningBudikerekeningSiti, lalu jalankandart rundi terminal untuk memverifikasi saldo keduanya.
Kesalahan yang sering terjadi
| Kesalahan Umum | Penyebab | Solusi yang Benar |
|---|---|---|
| Error: Non-nullable instance field 'nama' must be initialized | Mendeklarasikan field String nama; di dalam class tanpa nilai awal dan tanpa constructor di Dart 3. |
Beri nilai awal (String nama = '';), jadikan nullable (String? nama;), atau inisialisasi melalui constructor (dibahas di Materi 10). |
Menulis nama class dengan huruf kecil (class rekening_bank) |
Melanggar panduan resmi Effective Dart yang menyulitkan pembedaan antara nama tipe dan nama variabel. | Gunakan UpperCamelCase (PascalCase) untuk nama class (class RekeningBank) dan snake_case.dart untuk nama file (rekening_bank.dart). |
Mencampur . dan .. dalam satu rangkaian (obj..setor(100).tampilkanInfo()) |
Menaruh .tampilkanInfo() setelah setor(100) mencoba memanggil method pada hasil kembalian setor (void), bukan pada obj. |
Gunakan .. secara konsisten di setiap baris rangkaian: obj..setor(100)..tampilkanInfo();. |
Mengimpor file di dalam lib/ menggunakan jalur relatif keluar folder (import '../lib/...') |
Membuat jalur impor mudah rusak jika file di dalam bin/ atau lib/ dipindahkan ke subfolder lain. |
Gunakan format package import: import 'package:nama_paket_di_pubspec/nama_file.dart';. |
Latihan
Kerjakan tiga latihan berikut di proyek VS Code Anda (atau di DartPad jika Anda belum selesai menginstal SDK):
- Class Pengatur Waktu (
TimerPomodoro): Buatclass TimerPomodoroyang memiliki fieldString judulTugas = 'Belajar Dart',int durasiMenit = 25, danbool sedangBerjalan = false. Buat methodmulai(),jeda(), danreset(int menitBaru)yang mengubah state tersebut dan mencetak statusnya. Uji dimain()menggunakan operator cascade (..). - Class Produk Inventaris Gudang: Buat
class ProdukGudangdengan fieldString kode,String nama,int hargaSatuan, danint stok. Tambahkan methodint nilaiTotalStok()yang mengembalikanhargaSatuan * stok, serta methodvoid tambahStok(int jumlah). Buat tiga objek produk dimain()dan tampilkan nilai total stok masing-masing. - Interaksi Antar Objek (
KeranjangBelanja): Buatclass ItemBelanja(berisinama,harga,jumlahbeserta methodint subtotal()) danclass KeranjangBelanjayang menyimpanList<ItemBelanja> daftarItem = []. Tambahkan methodtambahItem(ItemBelanja item)danint hitungTotalBelanja()padaKeranjangBelanja.
Kunci jawaban dan pembahasan
Latihan 1
class TimerPomodoro {
String judulTugas = 'Belajar Dart';
int durasiMenit = 25;
bool sedangBerjalan = false;
void mulai() {
sedangBerjalan = true;
print('Mulai fokus: "$judulTugas" selama $durasiMenit menit.');
}
void jeda() {
sedangBerjalan = false;
print('Timer "$judulTugas" dijeda.');
}
void reset(int menitBaru) {
sedangBerjalan = false;
durasiMenit = menitBaru;
print('Timer direset menjadi $durasiMenit menit.');
}
}
void main() {
TimerPomodoro()
..judulTugas = 'Latihan Class & Object'
..mulai()
..jeda()
..reset(45)
..mulai();
}
Pembahasan: Operator cascade (..) memungkinkan kita menginisialisasi properti judulTugas lalu memanggil empat method berturut-turut pada objek TimerPomodoro yang sama bahkan tanpa perlu menyimpannya ke variabel terlebih dahulu.
Latihan 2
class ProdukGudang {
String kode = '';
String nama = '';
int hargaSatuan = 0;
int stok = 0;
void tambahStok(int jumlah) {
if (jumlah > 0) {
stok += jumlah;
}
}
int nilaiTotalStok() => hargaSatuan * stok;
void cetakRingkasan() {
print('[$kode] $nama | Stok: $stok | Nilai Aset: Rp${nilaiTotalStok()}');
}
}
void main() {
var keyboard = ProdukGudang()
..kode = 'KB-01'
..nama = 'Mechanical Keyboard'
..hargaSatuan = 450000
..stok = 10
..tambahStok(5);
var mouse = ProdukGudang()
..kode = 'MS-02'
..nama = 'Wireless Mouse'
..hargaSatuan = 180000
..stok = 20;
keyboard.cetakRingkasan();
mouse.cetakRingkasan();
}
Pembahasan: Method nilaiTotalStok() berada langsung di dalam class ProdukGudang sehingga ia dapat mengakses field hargaSatuan dan stok milik objek itu sendiri tanpa perlu dikirim ulang lewat parameter.
Latihan 3
class ItemBelanja {
String nama = '';
int harga = 0;
int jumlah = 1;
int subtotal() => harga * jumlah;
}
class KeranjangBelanja {
List<ItemBelanja> daftarItem = [];
void tambahItem(ItemBelanja item) {
daftarItem.add(item);
print('Menambahkan ${item.jumlah}x ${item.nama} (Subtotal: Rp${item.subtotal()})');
}
int hitungTotalBelanja() {
return daftarItem.fold(0, (total, item) => total + item.subtotal());
}
}
void main() {
var item1 = ItemBelanja()
..nama = 'Buku OOP Dart'
..harga = 95000
..jumlah = 2;
var item2 = ItemBelanja()
..nama = 'Buku Flutter Dasar'
..harga = 120000
..jumlah = 1;
var keranjang = KeranjangBelanja()
..tambahItem(item1)
..tambahItem(item2);
print('Total seluruh keranjang: Rp${keranjang.hitungTotalBelanja()}');
}
Pembahasan: Sebuah class dapat menyimpan koleksi berisi objek dari class lain (List<ItemBelanja>). Kita juga menggabungkan ilmu dari Bab 2 (.fold()) di dalam method hitungTotalBelanja() untuk menjumlahkan hasil item.subtotal() dari seluruh objek di dalam keranjang.
Discussion (0)
We store your optional name, your comment, and technical hashes (not your raw IP address) to prevent spam. A functional cookie lets you edit or delete your own comments from this browser. To request deletion, use the contact page.
No comments yet. Start the discussion.