responsivelayoutadaptiveMahir4 mnt baca

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

dart
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:

dart
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: MediaQuery dan LayoutBuilder otomatis 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 nilai MediaQuery.textScaleFactorOf yang besar.
  • Keyboard: saat keyboard muncul, MediaQuery.of(context).viewInsets.bottom berubah. Form di HP kecil bisa tertutup keyboard kalau tidak memakai SingleChildScrollView.
  • Orientasi: OrientationBuilder lebih ringan daripada MediaQuery kalau 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, atau Expanded sering 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).size di dalam widget yang berada di dalam dialog atau panel samping: ia mengembalikan ukuran layar penuh, bukan ruang widget tersebut. Di situlah LayoutBuilder menang.

Catatan teknis: LayoutBuilder tidak 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 atau GlobalKey dengan RenderBox, 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?