animasicontrollertweenMahir5 mnt baca

AnimationController & Tween

Animasi eksplisit: controller, tween, dan repeat.

Kapan Kontrol Penuh Dibutuhkan

Animasi implisit cukup untuk efek sekali jalan yang dipicu setState, tapi ada kebutuhan yang tidak bisa ia jawab: animasi yang berputar terus tanpa henti (loading spinner), animasi yang maju-mundur mengikuti logika (progress yang bisa pause), rangkaian animasi bertahap di mana tiap elemen mulai pada waktu berbeda (staggered entrance), dan animasi yang mengikuti gerakan jari. Untuk semua ini kamu butuh AnimationController: objek yang memegang nilai 0.0 sampai 1.0 sepanjang duration, yang bisa di-forward, reverse, repeat, atau dihentikan kapan pun, dengan presisi per frame.

Pola Dasar: Controller + Transition

Struktur minimalnya selalu sama: State dengan SingleTickerProviderStateMixin (penyedia vsync), controller dibuat di initState, dipakai di build lewat widget transisi, dan di-dispose:

dart
class PutarPage extends StatefulWidget {
  const PutarPage({super.key});
  @override
  State<PutarPage> createState() => _PutarPageState();
}

class _PutarPageState extends State<PutarPage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RotationTransition(
          turns: _controller,
          child: const Icon(Icons.sync, size: 64),
        ),
      ),
    );
  }
}

vsync: this menyinkronkan animasi dengan refresh rate layar dan menghentikannya otomatis saat widget tidak terlihat (hemat baterai). Widget seperti RotationTransition atau FadeTransition menerima animation langsung, jadi tidak perlu setState manual per frame.

Kasus Nyata: Staggered Entrance

Satu controller bisa menggerakkan banyak animasi dengan timing berbeda memakai Interval. Contoh form login di mana tiap field fade + slide masuk bergantian:

dart
late final AnimationController _controller;
late final List<Animation<double>> _fadeAnims;
late final List<Animation<Offset>> _slideAnims;

@override
void initState() {
  super.initState();
  _controller = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 1200),
  );
  // 3 field, masing-masing mulai 0.2 detik setelah sebelumnya
  _fadeAnims = List.generate(3, (i) {
    final start = i * 0.2;
    return Tween<double>(begin: 0, end: 1).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(start, start + 0.4, curve: Curves.easeOut),
      ),
    );
  });
  _slideAnims = List.generate(3, (i) {
    final start = i * 0.2;
    return Tween<Offset>(
      begin: const Offset(0, 0.3),
      end: Offset.zero,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(start, start + 0.4, curve: Curves.easeOut),
      ),
    );
  });
  _controller.forward();
}

Di build, bungkus tiap field dengan FadeTransition + SlideTransition. Tween memetakan rentang 0.0-1.0 controller ke nilai yang kamu mau, dan Interval menentukan jendela waktu tiap animasi.

Edge Cases yang Wajib Diketahui

Pertama, lupa dispose adalah sumber memory leak dan error Ticker paling umum; controller yang tidak di-dispose tetap berdenyut di background. Kedua, butuh dua controller atau lebih? Ganti mixin ke TickerProviderStateMixin (tanpa "Single"). Ketiga, repeat() tanpa reverse: true melompat ke awal tiap putaran; untuk efek bolak-balik halus pakai repeat(reverse: true) atau addStatusListener dengan forward()/reverse() manual. Keempat, jangan membuat controller di dalam build: ia akan dibuat ulang tiap rebuild dan animasi restart terus. Kelima, animasi jalan tapi tak terlihat? Cek widget transisi benar memakai animation, dan pastikan tidak ada parent ber-opacity: 0 yang menyembunyikan.

Kapan TIDAK Pakai

Jangan bawa AnimationController untuk fade sederhana, expand kartu, atau perubahan warna saat tap: itu wilayah animasi implisit yang selesai dalam 5 baris. Controller baru layak saat kamu butuh loop, urutan bertahap, sinkronisasi dengan gesture, atau kontrol play/pause/reverse eksplisit. Aturan praktisnya: kalau animasimu bisa dideskripsikan sebagai "dari nilai A ke nilai B saat X terjadi", pakai implisit; kalau "animasi ini punya lifecycle sendiri", pakai controller.

Catatan teknis: Secara default AnimationController adalah Animation<double> 0.0-1.0, tapi lowerBound/upperBound bisa diubah. Karena CurvedAnimation mengasumsikan input 0.0-1.0, untuk kasus kustom lebih aman memetakan lewat Tween.

Tantangan

Ikon berputar

Buat halaman dengan ikon sync berputar terus memakai AnimationController + RotationTransition. Jangan lupa dispose.