Widget layout

Mengatur layout dengan Expanded, Flexible, Stack, dan Positioned.

Daftar isi

Row dan Column meletakkan anak-anaknya berdampingan, tetapi sebagian besar layar menuntut pembagian ruang yang lebih dinamis. Topik ini membahas Expanded dan Flexible untuk mendistribusikan ruang pada sumbu utama, mainAxisSize untuk mengatur ukuran baris atau kolom itu sendiri, serta Stack dan Positioned untuk menumpuk widget. Contoh yang digunakan bertema daftar tugas dan statistik nilai ujian.

Expanded: mengisi sisa ruang#

Expanded memaksa anaknya mengisi seluruh ruang kosong pada sumbu utama Row atau Column. Widget ini menyelesaikan masalah yang paling umum pada layout, yaitu anak yang tersangkut di ujung baris karena ruangnya tidak terpakai. Apabila terdapat beberapa Expanded, sisa ruang dibagi di antara seluruh Expanded tersebut sesuai nilai flex masing-masing, dengan nilai bawaan 1.

dart Contoh: diagram batang statistik dengan flex 2:1
// tidak-dijalankan
Row(
  children: [
    Expanded(
      flex: 2,
      child: Container(height: 24, color: Colors.indigo),
    ),
    Expanded(
      flex: 1,
      child: Container(height: 24, color: Colors.teal),
    ),
  ],
)

Nilai flex 2 dan 1 membuat batang pertama mendapat dua per tiga sisa ruang, sedangkan batang kedua mendapat satu per tiga. Pola ini cocok untuk diagram batang statistik sederhana. Penggunaan lain yang lazim adalah membungkus Text yang panjang dengan Expanded, sehingga teks memperoleh ruang sisa alih-alih meluber keluar baris. Semua contoh pada bagian ini menggunakan Row, sedangkan Expanded bekerja dengan cara yang sama pada Column dengan membagi sisa tinggi.

Flexible: batas yang longgar atau ketat#

Flexible menyerupai Expanded, tetapi menambahkan properti fit yang menentukan apakah anak wajib mengisi ruangnya atau boleh lebih kecil. Nilai FlexFit.tight mewajibkan anak mengisi seluruh ruang alokasinya, sedangkan FlexFit.loose membiarkan anak memilih ukuran yang lebih kecil sesuai kebutuhannya. Expanded sebenarnya adalah Flexible dengan fit yang sudah dikunci pada FlexFit.tight.

dart Contoh: membandingkan FlexFit.loose dan FlexFit.tight
// tidak-dijalankan
Row(
  children: [
    Flexible(
      fit: FlexFit.loose,
      child: Text('Teks pendek'),
    ),
    Flexible(
      fit: FlexFit.tight,
      child: Text('Teks kedua memenuhi sisa ruang baris'),
    ),
  ],
)

Pada contoh tersebut, anak pertama hanya selebar teksnya meskipun ruang alokasinya lebih besar, sedangkan anak kedua dipaksa mengisi penuh. Mode longgar cocok untuk teks yang ukuran alaminya ingin dipertahankan, sedangkan mode ketat cocok untuk elemen yang harus membentang hingga ujung baris.

mainAxisSize: ukuran baris dan kolom itu sendiri#

Row dan Column secara bawaan mengambil seluruh ruang pada sumbu utama yang diberikan induknya, dan perilaku ini diatur oleh properti mainAxisSize. Nilai MainAxisSize.max menjaga perilaku bawaan tersebut, sedangkan MainAxisSize.min menyusutkan baris atau kolom hingga cukup untuk anak-anaknya.

dart Contoh: menengahkan kolom dengan MainAxisSize.min
// tidak-dijalankan
Center(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    children: const [
      Text('Kartu Profil'),
      Text('Citra'),
    ],
  ),
)

Tanpa MainAxisSize.min, Column mengambil seluruh tinggi ruang yang tersedia, sehingga Center tidak lagi tampak menengahkan apa pun secara vertikal. Dengan nilai min, kolom menyusut mengikuti dua teksnya dan benar-benar berada di tengah. Pola Center yang membungkus Column dengan MainAxisSize.min adalah cara baku untuk menengahkan sekumpulan widget yang ukurannya mengikuti isi.

Stack dan Positioned: menumpuk widget#

Stack menumpuk anak-anaknya pada satu bidang sehingga widget dapat saling bertumpang tindih, misalnya teks keterangan di atas gambar atau lencana di pojok kartu. Anak yang ditulis lebih dulu berada di lapisan bawah, sedangkan anak terakhir berada paling atas. Secara bawaan, ukuran Stack mengikuti ukuran terbesar di antara anak-anak yang tidak dibungkus Positioned. Anak-anak yang tidak dibungkus Positioned dapat diratakan secara bersama melalui properti alignment pada Stack, misalnya Alignment.center.

dart Contoh: lencana tugas di pojok kartu
// tidak-dijalankan
Stack(
  children: [
    Container(width: 160, height: 80, color: Colors.blueGrey),
    const Positioned(
      top: 8,
      right: 8,
      child: Text('3 tugas baru'),
    ),
  ],
)

Positioned hanya berlaku sebagai anak langsung Stack, dan posisinya dinyatakan dengan properti top, right, bottom, dan left. Anak yang dibungkus Positioned ditempatkan menurut jarak dari tepi Stack, terlepas dari penataan baris maupun kolom. Pada contoh, teks lencana berjarak delapan piksel logis dari tepi atas dan tepi kanan kotak.

Demo: membandingkan distribusi ruang#

Perbedaan mode distribusi ruang paling mudah ditangkap lewat percobaan langsung. Jalankan demo berikut, klik tombol Expanded, kemudian klik tombol Flexible dan bandingkan lebar ketiga kotaknya pada kedua mode. Pada mode Tetap, coba pula tombol mainAxisSize dan amati panel Row yang menyusut mengikuti isinya.

Demo tersebut ditulis dengan CSS, tetapi setiap modenya memiliki padanan perilaku pada Flutter. Mode Expanded memberi setiap anak pembagian yang sama rata tanpa memedulikan lebar isi, mode Flexible longgar membiarkan lebar isi ikut menentukan, sedangkan mode Tetap membiarkan setiap kotak selebar ukuran tetapnya. Tombol mainAxisSize hanya diaktifkan pada mode Tetap, karena sisa ruang yang dapat dipangkas memang baru ada pada mode itu. Pembagian sama rata pada demo menggambarkan nilai flex bawaan, dan nilai tersebut dapat diubah untuk memperoleh proporsi seperti contoh diagram batang.

Kesalahan umum#

  1. Meletakkan Expanded di luar Row, Column, atau Flex. Widget ini bekerja dengan membagi ruang milik induknya, sehingga penempatannya di tempat lain memicu pesan error. Perbaikannya, pastikan Expanded selalu menjadi anak langsung dari widget bertipe Flex.
  2. Meletakkan Positioned di luar Stack. Positioned melampirkan data posisi yang hanya dipahami oleh Stack sebagai induknya. Apabila diletakkan di tempat lain, aplikasi menampilkan error saat berjalan. Perbaikannya, pastikan Positioned menjadi anak langsung Stack.

Ringkasan#

  • Expanded mengisi seluruh sisa ruang sumbu utama, dan beberapa Expanded membaginya sesuai nilai flex.
  • Flexible mengatur apakah alokasi ruang wajib diisi penuh atau boleh lebih kecil melalui fit, dengan Expanded sebagai bentuk singkat FlexFit.tight.
  • mainAxisSize menentukan apakah Row atau Column memenuhi ruang yang tersedia atau menyusut mengikuti isi.
  • Stack menumpuk widget, sedangkan Positioned menempatkan anak pada jarak tertentu dari tepi Stack.

Referensi#