Responsive Layout
Satu kode untuk HP, tablet, dan desktop: LayoutBuilder & MediaQuery.
Kenapa Responsive Layout Ada: Masalah yang Diselesaikan
Flutter menjanjikan satu codebase untuk banyak platform: HP Android, iPhone, tablet, web, sampai desktop. Masalahnya, layar 360px dan layar 1440px punya kebutuhan tata letak yang sangat berbeda. Kalau kamu hardcode satu layout untuk HP, aplikasimu akan terlihat kosong dan canggung di tablet atau desktop.
Solusinya bukan membuat tiga aplikasi terpisah, melainkan satu layout yang beradaptasi: membaca ruang yang tersedia, lalu memilih susunan widget yang pas. Dua alat utamanya adalah LayoutBuilder (membaca constraint dari parent) dan MediaQuery (membaca info layar).
Contoh Dasar: LayoutBuilder
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
// tablet/desktop: 2 kolom berdampingan
return const Row(
children: [
Expanded(child: MenuPanel()),
Expanded(child: DetailPanel()),
],
);
}
// HP: 1 kolom
return const MenuPanel();
},
)LayoutBuilder bereaksi terhadap ruang yang benar-benar tersedia untuk widget itu, bukan ukuran layar penuh. Ini penting untuk panel di dalam Row atau dialog yang tidak selebar layar.
Contoh Nyata: Adaptive Scaffold Lengkap
Untuk aplikasi produksi, rapikan breakpoint dalam satu tempat supaya konsisten di semua layar:
enum UkuranLayar { hp, tablet, desktop }
UkuranLayar ukuranLayar(double lebar) {
if (lebar < 600) return UkuranLayar.hp;
if (lebar < 1200) return UkuranLayar.tablet;
return UkuranLayar.desktop;
}
class AdaptiveScaffold extends StatelessWidget {
final Widget body;
const AdaptiveScaffold({super.key, required this.body});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
switch (ukuranLayar(constraints.maxWidth)) {
case UkuranLayar.hp:
return Scaffold(
body: body,
bottomNavigationBar: const MenuBawah(),
);
case UkuranLayar.tablet:
return Scaffold(
body: Row(
children: [
const NavigationRailToko(),
Expanded(child: body),
],
),
);
case UkuranLayar.desktop:
return Scaffold(
body: Row(
children: [
const DrawerPermanen(),
Expanded(child: body),
],
),
);
}
},
);
}
}Satu widget ini mengatur navigasi yang tepat untuk tiap ukuran: bottom nav di HP, navigation rail di tablet, drawer permanen di desktop.
Edge Case yang Sering Terlewat
- Resize di desktop/web:
MediaQuerydanLayoutBuilderotomatis rebuild saat jendela di-resize, jadi pastikan tidak ada state yang hilang saat layout berganti (misalnya tab yang sedang terbuka). - Split-screen di Android: lebar layar bisa berubah drastis saat user masuk mode split-screen. Jangan asumsikan lebar selalu penuh.
textScaleFactor: pengguna bisa memperbesar font di pengaturan aksesibilitas. Layout yang pas di skala 1.0 bisa overflow di skala 1.3. Uji dengan nilaiMediaQuery.textScaleFactorOfyang besar.- Keyboard: saat keyboard muncul,
MediaQuery.of(context).viewInsets.bottomberubah. Form di HP kecil bisa tertutup keyboard kalau tidak memakaiSingleChildScrollView. - Orientasi:
OrientationBuilderlebih ringan daripadaMediaQuerykalau kamu hanya peduli portrait vs landscape, karena rebuild-nya lebih terbatas.
Kapan JANGAN Berlebihan
- Jangan bikin 10 breakpoint. Tiga (HP, tablet, desktop) cukup untuk hampir semua aplikasi. Terlalu banyak breakpoint membuat kode sulit diuji.
- Untuk penyesuaian kecil,
FractionallySizedBox,Wrap,FittedBox, atauExpandedsering cukup tanpa logika breakpoint sama sekali. - Jangan hardcode ukuran piksel (misalnya
Container(width: 350)) untuk elemen utama. Pakai proporsi dari constraint yang tersedia. - Hindari
MediaQuery.of(context).sizedi dalam widget yang berada di dalam dialog atau panel samping: ia mengembalikan ukuran layar penuh, bukan ruang widget tersebut. Di situlahLayoutBuildermenang.
Catatan teknis:
LayoutBuildertidak bisa dipakai untuk memutuskan ukuran dirinya sendiri (itu circular). Ia hanya membaca constraint yang diberikan parent, lalu memilih child. Kalau butuh ukuran setelah layout selesai, pakai callback post-frame atauGlobalKeydenganRenderBox, tapi itu jarang diperlukan.
Tantangan
Layout adaptif
Buat widget yang menampilkan GridView 2 kolom jika lebar > 600, ListView 1 kolom jika <= 600, memakai LayoutBuilder.
Kuis Bab
Uji pemahamanmu: UI Polish: Tema & Animasi
Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".
1.Apa keuntungan mendefinisikan warna lewat ThemeData dibanding hardcode?
2.Bagaimana cara membuat palet harmonis otomatis di Material 3?
3.Kapan cukup memakai animasi implisit (AnimatedContainer)?
4.Apa syarat Hero animation bekerja?
5.LayoutBuilder vs MediaQuery: kapan LayoutBuilder lebih tepat?