Flutter Docs
Dart

Pengantar

Dart adalah bahasa pemrograman berorientasi objek (OOP). Hampir semua hal di Dart adalah object — angka, string, fungsi, bahkan null. Setiap object adalah instance dari sebuah class yang mendefinisikan struktur dan perilakunya.

Di halaman ini, Anda akan mempelajari cara membuat class, membuat object, menggunakan berbagai jenis constructor, serta konsep OOP penting seperti inheritance, abstract class, interfaces, dan mixins.

Deklarasi Class dan Membuat Object

Class adalah blueprint (cetak biru) untuk membuat object. Di dalam class, Anda mendefinisikan properties (data) dan methods (perilaku).

// Deklarasi class sederhana
class Mobil {
  // Properties (atribut)
  String merk = '';
  String warna = '';
  int tahun = 0;

  // Method (perilaku)
  void klakson() {
    print('$merk: Beep beep!');
  }

  void info() {
    print('$merk $warna tahun $tahun');
  }
}

void main() {
  // Membuat object (instance) dari class Mobil
  var mobilku = Mobil();
  mobilku.merk = 'Toyota';
  mobilku.warna = 'Hitam';
  mobilku.tahun = 2023;

  mobilku.info();    // Toyota Hitam tahun 2023
  mobilku.klakson(); // Toyota: Beep beep!

  // Membuat object lain dari class yang sama
  var mobilTeman = Mobil();
  mobilTeman.merk = 'Honda';
  mobilTeman.warna = 'Putih';
  mobilTeman.tahun = 2024;

  mobilTeman.info(); // Honda Putih tahun 2024
}

Properties dan Methods

Properties menyimpan data, sedangkan methods mendefinisikan aksi yang bisa dilakukan object.

class Persegi {
  double sisi;

  // Constructor
  Persegi(this.sisi);

  // Method untuk menghitung luas
  double hitungLuas() {
    return sisi * sisi;
  }

  // Method untuk menghitung keliling
  double hitungKeliling() {
    return 4 * sisi;
  }

  // Method yang mengembalikan String
  String deskripsi() {
    return 'Persegi dengan sisi $sisi, luas ${hitungLuas()}, keliling ${hitungKeliling()}';
  }
}

void main() {
  var kotak = Persegi(5.0);

  print(kotak.sisi);            // 5.0
  print(kotak.hitungLuas());    // 25.0
  print(kotak.hitungKeliling());// 20.0
  print(kotak.deskripsi());
  // Persegi dengan sisi 5.0, luas 25.0, keliling 20.0
}

Constructor

Constructor adalah method khusus yang dipanggil saat membuat object baru. Dart mendukung beberapa jenis constructor.

Default Constructor

Jika Anda tidak mendefinisikan constructor, Dart menyediakan constructor default tanpa parameter.

class Kucing {
  String nama = 'Tanpa Nama';
  int umur = 0;
}

void main() {
  var kucing = Kucing(); // Menggunakan default constructor
  print(kucing.nama);    // Tanpa Nama
}

Constructor dengan Parameter

class Mahasiswa {
  String nama;
  String jurusan;
  int angkatan;

  // Constructor dengan parameter — shorthand syntax
  Mahasiswa(this.nama, this.jurusan, this.angkatan);
}

void main() {
  var mhs = Mahasiswa('Andi', 'Informatika', 2023);
  print('${mhs.nama} - ${mhs.jurusan} (${mhs.angkatan})');
  // Andi - Informatika (2023)
}

Named Constructor

Named constructor memungkinkan Anda membuat beberapa constructor dengan nama berbeda dalam satu class.

class Titik {
  double x;
  double y;

  // Constructor utama
  Titik(this.x, this.y);

  // Named constructor — titik di origin (0, 0)
  Titik.origin()
      : x = 0,
        y = 0;

  // Named constructor — titik dari Map
  Titik.dariMap(Map<String, double> map)
      : x = map['x'] ?? 0,
        y = map['y'] ?? 0;

  @override
  String toString() => 'Titik($x, $y)';
}

void main() {
  var t1 = Titik(3.0, 4.0);
  var t2 = Titik.origin();
  var t3 = Titik.dariMap({'x': 7.5, 'y': 2.3});

  print(t1); // Titik(3.0, 4.0)
  print(t2); // Titik(0.0, 0.0)
  print(t3); // Titik(7.5, 2.3)
}

Factory Constructor

Factory constructor tidak selalu membuat instance baru. Bisa mengembalikan instance dari cache, subclass, atau hasil komputasi lain.

class Warna {
  final int r, g, b;

  // Constructor biasa (private dengan underscore)
  const Warna._internal(this.r, this.g, this.b);

  // Factory constructor — mengembalikan instance yang sudah didefinisikan
  factory Warna(String nama) {
    switch (nama.toLowerCase()) {
      case 'merah':
        return Warna._internal(255, 0, 0);
      case 'hijau':
        return Warna._internal(0, 255, 0);
      case 'biru':
        return Warna._internal(0, 0, 255);
      default:
        return Warna._internal(0, 0, 0); // Hitam sebagai default
    }
  }

  @override
  String toString() => 'Warna(r: $r, g: $g, b: $b)';
}

void main() {
  var merah = Warna('merah');
  var biru = Warna('biru');
  var lainnya = Warna('ungu');

  print(merah);   // Warna(r: 255, g: 0, b: 0)
  print(biru);    // Warna(r: 0, g: 0, b: 255)
  print(lainnya); // Warna(r: 0, g: 0, b: 0)
}

Getter dan Setter

Getter dan setter memungkinkan Anda mengontrol akses ke properties. Gunakan keyword get dan set untuk mendefinisikannya.

class Lingkaran {
  double _radius; // Underscore menandakan private (konvensi)

  Lingkaran(this._radius);

  // Getter — mengakses nilai turunan
  double get radius => _radius;
  double get luas => 3.14159 * _radius * _radius;
  double get keliling => 2 * 3.14159 * _radius;
  double get diameter => _radius * 2;

  // Setter — mengontrol perubahan nilai
  set radius(double nilai) {
    if (nilai > 0) {
      _radius = nilai;
    } else {
      print('Radius harus positif!');
    }
  }
}

void main() {
  var lingkaran = Lingkaran(7.0);

  // Menggunakan getter (seperti mengakses property biasa)
  print('Radius: ${lingkaran.radius}');     // Radius: 7.0
  print('Diameter: ${lingkaran.diameter}'); // Diameter: 14.0
  print('Luas: ${lingkaran.luas}');         // Luas: 153.93791
  print('Keliling: ${lingkaran.keliling}'); // Keliling: 43.98226

  // Menggunakan setter
  lingkaran.radius = 10.0;
  print('Radius baru: ${lingkaran.radius}'); // Radius baru: 10.0

  lingkaran.radius = -5.0; // Radius harus positif!
  print('Radius tetap: ${lingkaran.radius}'); // Radius tetap: 10.0
}

Inheritance (Pewarisan)

Inheritance memungkinkan sebuah class mewarisi properties dan methods dari class lain menggunakan keyword extends. Class yang mewarisi disebut subclass, dan class yang diwarisi disebut superclass.

// Superclass (class induk)
class Hewan {
  String nama;
  int umur;

  Hewan(this.nama, this.umur);

  void makan() {
    print('$nama sedang makan');
  }

  void tidur() {
    print('$nama sedang tidur');
  }

  void info() {
    print('$nama, umur $umur tahun');
  }
}

// Subclass (class anak) — mewarisi semua dari Hewan
class Anjing extends Hewan {
  String ras;

  // Constructor subclass harus memanggil constructor superclass
  Anjing(super.nama, super.umur, this.ras);

  // Method tambahan khusus Anjing
  void gonggong() {
    print('$nama: Guk guk!');
  }
}

class Kucing extends Hewan {
  bool dalamRuangan;

  Kucing(super.nama, super.umur, {this.dalamRuangan = true});

  void mengeong() {
    print('$nama: Meow!');
  }
}

void main() {
  var anjing = Anjing('Buddy', 3, 'Golden Retriever');
  anjing.info();     // Buddy, umur 3 tahun (dari Hewan)
  anjing.makan();    // Buddy sedang makan (dari Hewan)
  anjing.gonggong(); // Buddy: Guk guk! (khusus Anjing)

  var kucing = Kucing('Mimi', 2);
  kucing.info();     // Mimi, umur 2 tahun (dari Hewan)
  kucing.tidur();    // Mimi sedang tidur (dari Hewan)
  kucing.mengeong(); // Mimi: Meow! (khusus Kucing)
}

Method Overriding

Subclass bisa menimpa (override) method dari superclass menggunakan anotasi @override. Ini memungkinkan subclass memberikan implementasi yang berbeda.

class Bentuk {
  String nama;

  Bentuk(this.nama);

  double hitungLuas() {
    return 0; // Default — akan di-override oleh subclass
  }

  @override
  String toString() => '$nama: luas = ${hitungLuas()}';
}

class PersegiPanjang extends Bentuk {
  double panjang;
  double lebar;

  PersegiPanjang(this.panjang, this.lebar) : super('Persegi Panjang');

  @override
  double hitungLuas() {
    return panjang * lebar;
  }
}

class Segitiga extends Bentuk {
  double alas;
  double tinggi;

  Segitiga(this.alas, this.tinggi) : super('Segitiga');

  @override
  double hitungLuas() {
    return 0.5 * alas * tinggi;
  }
}

void main() {
  var pp = PersegiPanjang(10, 5);
  var sg = Segitiga(8, 6);

  print(pp); // Persegi Panjang: luas = 50.0
  print(sg); // Segitiga: luas = 24.0

  // Polymorphism — variabel bertipe superclass bisa menampung subclass
  List<Bentuk> bentukList = [pp, sg];
  for (var bentuk in bentukList) {
    print('${bentuk.nama}: ${bentuk.hitungLuas()}');
  }
  // Persegi Panjang: 50.0
  // Segitiga: 24.0
}

Abstract Class

Abstract class tidak bisa di-instantiate secara langsung. Digunakan sebagai kontrak yang harus diimplementasikan oleh subclass. Method abstract tidak memiliki body — subclass wajib mengimplementasikannya.

// Abstract class — tidak bisa dibuat object-nya langsung
abstract class Kendaraan {
  String merk;

  Kendaraan(this.merk);

  // Method abstract — harus diimplementasikan oleh subclass
  void bergerak();
  double hitungPajak();

  // Method biasa — bisa langsung digunakan oleh subclass
  void info() {
    print('Kendaraan: $merk');
  }
}

class Mobil extends Kendaraan {
  int cc;

  Mobil(super.merk, this.cc);

  @override
  void bergerak() {
    print('$merk melaju di jalan raya');
  }

  @override
  double hitungPajak() {
    return cc * 50.0; // Pajak berdasarkan CC mesin
  }
}

class Sepeda extends Kendaraan {
  Sepeda(super.merk);

  @override
  void bergerak() {
    print('$merk dikayuh di jalur sepeda');
  }

  @override
  double hitungPajak() {
    return 0; // Sepeda tidak kena pajak
  }
}

void main() {
  // var k = Kendaraan('Test'); // Error: abstract class tidak bisa di-instantiate

  var mobil = Mobil('Toyota', 1500);
  mobil.info();       // Kendaraan: Toyota
  mobil.bergerak();   // Toyota melaju di jalan raya
  print('Pajak: Rp${mobil.hitungPajak()}'); // Pajak: Rp75000.0

  var sepeda = Sepeda('Polygon');
  sepeda.bergerak();  // Polygon dikayuh di jalur sepeda
  print('Pajak: Rp${sepeda.hitungPajak()}'); // Pajak: Rp0.0
}

Interfaces (implements)

Di Dart, setiap class bisa digunakan sebagai interface. Tidak ada keyword interface khusus. Gunakan implements untuk mengimplementasikan interface — class yang mengimplementasikan wajib menyediakan semua properties dan methods.

// Class ini berfungsi sebagai interface
class Printable {
  String formatOutput() => '';
}

class Storable {
  void simpan() {}
  void hapus() {}
}

// Mengimplementasikan beberapa interface sekaligus
class Dokumen implements Printable, Storable {
  String judul;
  String isi;

  Dokumen(this.judul, this.isi);

  @override
  String formatOutput() {
    return '=== $judul ===\n$isi';
  }

  @override
  void simpan() {
    print('Dokumen "$judul" disimpan');
  }

  @override
  void hapus() {
    print('Dokumen "$judul" dihapus');
  }
}

void main() {
  var doc = Dokumen('Laporan', 'Isi laporan bulanan...');

  print(doc.formatOutput());
  // === Laporan ===
  // Isi laporan bulanan...

  doc.simpan(); // Dokumen "Laporan" disimpan
  doc.hapus();  // Dokumen "Laporan" dihapus
}

Perbedaan extends vs implements

class A {
  void metode() {
    print('Implementasi dari A');
  }
}

// extends — mewarisi implementasi
class B extends A {
  // metode() sudah tersedia dari A, tidak wajib override
}

// implements — harus implementasi ulang semua method
class C implements A {
  @override
  void metode() {
    print('Implementasi baru dari C');
  }
}

void main() {
  B().metode(); // Implementasi dari A
  C().metode(); // Implementasi baru dari C
}

Mixins

Mixin adalah cara untuk berbagi kode antar beberapa class tanpa menggunakan inheritance. Gunakan keyword mixin untuk mendefinisikan dan with untuk menggunakannya. Satu class bisa menggunakan beberapa mixin sekaligus.

// Definisi mixin
mixin BisaTerbang {
  void terbang() {
    print('Sedang terbang di udara');
  }
}

mixin BisaBerenang {
  void berenang() {
    print('Sedang berenang di air');
  }
}

mixin BisaBerlari {
  int kecepatan = 0;

  void berlari(int kec) {
    kecepatan = kec;
    print('Berlari dengan kecepatan $kec km/jam');
  }
}

// Menggunakan mixin dengan keyword 'with'
class Bebek extends Hewan with BisaTerbang, BisaBerenang {
  Bebek(super.nama);
}

class Elang extends Hewan with BisaTerbang {
  Elang(super.nama);
}

class Kuda extends Hewan with BisaBerlari {
  Kuda(super.nama);
}

// Superclass sederhana
class Hewan {
  String nama;
  Hewan(this.nama);
}

void main() {
  var bebek = Bebek('Donald');
  bebek.terbang();  // Sedang terbang di udara
  bebek.berenang(); // Sedang berenang di air

  var elang = Elang('Garuda');
  elang.terbang();  // Sedang terbang di udara

  var kuda = Kuda('Spirit');
  kuda.berlari(60); // Berlari dengan kecepatan 60 km/jam
}

Mixin dengan Batasan (on)

Anda bisa membatasi mixin agar hanya bisa digunakan oleh class tertentu menggunakan keyword on.

class Musisi {
  String nama;
  Musisi(this.nama);

  void tampil() {
    print('$nama tampil di panggung');
  }
}

// Mixin ini hanya bisa digunakan oleh class yang extends Musisi
mixin Gitaris on Musisi {
  void mainGitar() {
    print('$nama memainkan gitar');
  }
}

mixin Vokalis on Musisi {
  void menyanyi() {
    print('$nama sedang menyanyi');
  }
}

class RockStar extends Musisi with Gitaris, Vokalis {
  RockStar(super.nama);
}

void main() {
  var bintang = RockStar('Andi');
  bintang.tampil();     // Andi tampil di panggung
  bintang.mainGitar();  // Andi memainkan gitar
  bintang.menyanyi();   // Andi sedang menyanyi
}

Static Members

Static members (properties dan methods) milik class itu sendiri, bukan milik instance. Diakses langsung melalui nama class tanpa membuat object.

class Matematika {
  // Static property
  static const double pi = 3.14159;
  static int _hitungan = 0;

  // Static method
  static double luasLingkaran(double radius) {
    _hitungan++;
    return pi * radius * radius;
  }

  static double kelilingLingkaran(double radius) {
    _hitungan++;
    return 2 * pi * radius;
  }

  static int get totalPerhitungan => _hitungan;
}

class Pengguna {
  static int _jumlahPengguna = 0;

  String nama;

  Pengguna(this.nama) {
    _jumlahPengguna++; // Increment setiap kali object dibuat
  }

  static int get jumlahPengguna => _jumlahPengguna;
}

void main() {
  // Akses static member langsung dari class — tanpa membuat object
  print(Matematika.pi);                     // 3.14159
  print(Matematika.luasLingkaran(7));       // 153.93791
  print(Matematika.kelilingLingkaran(7));   // 43.98226
  print('Total perhitungan: ${Matematika.totalPerhitungan}'); // 2

  // Static property untuk tracking jumlah instance
  var u1 = Pengguna('Andi');
  var u2 = Pengguna('Budi');
  var u3 = Pengguna('Citra');
  print('Jumlah pengguna: ${Pengguna.jumlahPengguna}'); // 3
}

Ringkasan

KonsepKeywordKeterangan
Classclass NamaClass {}Blueprint untuk membuat object
Objectvar obj = NamaClass()Instance dari sebuah class
ConstructorNamaClass(this.prop)Method khusus untuk inisialisasi object
Named constructorNamaClass.nama()Constructor alternatif dengan nama
Factory constructorfactory NamaClass()Constructor yang bisa mengembalikan instance yang sudah ada
Getter / Setterget prop => / set prop(v)Mengontrol akses ke properties
InheritanceextendsMewarisi properties dan methods dari superclass
Override@overrideMenimpa method dari superclass
Abstract classabstract classClass yang tidak bisa di-instantiate langsung
InterfaceimplementsMengimplementasikan semua members dari class lain
Mixinmixin + withBerbagi kode antar class tanpa inheritance
StaticstaticMembers milik class, bukan instance

Pemrograman berorientasi objek adalah fondasi utama Dart dan Flutter. Dengan memahami class, inheritance, dan mixins, Anda siap membangun aplikasi Flutter yang terstruktur dan mudah di-maintain.

Lanjutkan ke halaman Null Safety untuk mempelajari sistem null safety di Dart yang membantu mencegah null reference errors.

On this page