Flutter: Contoh Interaktif Visual – Beberapa Widget yang Rebuild Berbeda-beda Saat State Berubah

Flutter memang menyenangkan, tapi kadang membingungkan terutama saat kita berbicara tentang rebuild widget. Semua widget di Flutter bisa dipanggil ulang melalui metode build(), tapi apakah semua widget selalu rebuild bersamaan? Jawabannya: tidak selalu. Dengan memahami ini, kamu bisa membuat aplikasi yang lebih efisien, cepat, dan bersih.

Artikel ini akan membahas versi lanjutan Flutter: contoh interaktif visual di mana beberapa widget rebuild berbeda-beda saat state berubah. Kita akan pakai contoh nyata dan penjelasan santai supaya konsep ini gampang dipahami, bahkan untuk pemula yang sudah pernah bermain dengan build().

1. Kenapa Widget Bisa Rebuild Berbeda-Beda?

Di Flutter, widget adalah immutable. Artinya, begitu widget dibuat, isinya tidak bisa diubah. Kalau kita ingin merubah UI, Flutter akan membangun ulang widget itu lewat build().

Namun, hanya widget yang dipengaruhi oleh state yang berubah yang akan rebuild. Ini penting karena:

  1. Meningkatkan performa → tidak semua widget harus di-render ulang setiap kali state berubah.

  2. Memudahkan modularisasi → kamu bisa memisahkan UI menjadi widget kecil yang independen.

  3. Kontrol lebih baik atas UI → tahu persis bagian mana yang akan update saat interaksi terjadi.

2. Membuat Contoh Interaktif Sederhana

Kita akan buat contoh dengan 3 widget yang berbeda:

  1. Counter A → hanya rebuild saat tombol A ditekan.

  2. Counter B → hanya rebuild saat tombol B ditekan.

  3. Text statis → tidak pernah rebuild.

Berikut kodenya:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Interactive Rebuild Demo',
      home: Scaffold(
        appBar: AppBar(title: const Text('Widget Rebuild Demo')),
        body: const RebuildDemo(),
      ),
    );
  }
}

class RebuildDemo extends StatefulWidget {
  const RebuildDemo({super.key});

  @override
  State<RebuildDemo> createState() => _RebuildDemoState();
}

class _RebuildDemoState extends State<RebuildDemo> {
  int counterA = 0;
  int counterB = 0;

  @override
  Widget build(BuildContext context) {
    print('RebuildDemo build called'); // Untuk melihat kapan rebuild terjadi
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        CounterWidget(
          label: 'Counter A',
          count: counterA,
          onPressed: () {
            setState(() {
              counterA++;
            });
          },
        ),
        CounterWidget(
          label: 'Counter B',
          count: counterB,
          onPressed: () {
            setState(() {
              counterB++;
            });
          },
        ),
        const StaticText(),
      ],
    );
  }
}

class CounterWidget extends StatelessWidget {
  final String label;
  final int count;
  final VoidCallback onPressed;

  const CounterWidget({
    required this.label,
    required this.count,
    required this.onPressed,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    print('$label build called'); // Lihat kapan widget ini rebuild
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('$label: $count'),
          const SizedBox(width: 20),
          ElevatedButton(
            onPressed: onPressed,
            child: const Text('Increment'),
          ),
        ],
      ),
    );
  }
}

class StaticText extends StatelessWidget {
  const StaticText({super.key});

  @override
  Widget build(BuildContext context) {
    print('StaticText build called'); // Tidak berubah
    return const Padding(
      padding: EdgeInsets.all(8.0),
      child: Text(
        'This text never rebuilds!',
        style: TextStyle(fontSize: 18, color: Colors.blue),
      ),
    );
  }
}

3. Analisis Contoh

  1. RebuildDemo
    Ini adalah StatefulWidget utama. setState() di dalamnya akan memicu rebuild seluruh _RebuildDemoState.

  2. CounterWidget
    Widget ini Stateless, tapi menerima count dan onPressed dari parent.

    • Ketika counterA berubah → Counter A rebuild, Counter B juga ikut rebuild karena parent memanggil setState().

    • Jika ingin Counter B tidak ikut rebuild, kita bisa pisahkan menjadi widget stateful independen.

  3. StaticText
    Karena tidak tergantung state, isinya tetap sama. Jika kita buat const, Flutter tidak akan membangunnya ulang.

4. Optimasi: Menghindari Rebuild yang Tidak Perlu

Jika kita ingin hanya Counter A yang rebuild tanpa mempengaruhi Counter B, kita bisa pisahkan state di masing-masing widget:

class CounterWidgetStateful extends StatefulWidget {
  final String label;
  const CounterWidgetStateful({required this.label, super.key});

  @override
  State<CounterWidgetStateful> createState() => _CounterWidgetStatefulState();
}

class _CounterWidgetStatefulState extends State<CounterWidgetStateful> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    print('${widget.label} build called');
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('${widget.label}: $count'),
        const SizedBox(width: 20),
        ElevatedButton(
          onPressed: () {
            setState(() {
              count++;
            });
          },
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

Dengan cara ini:

  • Counter A dan B menjadi independen → increment satu counter tidak mempengaruhi counter lain.

  • UI lebih efisien dan performa lebih bagus untuk aplikasi besar.

5. Visualisasi Interaktif

Tips visualisasi untuk memahami rebuild:

  • Tambahkan print() di setiap build() → lihat di console kapan widget rebuild.

  • Gunakan warna berbeda di setiap widget → lebih jelas bagian mana yang berubah.

Container(
  color: count % 2 == 0 ? Colors.green : Colors.red,
  child: Text('$label: $count'),
)

Dengan warna berubah setiap kali setState() dipanggil, kamu bisa langsung melihat visual rebuild.

6. Kapan Harus Menggunakan Strategi Ini?

  1. Aplikasi dengan banyak widget → membatasi rebuild akan sangat menghemat memori dan CPU.

  2. Widget kompleks dengan tree yang dalam → misalnya dashboard dengan chart, list panjang, atau animasi.

  3. Interaksi yang sering terjadi → seperti tombol increment, switch, slider, agar UI tetap smooth.

Dengan contoh interaktif ini, kita belajar beberapa hal penting:

  • Tidak semua widget harus rebuild setiap kali setState() dipanggil.

  • Memisahkan state di widget kecil membuat aplikasi lebih efisien.

  • Widget const dan Stateless yang tidak tergantung state tidak akan rebuild, jadi gunakan untuk elemen statis.

  • Debug dengan print() atau perubahan warna membantu memahami kapan rebuild terjadi.

Flutter memang powerful, tapi menguasai rebuild widget secara efisien adalah kunci untuk membuat aplikasi yang responsif, cepat, dan profesional.

Tips :

  • Gunakan const sebanyak mungkin untuk widget statis.

  • Pertimbangkan Provider, Riverpod, atau Bloc jika state kompleks.

  • Selalu pisahkan widget menjadi bagian kecil supaya lebih modular dan mudah dirawat.

Dengan contoh ini, kamu sudah punya dasar untuk membuat versi lanjutan Flutter dengan interaksi visual dan kontrol rebuild widget. Kamu bisa eksplor lebih jauh dengan list panjang, chart animasi, atau UI yang interaktif sambil memanfaatkan prinsip ini.


0 Comments:

Post a Comment