Flutter Docs
Dart

Pengantar

Di halaman sebelumnya kamu sudah belajar tentang tipe data dasar dan fungsi. Saatnya mendalami collections — struktur data yang menyimpan banyak nilai dalam satu variabel. Tanpa collections, kamu bakal harus bikin variabel terpisah untuk setiap data:

// Tanpa collection — tidak scalable
String nama1 = "Alice";
String nama2 = "Bob";
String nama3 = "Charlie";
String nama4 = "Diana";
// Bayangkan untuk 1000 data...

Dengan collection, semua data itu bisa disimpan rapi dalam satu struktur:

List<String> nama = ["Alice", "Bob", "Charlie", "Diana"];
// Satu variabel, banyak data. Jauh lebih mudah dikelola.

Dart punya tiga collection utama yang wajib kamu kuasai:

CollectionKarakteristikUse Case
ListBerurutan (indexed), boleh duplikatData berurutan, daftar item
SetUnik, tidak berurutanData tanpa duplikat, operasi himpunan
MapKey-value pairsData dengan label, lookup cepat

Selain itu, Dart punya fitur modern seperti spread operator, collection if/for, dan metode functional seperti map, where, reduce yang bikin kode collection jadi jauh lebih ekspresif. Yuk kita bedah satu-satu.


List

List adalah collection paling dasar di Dart. Mirip array di bahasa lain, tapi lebih powerful.

Membuat List

Ada beberapa cara membuat List:

void main() {
  // Literal list — paling umum
  List<String> buah = ['apel', 'mangga', 'jeruk'];
  print(buah); // [apel, mangga, jeruk]

  // Tipe bisa di-infer
  var hewan = ['kucing', 'anjing', 'ikan'];
  print(hewan); // [kucing, anjing, ikan]

  // List dengan tipe campuran (pakai dynamic atau Object?)
  var campuran = <dynamic>[1, 'dua', true, 3.14];
  print(campuran); // [1, dua, true, 3.14]

  // List kosong
  List<int> kosong = [];
  List<String> jugaKosong = List<String>.empty();
  print('kosong: $kosong, jugaKosong: $jugaKosong');

  // List dengan panjang tetap
  var tetap = List<int>.filled(5, 0);
  print(tetap); // [0, 0, 0, 0, 0]

  // List dari generator
  var generate = List<int>.generate(5, (i) => i * 2);
  print(generate); // [0, 2, 4, 6, 8]
}

Tabel Method List Penting

MethodDeskripsiContoh
add(value)Tambah satu elemen di akhirlist.add('baru')
addAll(iterable)Tambah banyak elemenlist.addAll(['a','b'])
insert(index, value)Sisip di posisi tertentulist.insert(0, 'awal')
remove(value)Hapus elemen pertama yang cocoklist.remove('foo')
removeAt(index)Hapus berdasarkan indexlist.removeAt(2)
removeWhere(test)Hapus semua yang memenuhi kondisilist.removeWhere((e) => e < 0)
contains(value)Cek apakah adalist.contains('x')
indexOf(value)Cari index pertamalist.indexOf('x')
sublist(start, [end])Ambil potongan listlist.sublist(1, 3)

Akses dan Modifikasi

void main() {
  var angka = [10, 20, 30, 40, 50];

  // Akses by index
  print(angka[0]); // 10
  print(angka[angka.length - 1]); // 50 — elemen terakhir

  // first, last — properti yang lebih readable
  print(angka.first); // 10
  print(angka.last); // 50

  // isEmpty, isNotEmpty
  print(angka.isEmpty); // false
  print(angka.isNotEmpty); // true

  // Modifikasi elemen
  angka[2] = 99;
  print(angka); // [10, 20, 99, 40, 50]

  // Menambah elemen
  angka.add(60);
  print(angka); // [10, 20, 99, 40, 50, 60]

  angka.addAll([70, 80]);
  print(angka); // [10, 20, 99, 40, 50, 60, 70, 80]

  // Menyisip
  angka.insert(1, 15);
  print(angka); // [10, 15, 20, 99, 40, 50, 60, 70, 80]

  // Menghapus
  angka.remove(99);
  print(angka); // [10, 15, 20, 40, 50, 60, 70, 80]

  angka.removeAt(0);
  print(angka); // [15, 20, 40, 50, 60, 70, 80]

  angka.removeLast();
  print(angka); // [15, 20, 40, 50, 60, 70]

  // removeWhere — hapus dengan kondisi
  angka.removeWhere((e) => e > 50);
  print(angka); // [15, 20, 40, 50]
}

Iterasi List

void main() {
  var nama = ['Alice', 'Bob', 'Charlie'];

  // for loop biasa
  for (int i = 0; i < nama.length; i++) {
    print('Index $i: ${nama[i]}');
  }

  // for-in — lebih idiomatic
  for (var n in nama) {
    print('Halo, $n!');
  }

  // forEach dengan function literal
  nama.forEach((n) => print('forEach: $n'));

  // forEach dengan named function
  void sapa(String orang) => print('Salam, $orang!');
  nama.forEach(sapa);
}

Spread Operator

Spread operator (...) dan null-aware spread (...?) adalah fitur yang sangat berguna untuk menggabungkan collection.

void main() {
  var ganjil = [1, 3, 5];
  var genap = [2, 4, 6];

  // Menggabungkan list
  var semua = [...ganjil, ...genap];
  print(semua); // [1, 3, 5, 2, 4, 6]

  // Bisa juga dengan addAll — tapi spread lebih ekspresif
  var caraLain = [0, ...ganjil, ...genap, 7];
  print(caraLain); // [0, 1, 3, 5, 2, 4, 6, 7]

  // Null-aware spread — aman walau null
  List<int>? nullable;
  var aman = [1, ...?nullable, 3];
  print(aman); // [1, 3] — tidak error

  // Tanpa null-aware, ini error
  // var error = [1, ...nullable, 3]; // ERROR!

  // Spread di dalam Set dan Map juga works
  var setA = {1, 2, 3};
  var setB = {3, 4, 5};
  var setGabung = {...setA, ...setB};
  print(setGabung); // {1, 2, 3, 4, 5} — Set otomatis unik
}

Collection If & For

Dart mendukung collection if dan collection for di dalam literal collection. Ini memungkinkan conditional dan looping langsung di dalam kurung siku atau kurung kurawal.

void main() {
  // --- Collection If ---
  bool includeDessert = true;
  bool includeMinuman = false;

  var menu = [
    'Nasi Goreng',
    'Mie Ayam',
    if (includeDessert) 'Es Krim',
    if (includeMinuman) 'Jus Jeruk',
  ];
  print(menu); // [Nasi Goreng, Mie Ayam, Es Krim]
  // 'Jus Jeruk' tidak masuk karena includeMinuman == false

  // Bisa juga dengan else (Dart 3.0+)
  bool isPremium = true;
  var diskon = [
    'Diskon 10%',
    if (isPremium) 'Diskon 20%' else 'Diskon 5%',
  ];
  print(diskon); // [Diskon 10%, Diskon 20%]

  // --- Collection For ---
  var listAngka = [1, 2, 3];
  var dikali10 = [
    for (var a in listAngka) a * 10,
  ];
  print(dikali10); // [10, 20, 30]

  // Kombinasi collection for + if di dalamnya
  var nilai = [85, 60, 92, 45, 78];
  var lulus = [
    for (var n in nilai)
      if (n >= 75) n,
  ];
  print(lulus); // [85, 92, 78]

  // Collection for bisa menghasilkan banyak elemen per iterasi
  var berpasangan = [
    for (var i = 0; i < 3; i++) i,
    for (var i = 3; i >= 0; i--) i,
  ];
  print(berpasangan); // [0, 1, 2, 3, 2, 1, 0]
}

Kalau tanpa collection if/for, kode di atas bakal jauh lebih panjang. Bandingkan:

// Tanpa collection if
var menu = ['Nasi Goreng', 'Mie Ayam'];
if (includeDessert) menu.add('Es Krim');
if (includeMinuman) menu.add('Jus Jeruk');

// Dengan collection if — lebih deklaratif
var menu = [
  'Nasi Goreng',
  'Mie Ayam',
  if (includeDessert) 'Es Krim',
  if (includeMinuman) 'Jus Jeruk',
];

Set

Set adalah collection yang menyimpan nilai unik — tidak ada duplikat. Set tidak punya urutan (unordered), jadi tidak bisa akses via index seperti List.

void main() {
  // Membuat Set
  Set<String> buahSet = {'apel', 'mangga', 'jeruk'};
  var hewanSet = <String>{'kucing', 'anjing'};

  // Set dengan duplikat — otomatis di-deduplikasi
  var denganDuplikat = {1, 2, 2, 3, 1, 3};
  print(denganDuplikat); // {1, 2, 3}
  print(denganDuplikat.length); // 3 (bukan 6)

  // Operasi dasar
  var set = <int>{};

  set.add(1);
  set.add(2);
  set.add(1); // diabaikan — sudah ada
  print(set); // {1, 2}

  set.addAll([3, 4, 5]);
  print(set); // {1, 2, 3, 4, 5}

  set.remove(3);
  print(set); // {1, 2, 4, 5}

  // Mengecek anggota
  print(set.contains(2)); // true
  print(set.contains(99)); // false

  // containsAll — cek beberapa sekaligus
  print(set.containsAll({1, 2})); // true
  print(set.containsAll({1, 99})); // false
}

Operasi Himpunan (Set Operations)

Ini adalah kekuatan utama Set — operasi matematis seperti union, intersection, dan difference.

void main() {
  var groupA = {1, 2, 3, 4, 5};
  var groupB = {4, 5, 6, 7, 8};

  // Union — gabungan kedua set
  var union = groupA.union(groupB);
  print('Union: $union'); // {1, 2, 3, 4, 5, 6, 7, 8}

  // Intersection — irisan (yang ada di keduanya)
  var intersection = groupA.intersection(groupB);
  print('Intersection: $intersection'); // {4, 5}

  // Difference — yang di A tapi tidak di B
  var diffAB = groupA.difference(groupB);
  print('A - B: $diffAB'); // {1, 2, 3}

  // Difference — yang di B tapi tidak di A
  var diffBA = groupB.difference(groupA);
  print('B - A: $diffBA'); // {6, 7, 8}

  // Symmetric difference — pakai union dan difference
  var symDiff = groupA.union(groupB).difference(groupA.intersection(groupB));
  print('Symmetric: $symDiff'); // {1, 2, 3, 6, 7, 8}

  // toSet() — konversi List ke Set (untuk deduplikasi)
  var listDenganDuplikat = [1, 2, 2, 3, 3, 3, 4];
  var unik = listDenganDuplikat.toSet();
  print(unik); // {1, 2, 3, 4}
  print(unik.toList()); // [1, 2, 3, 4] — balik ke List
}

Gunakan Set setiap kali kamu butuh menghilangkan duplikat atau operasi himpunan. Contoh nyata: filter daftar tag, cek permission overlap, dll.


Map

Map menyimpan data sebagai pasangan key-value. Key harus unik, value boleh duplikat.

void main() {
  // Membuat Map — literal
  Map<String, int> nilai = {
    'Alice': 90,
    'Bob': 85,
    'Charlie': 78,
  };

  // Type inference
  var ibukota = <String, String>{
    'Indonesia': 'Jakarta',
    'Malaysia': 'Kuala Lumpur',
    'Singapura': 'Singapura',
  };

  // Map kosong
  Map<String, dynamic> profil = {};
  var config = <String, String>{};

  // Akses value
  print(nilai['Alice']); // 90
  print(nilai['Unknown']); // null — key tidak ada return null

  // Modifikasi — assign langsung
  nilai['Alice'] = 95; // update
  nilai['Diana'] = 88; // tambah baru
  print(nilai);

  // Hapus
  nilai.remove('Charlie');
  print(nilai); // {Alice: 95, Bob: 85, Diana: 88}

  // Cek key
  print(nilai.containsKey('Bob')); // true
  print(nilai.containsKey('Eve')); // false

  // containsValue — jarang dipakai karena O(n)
  print(nilai.containsValue(95)); // true
}

Iterasi Map

void main() {
  var map = {'a': 1, 'b': 2, 'c': 3};

  // Iterasi entries (key + value)
  for (var entry in map.entries) {
    print('${entry.key} -> ${entry.value}');
  }

  // Iterasi keys
  for (var key in map.keys) {
    print('Key: $key');
  }

  // Iterasi values
  for (var value in map.values) {
    print('Value: $value');
  }

  // forEach dengan callback
  map.forEach((key, value) {
    print('forEach: $key = $value');
  });

  // Map entries diubah jadi List
  var entriesList = map.entries.toList();
  print(entriesList); // [MapEntry(a: 1), MapEntry(b: 2), MapEntry(c: 3)]
}

Method Penting Map

void main() {
  var data = <String, dynamic>{};

  // putIfAbsent — set value kalau key belum ada
  data.putIfAbsent('nama', () => 'Anonim');
  print(data); // {nama: Anonim}
  data.putIfAbsent('nama', () => 'Tidak Akan Terpakai');
  print(data); // {nama: Anonim} — tidak berubah

  // update — modifikasi value yang sudah ada
  var skor = {'Alice': 80, 'Bob': 90};
  skor.update('Alice', (v) => v + 10);
  print(skor); // {Alice: 90, Bob: 90}
  // Kalau key belum ada, pakai update dengan ifAbsent
  skor.update('Charlie', (v) => 100, ifAbsent: () => 85);
  print(skor); // {Alice: 90, Bob: 90, Charlie: 85}

  // addAll — gabungkan map lain
  var tambahan = {'Diana': 75, 'Eve': 95};
  skor.addAll(tambahan);
  print(skor);

  // Map.fromEntries — balikin iterable entries ke map
  var entries = [MapEntry('x', 10), MapEntry('y', 20)];
  var mapBaru = Map.fromEntries(entries);
  print(mapBaru); // {x: 10, y: 20}
}

Transformasi Collection

Inilah bagian paling powerful: metode functional untuk mentransformasi collection. Pola dasarnya selalu sama — collection punya metode yang menerima fungsi callback dan mengembalikan hasil transformasi.

map — Transformasi Setiap Elemen

void main() {
  var angka = [1, 2, 3, 4, 5];

  // Setiap elemen dikali 2
  var dikali2 = angka.map((e) => e * 2);
  print(dikali2); // (2, 4, 6, 8, 10) — Iterable, belum List!
  print(dikali2.toList()); // [2, 4, 6, 8, 10]

  // map dengan index (pakai asMap + map)
  var denganIndex = angka.asMap().entries.map((entry) {
    return 'Index ${entry.key} = ${entry.value}';
  }).toList();
  print(denganIndex);

  // Map<String, int> ke List<String>
  var nilai = {'Alice': 90, 'Bob': 85};
  var laporan = nilai.entries.map((e) => '${e.key}: ${e.value}').toList();
  print(laporan); // [Alice: 90, Bob: 85]
}

where — Filtering

void main() {
  var angka = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  // Filter bilangan genap
  var genap = angka.where((e) => e.isEven);
  print(genap); // (2, 4, 6, 8, 10)
  print(genap.toList()); // [2, 4, 6, 8, 10]

  // Filter dari Map
  var nilai = {'Alice': 90, 'Bob': 55, 'Charlie': 78, 'Diana': 45};
  var lulus = nilai.where((value) => value >= 75);
  print(lulus.toList()); // [90, 78] — hanya values
  // Kalau butuh key juga, filter entries dulu
  var siswaLulus = nilai.entries.where((e) => e.value >= 75).map((e) => e.key).toList();
  print(siswaLulus); // [Alice, Charlie]
}

reduce — Agregasi (Satu Tipe)

void main() {
  var angka = [1, 2, 3, 4, 5];

  // Jumlahkan semua elemen
  var total = angka.reduce((prev, curr) => prev + curr);
  print(total); // 15

  // Manual: 1 + 2 = 3, 3 + 3 = 6, 6 + 4 = 10, 10 + 5 = 15

  // Mencari nilai terbesar
  var terbesar = [3, 7, 2, 9, 5].reduce((a, b) => a > b ? a : b);
  print(terbesar); // 9

  // CATATAN: reduce ERROR kalau collection kosong!
  // var kosong = <int>[].reduce((a, b) => a + b); // Runtime error!
}

fold — Agregasi (Boleh Beda Tipe)

void main() {
  var angka = [1, 2, 3, 4, 5];

  // Jumlah — mirip reduce, tapi aman untuk empty list
  var total = angka.fold<int>(0, (prev, curr) => prev + curr);
  print(total); // 15

  // fold aman untuk empty list
  var kosong = <int>[].fold<int>(0, (prev, curr) => prev + curr);
  print(kosong); // 0 — tidak error

  // fold dengan tipe berbeda dari elemen
  var berdempet = angka.fold<String>('', (prev, curr) => '$prev, $curr');
  print(berdempet); // , 1, 2, 3, 4, 5 — agak jelek
  // Lebih rapi:
  var rapi = angka.fold<String>('', (prev, curr) => prev.isEmpty ? '$curr' : '$prev, $curr');
  print(rapi); // 1, 2, 3, 4, 5

  // fold untuk menggabungkan jadi Map
  var listNama = ['Alice', 'Bob', 'Charlie'];
  var mapNama = listNama.fold<Map<int, String>>({}, (map, nama) {
    map[map.length] = nama;
    return map;
  });
  print(mapNama); // {0: Alice, 1: Bob, 2: Charlie}
}
Fiturreducefold
Tipe returnSama dengan elemenBoleh berbeda
Empty collectionErrorReturn initial value
Parameter awalTidak adaAda initial value
Contohlist.reduce((a,b) => a+b)list.fold(0, (a,b) => a+b)

any, every, firstWhere

void main() {
  var angka = [1, 2, 3, 4, 5];
  var kosong = <int>[];

  // any — apakah ada yang memenuhi?
  print(angka.any((e) => e > 3)); // true
  print(angka.any((e) => e > 99)); // false
  print(kosong.any((e) => true)); // false — empty selalu false

  // every — apakah semua memenuhi?
  print(angka.every((e) => e > 0)); // true
  print(angka.every((e) => e.isEven)); // false — ada ganjil
  print(kosong.every((e) => false)); // true — empty selalu true!

  // firstWhere — cari elemen pertama yang match
  print(angka.firstWhere((e) => e > 3)); // 4

  // firstWhere with orElse — aman kalau tidak ketemu
  var hasil = angka.firstWhere(
    (e) => e > 99,
    orElse: () => -1,
  );
  print(hasil); // -1 — fallback

  // Tanpa orElse, firstWhere throw StateError kalau tidak ketemu!
  // var err = angka.firstWhere((e) => e > 99); // ERROR!
}

sort, take, skip

void main() {
  // --- sort ---
  var angka = [3, 1, 4, 1, 5, 9, 2];
  angka.sort();
  print(angka); // [1, 1, 2, 3, 4, 5, 9]

  // sort descending — pakai Comparator
  angka.sort((a, b) => b.compareTo(a));
  print(angka); // [9, 5, 4, 3, 2, 1, 1]

  // sort custom object
  var orang = [
    {'nama': 'Alice', 'umur': 30},
    {'nama': 'Bob', 'umur': 25},
    {'nama': 'Charlie', 'umur': 35},
  ];
  orang.sort((a, b) => a['umur']!.compareTo(b['umur']!));
  print(orang); // [Bob 25, Alice 30, Charlie 35]

  // --- take ---
  var list = [1, 2, 3, 4, 5, 6];
  print(list.take(3).toList()); // [1, 2, 3] — ambil N pertama
  print(list.take(100).toList()); // [1, 2, 3, 4, 5, 6] — kalau > length, aman

  // --- skip ---
  print(list.skip(3).toList()); // [4, 5, 6] — lewati N pertama
  print(list.skip(2).take(2).toList()); // [3, 4] — skip 2, ambil 2

  // --- whereType ---
  var campuran = [1, 'dua', 3, 'empat', 5];
  print(campuran.whereType<int>().toList()); // [1, 3, 5]
  print(campuran.whereType<String>().toList()); // [dua, empat]
}

Common Mistakes

1. map() Return Iterable, Bukan List

Ini salah satu mistake paling sering. .map() mengembalikan Iterable, bukan List.

void main() {
  var angka = [1, 2, 3];

  // ❌ Salah — hasilnya Iterable, mungkin tidak punya method List
  var hasilSalah = angka.map((e) => e * 2);
  // print(hasilSalah[0]); // ERROR! Iterable tidak punya []

  // ✅ Benar — konversi ke List dulu
  var hasilBenar = angka.map((e) => e * 2).toList();
  print(hasilBenar[0]); // 2
}

Iterable seperti map, where, take, skip bersifat lazy — mereka tidak memproses data sampai kamu melakukan iterasi. .toList() memaksa eksekusi.

2. Modifikasi Collection Saat Iterasi

void main() {
  var list = [1, 2, 3, 4, 5];

  // ❌ Salah — ConcurrentModificationError!
  // for (var e in list) {
  //   if (e.isEven) list.remove(e);
  // }

  // ✅ Benar — pakai removeWhere
  list.removeWhere((e) => e.isEven);
  print(list); // [1, 3, 5]

  // ✅ Atau filter dulu, baru assign
  var angka = [1, 2, 3, 4, 5];
  angka = angka.where((e) => e.isOdd).toList();
  print(angka); // [1, 3, 5]
}

3. fold dengan Tipe Berbeda — Inisialisasi Wajib

void main() {
  var angka = [1, 2, 3, 4, 5];

  // ❌ Ini tidak akan compile — reduce butuh tipe sama
  // var gabung = angka.reduce((a, b) => '$a, $b'); // ERROR: String vs int

  // ✅ fold bisa handle tipe berbeda
  var gabung = angka.fold<String>('', (prev, curr) => '$prev, $curr');
  print(gabung);
}

4. List.from vs Spread vs .toList()

void main() {
  var asli = [1, 2, 3];

  // Semua ini bikin salinan (copy), bukan referensi
  var salin1 = List.from(asli);
  var salin2 = [...asli];
  var salin3 = asli.toList();

  asli[0] = 99;
  print(asli); // [99, 2, 3] — asli berubah
  print(salin1); // [1, 2, 3] — salinan tidak terpengaruh

  // Tapi HATI-HATI: List.from itu shallow copy untuk nested list!
  var nested = [[1, 2], [3, 4]];
  var salinNested = List.from(nested);
  nested[0][0] = 99;
  print(salinNested); // [[99, 2], [3, 4]] — ikut berubah!
  // Kalau perlu deep copy, butuh manual atau package.
}

5. Set vs List — contain Performa

void main() {
  // Set.contains() JAUH lebih cepat dari List.contains()
  var listBesar = List.generate(10000, (i) => i);
  var setBesar = Set.from(listBesar);

  // List.contains — O(n) linear
  // Set.contains — O(1) konstan (hash-based)

  var start = DateTime.now();
  print(listBesar.contains(9999)); // true — lambat
  var listDurasi = DateTime.now().difference(start);

  start = DateTime.now();
  print(setBesar.contains(9999)); // true — cepat
  var setDurasi = DateTime.now().difference(start);

  print('List: ${listDurasi.inMicroseconds}us');
  print('Set: ${setDurasi.inMicroseconds}us');
  // Perbedaan dramatis untuk data besar!
}

6. Lupa bahwa List.add Return void

void main() {
  // ❌ Salah — add return void, bukan list
  // var hasil = [1, 2].add(3); // hasil = null

  // ✅ Benar
  var list = [1, 2];
  list.add(3);
  print(list); // [1, 2, 3]
}

Ringkasan Cepat

CollectionOrderDuplikatAkses
ListYaBolehIndex [i]
SetTidakTidak.contains() — O(1)
MapTidak (entries)Key: tidak, Value: bolehKey [k]
OperasiFungsiReturn
Transformmap()Iterable
Filterwhere()Iterable
Agregasireduce()Sama tipe
Agregasifold()Boleh beda tipe
Cekany(), every()bool
Ambiltake(), skip()Iterable

Apa Selanjutnya?

Kamu sudah menguasai collection Dart — bagaimana menyimpan, mengakses, dan mentransformasi data. Ini adalah fondasi yang sangat penting. Selanjutnya kamu akan belajar tentang Async & Await, karena di dunia nyata aplikasi hampir selalu butuh operasi asynchronous: mengambil data dari internet, membaca file, atau menunggu timer.

→ Lanjutkan ke Async & Await

On this page