Hal Kecil tapi Penting Tentang Hasil Dart Format di Flutter
8 min read

Hal Kecil tapi Penting Tentang Hasil Dart Format di Flutter

Dart menyediakan code formatter bawaan, dart format, untuk merapikan kode secara otomatis — sangat membantu terutama karena kode Flutter cenderung punya struktur UI yang dalam dan bertingkat (nested widgets). Tapi banyak developer pernah mengalami momen frustrasi: hasil dart format terlihat “aneh” atau terlalu memadatkan kode jadi satu baris panjang yang sulit dibaca, padahal sudah memakai formatter resmi. Masalahnya hampir selalu bukan pada tool-nya, melainkan satu karakter kecil yang sering luput — trailing comma di akhir parameter. Artikel ini membedah mekanisme di balik keputusan formatter, kenapa satu koma bisa mengubah seluruh struktur output, dan di konteks mana saja kebiasaan ini benar-benar berpengaruh.

Bagaimana Dart Formatter Mengambil Keputusan

Sebelum membahas trailing comma secara spesifik, penting memahami prinsip dasar yang dipakai dart format untuk memutuskan apakah sebuah ekspresi ditulis dalam satu baris atau dipecah ke banyak baris. Formatter Dart pada dasarnya selalu mencoba memuat sebanyak mungkin kode ke dalam satu baris, selama masih berada dalam batas panjang baris yang diizinkan (default 80 karakter).

flowchart TD
    A[Formatter membaca satu ekspresi] --> B{Muat dalam satu baris<br/>dalam batas line-length?}
    B -- Ya --> C{Ada trailing comma<br/>di akhir argumen?}
    C -- Tidak ada --> D[Tulis dalam satu baris]
    C -- Ada --> E[Paksa pecah ke multi-baris]
    B -- Tidak --> E

Inilah inti masalahnya: formatter tidak tahu niat developer hanya dari struktur kode semata. Kalau kode kebetulan masih muat dalam 80 karakter, formatter akan memadatkannya jadi satu baris — meskipun secara konseptual struktur itu adalah widget tree yang dalam dan idealnya tetap terlihat berlapis. Trailing comma adalah satu-satunya sinyal eksplisit yang bisa kamu berikan untuk memberi tahu formatter: “bagian ini harus tetap multi-baris, terlepas dari apakah ia muat dalam satu baris atau tidak.”


Trailing Comma sebagai Sinyal Eksplisit

Trailing comma — koma tambahan setelah argumen atau elemen terakhir — bukan sekadar gaya penulisan kosmetik. Bagi parser Dart, koma ini secara sintaksis valid dan tidak mengubah makna kode sama sekali. Tapi bagi dart format, keberadaannya adalah instruksi eksplisit yang mengubah keputusan formatting sepenuhnya.

// Tanpa trailing comma -- formatter bebas memadatkan jika muat
Text("Hello")

// Dengan trailing comma -- formatter WAJIB memecah ke multi-baris
Text(
  "Hello",
)

Perbedaan ini terlihat sepele untuk satu widget pendek seperti contoh di atas, tapi efeknya membesar drastis begitu struktur kode jadi lebih kompleks — yang justru adalah kondisi normal di kode Flutter sehari-hari.


Tanpa Trailing Comma vs Dengan Trailing Comma

Perhatikan kode berikut, ditulis tanpa trailing comma:

// ANTI-PATTERN: tanpa trailing comma, formatter bebas memadatkan
Column(
  children: [
    Text("Hello"),
    Icon(Icons.star)
  ]
)

Begitu dart format dijalankan, karena seluruh ekspresi ini masih muat dalam batas line-length, hasilnya dipadatkan jadi satu baris:

Column(children: [Text("Hello"), Icon(Icons.star)])

Secara teknis ini valid dan benar — tapi struktur hierarki widget jadi hilang dari kode. Sulit melihat sekilas bahwa ini adalah Column yang berisi dua children, apalagi kalau nanti kamu menambahkan widget ketiga atau keempat ke dalamnya.

Sekarang bandingkan dengan versi yang memakai trailing comma secara konsisten:

// BENAR: trailing comma di setiap level memaksa struktur tetap multi-baris
Column(
  children: [
    Text("Hello"),
    Icon(Icons.star),
  ],
);

Hasil dart format-nya:

Column(
  children: [
    Text(
      "Hello",
    ),
    Icon(
      Icons.star,
    ),
  ],
);

Struktur hierarki widget langsung terlihat jelas dari indentasi — Column membungkus children, yang berisi Text dan Icon, masing-masing dengan parameternya sendiri. Inilah perbedaan yang dimaksud “hal kecil tapi berdampak besar”: satu koma di tempat yang tepat mengubah keterbacaan seluruh blok kode.


Studi Kasus: Widget Tree yang Dalam

Efek trailing comma makin terasa signifikan begitu widget tree bertambah dalam — situasi yang sangat umum di layout Flutter nyata. Bandingkan dua versi berikut untuk struktur yang sama: Card yang membungkus Padding, yang membungkus Row, yang berisi Icon dan Column bertingkat.

// Tanpa trailing comma di level dalam (hanya level luar)
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Row(children: [Icon(Icons.info), Column(children: [Text("Judul"), Text("Subjudul")])])
  ),
);

Begitu salah satu level lupa diberi trailing comma, formatter memadatkan bagian itu sebisa mungkin — menghasilkan baris yang sangat panjang dan sulit dibaca persis seperti contoh di atas, bahkan berpotensi melebihi batas line-length sehingga dipecah dengan cara yang tidak mengikuti hierarki widget secara intuitif.

// BENAR: trailing comma konsisten di SETIAP level nested
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Row(
      children: [
        Icon(Icons.info),
        Column(
          children: [
            Text("Judul"),
            Text("Subjudul"),
          ],
        ),
      ],
    ),
  ),
);

Dengan trailing comma di setiap level — bukan hanya di level terluar — dart format mempertahankan struktur berlapis ini sepenuhnya, dan setiap level nesting langsung terlihat dari indentasinya. Inilah kenapa kebiasaan menambahkan trailing comma harus konsisten di semua level, bukan hanya di tempat yang “kelihatan perlu” saat menulis kode pertama kali.

Trailing comma yang hanya ditambahkan di level terluar tidak cukup. Formatter mengevaluasi setiap level ekspresi secara independen — level dalam yang tidak punya trailing comma tetap akan dipadatkan meski level luarnya sudah dipecah multi-baris.

Trailing Comma di Konteks Lain (Bukan Cuma Widget)

Meski paling sering dibahas dalam konteks widget Flutter, trailing comma bukan fitur khusus widget — ia berlaku di seluruh sintaks Dart yang melibatkan daftar dipisah koma: pemanggilan fungsi, definisi fungsi, collection literal, dan named parameter.

// Function call dengan banyak argumen
hitungTotal(
  harga: 50000,
  diskon: 5000,
  pajak: 1100,
);

// Definisi fungsi dengan banyak parameter
void updateProfil({
  required String nama,
  required String email,
  String? nomorTelepon,
}) {
  // ...
}

// Collection literal -- List, Set, Map
final daftarWarna = [
  Colors.red,
  Colors.green,
  Colors.blue,
];

final konfigurasi = {
  'host': 'localhost',
  'port': 8080,
  'timeout': 30,
};

Di semua konteks ini, prinsipnya sama persis dengan widget: trailing comma adalah sinyal eksplisit ke formatter bahwa daftar ini sebaiknya tetap dalam bentuk multi-baris, terlepas dari apakah ia muat dalam satu baris atau tidak. Kebiasaan ini terutama berguna untuk daftar konfigurasi atau parameter yang kemungkinan akan bertambah seiring waktu — entry baru di masa depan tidak akan memicu perubahan formatting pada baris-baris lain di sekitarnya, yang membuat diff di code review lebih bersih.

Dampaknya terhadap Diff di Code Review

Salah satu manfaat trailing comma yang jarang disorot adalah efeknya pada git diff. Tanpa trailing comma, menambahkan satu elemen baru ke akhir list yang sebelumnya dipadatkan satu baris memaksa seluruh baris itu berubah:

// Sebelum: dipadatkan satu baris tanpa trailing comma
final daftarWarna = [Colors.red, Colors.green];

// Setelah menambah satu warna -- baris ini berubah TOTAL
final daftarWarna = [Colors.red, Colors.green, Colors.blue];

Reviewer melihat satu baris berubah penuh, padahal secara logis hanya ada satu elemen baru. Bandingkan dengan versi yang sudah dipecah multi-baris dengan trailing comma sejak awal:

final daftarWarna = [
  Colors.red,
  Colors.green,
  Colors.blue, // <- hanya baris ini yang baru ditambahkan
];

Diff yang dihasilkan git hanya menunjukkan satu baris tambahan, bukan satu baris yang berubah total. Untuk file konfigurasi atau daftar konstanta yang sering bertambah, efek kumulatifnya cukup besar terhadap kebersihan history commit dan kemudahan review.


Kapan Trailing Comma Tidak Mengubah Apapun

Penting dipahami bahwa trailing comma bukan jaminan kode selalu jadi multi-baris dalam segala kondisi — ada satu pengecualian yang sering bikin bingung. Untuk pemanggilan dengan satu argumen pendek saja, formatter Dart cenderung tetap memadatkannya jadi satu baris meski ada trailing comma, selama tetap muat dalam batas line-length.

// Trailing comma di sini TIDAK memaksa multi-baris,
// karena hanya ada satu argumen pendek
Text("Hello",);
// Hasil format tetap: Text("Hello");

Ini berbeda dari kasus widget dengan named parameter atau collection literal seperti children: [...], di mana trailing comma konsisten memaksa pemecahan baris. Pengecualian single-argument ini kadang membuat developer mengira trailing comma “tidak bekerja”, padahal sebenarnya formatter memang sengaja mempertahankan kepadatan untuk kasus paling sederhana ini.

Pengecualian single-argument ini berlaku konsisten di seluruh versi Dart formatter modern. Kalau kamu menambahkan trailing comma pada satu argumen pendek dan formatter tetap memadatkannya jadi satu baris, itu bukan bug — itu memang perilaku yang disengaja.

Mengotomatiskan dengan Lint Rule

Mengandalkan disiplin manual untuk selalu menambahkan trailing comma di setiap level nesting rawan terlewat, terutama saat refactoring cepat atau menulis kode di bawah tekanan deadline. Dart menyediakan lint rule require_trailing_commas yang bisa diaktifkan di analysis_options.yaml agar IDE menandai (dan dart fix bisa otomatis memperbaiki) parameter yang seharusnya punya trailing comma tapi belum ada.

# analysis_options.yaml
include: package:flutter_lints/flutter.yaml

linter:
  rules:
    - require_trailing_commas

Dengan lint rule ini aktif, editor akan menampilkan peringatan di kode yang seharusnya memakai trailing comma — dan kamu bisa menjalankan perbaikan otomatis lewat:

dart fix --apply
Aktifkan require_trailing_commas sejak awal project, bukan setelah kode sudah membesar. Menerapkannya di project yang sudah berjalan lama tetap bisa dilakukan lewat dart fix --apply, tapi akan menghasilkan diff besar sekaligus di banyak file — pertimbangkan menjalankannya sebagai commit tersendiri yang terpisah dari perubahan logic.

Tabel Ringkasan Perilaku

SituasiTrailing CommaHasil Format
Widget tunggal dengan satu argumen pendekAdaTetap satu baris (pengecualian)
Widget dengan named parameter (children:, dsb)Tidak adaDipadatkan jika muat dalam line-length
Widget dengan named parameter (children:, dsb)AdaSelalu multi-baris
Nested widget, trailing comma hanya di level luarSebagianLevel dalam tetap dipadatkan
Nested widget, trailing comma di semua levelKonsistenStruktur hierarki penuh terlihat
Function call / collection literalAdaSelalu multi-baris, sama seperti widget
Kode melebihi batas line-lengthTidak relevanSelalu dipecah multi-baris meski tanpa trailing comma

Ringkasan

  • dart format selalu mencoba memadatkan kode ke satu baris selama masih muat dalam batas line-length default 80 karakter.
  • Trailing comma adalah sinyal eksplisit ke formatter bahwa sebuah daftar argumen harus tetap multi-baris, terlepas dari apakah ia sebenarnya muat dalam satu baris.
  • Trailing comma harus ditambahkan di setiap level nesting, bukan hanya level terluar — level dalam yang tidak punya trailing comma tetap akan dipadatkan.
  • Efek trailing comma paling terasa di widget tree Flutter yang dalam, di mana struktur hierarki widget jadi jauh lebih mudah dibaca dengan format multi-baris yang konsisten.
  • Prinsip yang sama berlaku di luar widget — function call, definisi fungsi, dan collection literal (List, Set, Map) semuanya mengikuti aturan formatter yang identik.
  • Untuk pemanggilan dengan satu argumen pendek saja, trailing comma tidak memaksa multi-baris — ini pengecualian yang disengaja, bukan bug.
  • Lint rule require_trailing_commas di analysis_options.yaml mengotomatiskan kebiasaan ini lewat IDE warning dan dart fix --apply, mengurangi ketergantungan pada disiplin manual.

Portofolio