Dart Asynchronous

Secara harfiah, asynchronous berarti “tidak terjadi pada waktu yang sama”. Dalam pemrograman, ini adalah teknik untuk menjalankan operasi yang memakan waktu lama tanpa memblokir eksekusi program. Ini sangat penting untuk menjaga aplikasi tetap responsif, terutama saat melakukan operasi input/output (I/O) seperti:

  • Mengambil data dari internet (API).
  • Membaca atau menulis file dari disk.
  • Terhubung ke database.

Pada Dart, hasil dari operasi asinkron direpresentasikan oleh sebuah objek Future. Sebuah Future adalah janji bahwa suatu nilai (atau error) akan tersedia di masa depan. Future memiliki tiga status:

  • Uncompleted: Awalnya, operasi belum selesai.
  • Completed with a value: Operasi selesai dengan sukses dan mengembalikan nilai.
  • Completed with an error: Operasi gagal.

Mendeklarasikan dan Menggunakan Future

Anda dapat menggunakan Future untuk menyimulasikan operasi yang memakan waktu.

// Ini adalah Future yang akan selesai dalam 2 detik
Future<String> ambilDataDariServer() {
  return Future.delayed(Duration(seconds: 2), () {
    return 'Data berhasil diambil!';
  });
}

Untuk berinteraksi dengan Future, ada dua cara utama:

1. Menggunakan .then(), .catchError(), .whenComplete()

Ini adalah pendekatan lama yang berbasis callback.

  • .then(): Akan dieksekusi ketika Future selesai dengan sukses.
  • .catchError(): Akan dieksekusi jika terjadi error.
  • .whenComplete(): Selalu dieksekusi, baik sukses maupun gagal.
ambilDataDariServer().then((data) {
  print(data); // Output: Data berhasil diambil!
}).catchError((error) {
  print('Terjadi kesalahan: $error');
}).whenComplete(() {
  print('Proses pengambilan data selesai.');
});

print('Menunggu data...'); // Ini akan dicetak terlebih dulu

Dalam contoh di atas, “Menunggu data…” dicetak terlebih dahulu karena pemanggilan ambilDataDariServer() bersifat non-blocking.

2. async dan await

Cara modern dan lebih disarankan untuk menangani Future adalah dengan menggunakan kata kunci async dan await. Ini membuat kode asinkron terlihat dan terasa seperti kode sinkron yang berurutan.

  • async: Digunakan di depan deklarasi fungsi untuk menandakan bahwa fungsi tersebut akan melakukan operasi asinkron dan mengembalikan sebuah Future.
  • await: Digunakan untuk “menunggu” selesainya sebuah Future. Kata kunci ini hanya bisa digunakan di dalam fungsi yang ditandai dengan async.

Contoh penggunaan async dan await

Future<void> prosesData() async {
  print('Memulai proses...');
  try {
    // Menunggu hingga future selesai, lalu simpan hasilnya di variabel
    String data = await ambilDataDariServer();
    print(data); // Output: Data berhasil diambil!
  } catch (e) {
    print('Terjadi kesalahan: $e');
  } finally {
    print('Proses selesai.');
  }
}

void main() {
  prosesData();
  print('Fungsi prosesData() dipanggil.'); // Ini akan dicetak duluan
}

Meskipun kode di dalam prosesData() terlihat berurutan, eksekusi main akan terus berlanjut. prosesData() akan “berhenti” pada baris await dan akan melanjutkan eksekusinya setelah ambilDataDariServer() selesai.

3. async* dan Stream

Untuk operasi yang menghasilkan serangkaian nilai dari waktu ke waktu (seperti streaming data dari internet atau event dari UI), Dart menggunakan Stream.

  • Stream: Representasi dari serangkaian data asinkron.
  • async*: Digunakan pada fungsi untuk mengembalikan sebuah Stream. Di dalam fungsi async*, Anda bisa menggunakan kata kunci yield untuk mengirimkan nilai.

Contoh penggunaan Stream dan async*

Stream<int> hitungMundur(int dari) async* {
  for (int i = dari; i >= 0; i--) {
    await Future.delayed(Duration(seconds: 1));
    yield i; // Mengirimkan nilai i ke stream
  }
}

void main() {
  final stream = hitungMundur(5);

  stream.listen((angka) {
    print('Hitungan: $angka');
  });

  print('Memulai hitungan mundur...');
}

Dalam contoh ini, main akan terus berjalan. stream.listen() akan menerima nilai satu per satu saat hitungMundur() mengirimkannya dengan yield.

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Concurrency

Concurrency, atau konkurensi, adalah kemampuan program untuk menjalankan beberapa tugas secara simultan. Ini tidak berarti tugas-tugas itu dijalankan pada waktu yang persis sama, melainkan bahwa program dapat beralih antar tugas dengan cepat, sehingga tampak berjalan bersamaan. Concurrency sangat penting dalam pengembangan aplikasi modern, terutama saat menangani operasi yang memakan waktu lama, seperti pengambilan data dari internet, membaca file dari disk, atau melakukan komputasi berat.

Dart mencapai concurrency melalui konsep Isolates.

Isolates: Otak Terpisah

Bayangkan setiap Isolate 馃 sebagai “otak” kecil yang terisolasi dari otak-otak lainnya dalam program Anda. Setiap Isolate memiliki memori sendiri dan berjalan di single-threaded event loop yang terpisah. Ini berarti setiap Isolate:

  1. Tidak berbagi memori: Isolasi ini mencegah masalah race condition (di mana dua atau lebih proses mencoba mengakses atau memanipulasi data yang sama secara bersamaan) yang sering terjadi di bahasa pemrograman multi-threaded tradisional.
  2. Berjalan secara independen: Satu Isolate tidak akan memblokir (menghentikan) Isolate lainnya.

Jika kita memiliki tugas yang sangat berat dan memakan waktu lama, seperti komputasi matematika kompleks, menjalankannya di Isolate terpisah akan mencegah aplikasi kita “freeze” atau macet. Aplikasi utama (Isolate utama) akan tetap responsif untuk menangani UI, sementara tugas berat tersebut berjalan di latar belakang.

Cara Berkomunikasi antar Isolates

Karena Isolate tidak berbagi memori, mereka berkomunikasi dengan cara berkirim pesan (message passing). Cara kerjanya seperti ini:

  1. Isolate A (utama) membuat Isolate B (baru).
  2. Isolate B menyelesaikan tugasnya dan mengemas hasilnya ke dalam sebuah pesan.
  3. Isolate B mengirim pesan tersebut kembali ke Isolate A.
  4. Isolate A menerima pesan dan mengambil hasilnya.

Ini mirip seperti kita dan teman yang mengerjakan tugas di ruangan terpisah. Kita tidak bisa langsung mengambil kertas di meja teman kita, tetapi kita bisa melempar pesan melalui jendela untuk bertukar informasi.

Implementasi Dasar dengan Isolate.spawn

Fungsi Isolate.spawn adalah cara paling umum untuk membuat Isolate baru. Fungsi ini menerima dua argumen:

  1. entryPoint: Sebuah fungsi level-atas (atau fungsi statis) yang akan dijalankan di Isolate baru. Fungsi ini akan menjadi “otak” dari Isolate baru.
  2. message: Data yang akan dikirim sebagai argumen ke entryPoint saat Isolate dimulai.

Contoh sederhana: Menghitung bilangan prima yang sangat besar di latar belakang.

import 'dart:io';
import 'dart:isolate';

// Fungsi entry point untuk Isolate baru
void findPrimes(SendPort sendPort) {
  // Komputasi berat: Mencari bilangan prima hingga 100.000
  final primes = <int>[];
  for (int i = 2; i <= 100000; i++) {
    bool isPrime = true;
    for (int j = 2; j <= i / 2; j++) {
      if (i % j == 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) {
      primes.add(i);
    }
  }
  // Kirim hasil kembali ke Isolate utama
  sendPort.send(primes.length);
}

void main() async {
  print('Isolate utama: Mulai mencari bilangan prima...');

  // Membuat ReceivePort untuk menerima pesan dari Isolate baru
  final receivePort = ReceivePort();

  // Memulai Isolate baru dan mengirim SendPort-nya
  await Isolate.spawn(findPrimes, receivePort.sendPort);

  // Menunggu pesan dari Isolate baru
  final result = await receivePort.first;

  print('Isolate utama: Selesai! Ditemukan $result bilangan prima.');
  print('Isolate utama: Program selesai.');
}

Perbedaan Utama: Asynchronous vs. Concurrent

Sangat penting untuk membedakan antara asynchronous dan concurrent di Dart.

  • Asynchronous (Asynchronous Programming): Menggunakan async/await dan Future untuk menangani operasi yang membutuhkan waktu, tetapi tanpa memblokir single-thread utama. Selama operasi Future menunggu, thread utama dapat melakukan tugas lain. Ini ideal untuk I/O (input/output) seperti mengambil data dari internet karena CPU tidak perlu menunggu.
  • Concurrent (Concurrency): Menggunakan Isolates untuk benar-benar menjalankan kode secara paralel di event loop terpisah. Ini ideal untuk tugas yang memakan banyak CPU (CPU-intensive).

Pahami bahwa async/await pada dasarnya adalah asynchronous, bukan concurrent. Jika kita menjalankan komputasi berat di async tanpa Isolate, thread utama tetap akan terblokir. Gunakan Isolate hanya ketika kita memiliki tugas yang benar-benar akan membuat “freeze” aplikasi kita.

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Sealed Classes

sealed class adalah fitur baru yang diperkenalkan di Dart 3. Secara sederhana, sealed class adalah sebuah kelas abstrak yang memiliki himpunan subclass yang telah ditentukan, dan semua subclass tersebut harus dideklarasikan di dalam library (file .dart) yang sama dengan sealed class itu sendiri.

Tujuan utama dari sealed class adalah untuk membuat hierarki kelas yang “tertutup” atau “terbatas”. Artinya, kita hanya bisa membuat subclass dari kelas sealed di lokasi yang sama dengan kelas tersebut. Ini mencegah developer lain (terutama dari package lain) membuat subclass baru dari kelas sealed Anda.

Kapan menggunakan sealed class? Anda harus menggunakan sealed class saat kita memiliki kelas yang merepresentasikan status atau varian yang terbatas dan saling eksklusif. Contoh klasik adalah status koneksi jaringan atau hasil dari sebuah operasi asinkron.

Keunggulan Utama sealed class

  1. Kontrol Hierarki: kita memegang kendali penuh atas subclass yang diizinkan, mencegah penambahan subclass yang tidak terduga di luar library Anda.
  2. Pengecekan Komprehensif (Exhaustive Checking): Ini adalah fitur yang paling powerful dari sealed class. Ketika kita menggunakan switch atau if pada objek sealed class, Dart akan secara otomatis memeriksa apakah kita telah menangani semua kemungkinan subclass. Jika ada yang terlewat, analyser (bagian dari IDE yang memeriksa kode) akan memberikan peringatan atau error. Ini menjamin kode kita lebih aman dan tidak akan gagal karena kasus yang tidak terduga.
  3. Tidak Perlu Klausa default: Karena Dart tahu bahwa semua subclass sudah tercakup, kita tidak perlu lagi menggunakan klausa default pada switch. Ini membuat kode lebih bersih dan lebih aman.

Contoh Penggunaan Sealed Class

sealed class Vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Hanya class di file ini yang boleh extends/implements/mixin Vehicle
class Car extends Vehicle {
  @override
  void startEngine() {
    print("Car engine started");
  }
}

class Bike extends Vehicle {
  @override
  void startEngine() {
    print("Bike engine started");
  }
}

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Jika kita jalankan maka akan seperti berikut

$ dart run SealedClass.dart 
Car engine started
Engine stopped
Bike engine started
Engine stopped

Perbedaan dengan Final dan Interface Class

  • final class:聽Tidak bisa diwarisi sama sekali.
  • sealed class:聽Hanya bisa diwarisi di file yang sama.
  • interface class:聽Hanya bisa diimplementasikan, tidak bisa diinstansiasi langsung.

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Final Classes

final class adalah fitur di Dart yang digunakan untuk mencegah class tersebut diwarisi (extends) atau diimplementasikan (implements) oleh class lain. Artinya, class yang dideklarasikan dengan kata kunci final hanya bisa digunakan secara langsung, tidak bisa menjadi parent class

Buat file dengan nama FinalClass.dart

final class vehile {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Extending the final class error canont be extended
class Car extends vehile {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

// Impelement the final class error canont be extended
class Bike implements vehile {
  @override
  void startEngine() {
    print("Bike engine started");
  }

  @override
  void stopEngine() {
    print("Bike engine stopped");
  }
}

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Ketika kita jalankan maka akan terjadi error seperti berikut :

$ dart run FinalClass.dart 
FinalClass.dart:12:7: Error: The type 'Car' must be 'base', 'final' or 'sealed' because the supertype 'vehile' is 'final'.
Try adding 'base', 'final', or 'sealed' to the type.
class Car extends vehile {
      ^
FinalClass.dart:25:7: Error: The type 'Bike' must be 'base', 'final' or 'sealed' because the supertype 'vehile' is 'final'.
Try adding 'base', 'final', or 'sealed' to the type.
class Bike implements vehile {

Penjelasan Kode: Interface Class di Dart

Pada contoh di atas, vehile adalah sebuah final class.

Kenapa Terjadi Error?

1. Mencoba Extends Final Class, Kode di atas akan error karena聽vehile聽adalah final class, sehingga tidak boleh diwarisi oleh class lain.

2. Mencoba Implements Final Class, Begitu juga saat mencoba mengimplementasikan final class.

Tujuan Final Class

  • Keamanan kode:聽Mencegah perubahan perilaku class yang tidak diinginkan melalui pewarisan.
  • Optimasi:聽Compiler bisa melakukan optimasi lebih baik karena tahu class tersebut tidak akan diwarisi.
  • Desain API:聽Cocok untuk class yang memang tidak boleh diubah perilakunya oleh developer lain.

Kesimpulan

  • Gunakan聽final class聽jika ingin membuat class yang聽tidak boleh diwarisi atau diimplementasikan.
  • Jika mencoba mewarisi atau mengimplementasikan final class, akan muncul error dari Dart.

Untuk聽memanggil atau menggunakan final class聽di Dart, kita聽tidak bisa mewarisi (extends) atau mengimplementasikan (implements)聽class tersebut.
Namun, kita tetap bisa聽membuat objek langsung dari final class聽dan memanggil method-method yang ada di dalamnya.

Contoh Penggunaan Final Class

final class Vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

void main() {
  var myVehicle = Vehicle(); // Membuat objek langsung dari final class
  myVehicle.startEngine();   // Memanggil method
  myVehicle.stopEngine();
}

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Interface Classes

Dalam Dart, untuk mebuat calss interface dalam dart kita bisa mengunakan kata kunci interface. Meskipun pada dart, setiap class secara implisit adalah sebuah interface. Ini berarti kita dapat menggunakan kata kunci implements pada sebuah class untuk mengimplementasikan interface dari class lain.

  • Ketika sebuah class mengimplementasikan sebuah interface, ia harus menyediakan implementasi (body) untuk semua properti dan method dari interface tersebut.
  • Ini berguna untuk memastikan sebuah class memenuhi “kontrak” tertentu, tanpa perlu mewarisi implementasinya.

Buat file dengan nama InterfaceClass.dart

interface class vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Extending the interface class
class Car extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

// Another class implementing the interface class
class Bike implements vehicle {
  @override
  void startEngine() {
    print("Bike engine started");
  }

  @override
  void stopEngine() {
    print("Bike engine stopped");
  }
}

// Main function to demonstrate the usage
void main() {
  print("Interface Class Extends:");
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  print("Interface Class Implements:");
  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Contoh Output:

$ dart run InterfaceClass.dart
Interface Class Extends:
Engine started
Engine stopped
Interface Class Implements:
Bike engine started
Bike engine stopped

Penjelasan Kode: Interface Class di Dart

1. Interface Class vehicle

interface class vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}
  • interface class adalah fitur baru di Dart yang memungkinkan sebuah class hanya bisa di-extend atau di-implement oleh class lain, tapi tidak bisa di-instansiasi langsung.
  • Pada contoh di atas, vehicle mendefinisikan dua method: startEngine() dan stopEngine(), yang masing-masing mencetak pesan ke konsol.

2. Extending Interface Class: Car

class Car extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}
  • Car adalah class yang mewarisi (extends) dari vehicle.
  • Method startEngine() dan stopEngine() di-override untuk menunjukkan bahwa class turunan bisa mengubah atau menambah perilaku dari method parent-nya.
  • Pada contoh ini, method hanya memanggil method dari parent (super.startEngine()), tapi bisa dikembangkan sesuai kebutuhan.

3. Implementasi Lain: Bike

class Bike extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

Bike juga mewarisi dari vehicle dan meng-override kedua method dengan cara yang sama seperti Car.

4. Fungsi Main

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}
  • Pada fungsi main, objek Car dan Bike dibuat.
  • Setiap objek memanggil method startEngine() dan stopEngine(), sehingga akan mencetak pesan ke konsol.

Kesimpulan

  • interface class di Dart sangat berguna untuk membuat kontrak perilaku yang harus diikuti oleh class turunan.
  • Tidak bisa membuat objek langsung dari vehicle, tapi bisa membuat objek dari class turunannya (CarBike).
  • Cocok digunakan untuk arsitektur aplikasi yang membutuhkan konsistensi perilaku pada beberapa class turunan.

Sample source code bisa di download di github pada link berikut dart-tutorial

hyvercode

Dart CLI System Information

Dart CLI System Information, adalah program console sederhana untuk menampilkan informasi System Operasi atau Hardware yang kita gunakan, struktur project kali ini cukup simple seperti berikut:

Bisa kita lihat program sederhana ini terdidiri dari 2 file utama :

  1. main.dart, ini adalah file yang akan kita gunakan untuk menuliskan code / script kita.
  2. pubspec.yaml, ini adalah file library management dimana file ini berfunsgi untuk mendeklarasikan konfigurasi program maupun library2 yang akan kita gunakan.

Langkanh pertama buat folder dengan nama dartSysFo atau sesuaikan dengan yang dinginkan kemudian buat file puspec.yaml seperti berikut :

name: dart_system_info

environment:
  sdk: '^3.8.0'

dependencies:
  system_info: ^1.0.1

dev_dependencies:
  test: ^1.24.0 # Or the latest version

Disini kita mengunakan dart SDK versi ‘^3.8.0’ dan library atau dependencies system_info: ^1.0.1, kemudian jalankan perintah berikut pada terminal / cmd untuk download library yang kita butuhkan

$ dart pub get

langkah berikutnya buat file main.dart, kurang lebih isinya seperti berikut :

import 'dart:io';

import 'package:dart_sqlite/models/Product.dart';
import 'lib/database/database_helper.dart';

import 'dart:io';
import 'package:system_info/system_info.dart';

const int MEGABYTE = 1024 * 1024;
int cpu = 1;
void main() {
  stdout.writeln();
  stdout.writeln("Welcome to Dart Programming System Information CLI");
  print(
    "========================= Information Detail =========================",
  );
  stdout.writeln();
  print("Operatig System         : ${SysInfo.operatingSystemName}");
  print("Operatig System Version : ${SysInfo.operatingSystemVersion}");
  print("Kernel architecture     : ${SysInfo.kernelArchitecture}");
  print("Kernel bitness          : ${SysInfo.kernelBitness}");
  print("Kernel name             : ${SysInfo.kernelName}");
  print("Kernel version          : ${SysInfo.kernelVersion}");
  print("Operating system name   : ${SysInfo.operatingSystemName}");
  print("Operating system version: ${SysInfo.operatingSystemVersion}");
  print("User directory          : ${SysInfo.userDirectory}");
  print("User id                 : ${SysInfo.userId}");
  print("User name               : ${SysInfo.userName}");
  print("User space bitness      : ${SysInfo.userSpaceBitness}");
  var processors = SysInfo.processors;
  print("Number of processors    : ${processors.length}");
  for (var processor in processors) {
    print("  Cpu                   : ${cpu++}");
    print("  Architecture          : ${processor.architecture}");
    print("  Name                  : ${processor.name}");
    print("  Socket                : ${processor.socket}");
    print("  Vendor                : ${processor.vendor}");
  }
  print(
    "Total physical memory   : ${SysInfo.getTotalPhysicalMemory() ~/ MEGABYTE} MB",
  );
  print(
    "Free physical memory    : ${SysInfo.getFreePhysicalMemory() ~/ MEGABYTE} MB",
  );
  print(
    "Total virtual memory    : ${SysInfo.getTotalVirtualMemory() ~/ MEGABYTE} MB",
  );
  print(
    "Free virtual memory     : ${SysInfo.getFreeVirtualMemory() ~/ MEGABYTE} MB",
  );
  print(
    "Virtual memory use      : ${SysInfo.getVirtualMemorySize() ~/ MEGABYTE} MB",
  );
}

setalah file-file nya kita buat, sekarang kira jalankan aplikasi dengan perintah berikut :

$ dart run main.dart

Sample source code bisa didownload di link github berikut聽聽dart-tutorial

hyvercode

Dart Abstract Classes

Abstract Class , adalah class yang tidak bisa dibuat object Karena masih bersifat abstract, Abstract class memiliki method yang juga bersifat abstract atau belum memiliki implentasinya. Untuk membuat abstract class yaitu dengan menggunakan keyword abstract contoh pembuatan abstract class :

abstract class car{
  void onRun()
}

untuk membuat implementasi dari sebuah abstract class maka kita bisa embu芒t class implementatornya seperti berikut:

abstract class Car{
  void onRun();
  void onBreak();
}

class Truck implements Car{
  @override
  void onRun() {
    // TODO: implement onRun
    print("Truck run");
  }

  @override
  void onBreak() {
    // TODO: implement onBreak
    print("Truck on break");
  }

}

class Bus implements Car{
  @override
  void onBreak() {
    // TODO: implement onBreak
    print("Bus on break");
  }

  @override
  void onRun() {
    // TODO: implement onRun
    print("Bus on run");
  }

}

void main(){
  var truck = new Truck();
  truck.onRun();
  truck.onBreak();

  var bus = new Bus();
  bus.onRun();
  bus.onBreak();

}
//output

Truck run
Truck on break
Bus on run
Bus on break

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Methods

Methods adalah function yang memberikan perilaku dari sebuah object.

Instance Methods

Metode instance pada objek dapat mengakses variabel instance dan this. Metode distanceTo () dalam contoh berikut adalah contoh metode instance

import 'dart:math';

class Point {
  double x, y;

  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main(){
  var point = Point(10, 10);
  print("Value is "+point.distanceTo(point).toString());
}
Value is 0.0

Operators

Operator adalah metode instance dengan nama khusus. Dart memungkinkan kita menentukan operator dengan nama berikut:

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>

contoh penggunaan operator

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown.
  // 路路路
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

Getter and Setters

Getter dan setter adalah metode khusus yang menyediakan akses read dan write ke properti objek. Ingatlah bahwa setiap variabel contoh memiliki inplicit getter dan setter jika sesuai. Kita dapat membuat properti tambahan dengan menerapkan getter dan setter, menggunakan get dan set kata kunci:

class Rectangle {
  double left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

Dengan getter dan setter, kita dapat memulai dengan variabel instance, kemudian menggabungkannya dengan metode, semuanya tanpa mengubah kode klien.

Sample source code bisa di download di github pada link berikut聽dart-tutorial

hyvercode

Dart Constructors

Constructor adalah method kudus yang akan di eksekusi saat pembuatan object dari sebuah class. Biasanya constructor digunakan untuk insialisasi atau memperispakn data yang object butuhkan. Cara membuat constructor yaitu dengan membuat function dengan nama yang sama pengan nama class .

class Person{
  String name;
  int age;

  Person(String name,int age){
    this.name =name;
    this.age = age;
 }
}

Pola penugasan argumen constructor ke variabel instan sangat umum, Dart memiliki sugar sintaksis untuk membuatnya mudah:

class Person{
  String name;
  int age;

  Person(this.name,this.age);
}

Default Constructors

Jiak kita tidak membuat contsructor pada sebua class, maka secara default cobstructors akan disediakan. Default constructors tidak memiliki arguments dan memanggil constructors tanpa argumen di superclass.

Constructors aren’t inherited

Subclass tidak mewarisi konstruktor dari superclass mereka. Subclass yang mendeklarasikan tidak ada konstruktor hanya memiliki konstruktor default (tanpa argumen, tanpa nama).

Names Constructors

Untuk membuat sebuah class yang memiliki contructors lebin dari satu ( Multiple Constructors), Maka kita bisa memberi nama pada contractos yang kita buat:

class Person{
  String name;
  int age;

  Person(this.name,this.age);
  // named contractor
  Person.getName(String name){
    this.name =name;
 }
}

Ingat bahwa konstruktor tidak diwariskan, yang berarti konstruktor bernama superclass tidak diwarisi oleh subclass. Jika kita ingin subclass dibuat dengan konstruktor bernama yang ditentukan di superclass, Anda harus mengimplementasikan konstruktor itu di subclass.

Invoking a non-default superclass constructor

Secara default, konstruktor dalam subclass memanggil konstruktor tanpa argumen kelas super tersebut. Konstruktor superclass dipanggil di awal badan konstruktor. Jika daftar penginisialisasi juga digunakan, itu dijalankan sebelum superclass dipanggil. Singkatnya, urutan eksekusi adalah sebagai berikut:

  • initializer list
  • superclass鈥檚 no-arg constructor
  • main class鈥檚 no-arg constructor
class Employee extends Person {
  Employee() : super.fromJson(defaultData);
  // 路路路
}

Initializer list

Selain memanggil konstruktor superclass, kita juga bisa menginisialisasi variabel instan sebelum badan konstruktor dijalankan. Pisahkan penginisialisasi dengan koma.

Point.fromJson(Map<String, double> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

Redirecting constructors

Terkadang satu-satunya tujuan konstruktor adalah mengarahkan ke konstruktor lain di class yang sama. Badan konstruktor is empty, dengan panggilan konstruktor muncul setelah titik dua (:).

class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

Constant constructors

Jika kelas yang kita buat menghasilkan objek yang tidak pernah berubah, kita dapat membuat objek ini menjadi konstanta (const) waktu kompilasi. Untuk melakukan ini, tentukan konstruktor const dan pastikan bahwa semua variabel instan sudah final.

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

Factory Constructors

Gunakan factory keyword ketika melakukan implementasi constructor tanpa harus membuat instance baru dari sebuah class. factory mungkin mengembalikan instance dari cache atau mengembalikan instance dari subtype, Kasus penggunaan lain untuk konstruktor factory adalah menginisialisasi variabel final menggunakan logika yang tidak dapat ditangani dalam daftar penginisialisasi.

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

Sample source code can be downloaded on github at the following link聽聽聽聽聽dart-tutorial

hyvercode

Dart Instance Variables

Semua instance dari variable di generate implicit getter method. Non-final instance variable digenerate implicit setter method:

//code
class Person{
  String name;
  int age;

  Person(this.name,this.age);
}

void main(){
  var person = Person("Alfaz",30);
  
  print('Name : '+person.name);
  print('Age :'+person.age.toString());
}
//output
Name : Alfaz
Age :30

Jika kita menginisialisasi variable instance di mana dideklarasikan (bukan di constructor atau method), nilai harus ditetapkan saat instance dibuat, yaitu sebelum constructor dan daftar penginisialisasinya dijalankan.

Sample source code can be downloaded on github at the following link聽聽聽dart-tutorial

hyvercode