Animation

การสร้างความเคลื่อนไหวของ UI ใน Flutter — มี 2 แบบหลัก: Implicit (ง่าย) และ Explicit (ควบคุมเอง)

Implicit Animation — ใช้ widget สำเร็จรูป (ง่าย)

Flutter จัดการ animation ให้ — แค่เปลี่ยนค่า widget จะ animate ให้อัตโนมัติ:

// AnimatedSwitcher — animate เมื่อเปลี่ยน child
AnimatedSwitcher(
  duration: const Duration(milliseconds: 300),
  child: content,  // เปลี่ยน child → animate อัตโนมัติ
  // สำคัญ: child ต้องมี key ต่างกัน ถึงจะ animate
);
 
// AnimatedContainer — animate เมื่อ properties เปลี่ยน
AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  width: isExpanded ? 200 : 100,   // เปลี่ยนค่า → animate
  height: isExpanded ? 200 : 100,
  color: isExpanded ? Colors.blue : Colors.red,
);
 
// AnimatedOpacity — animate ความโปร่งใส
AnimatedOpacity(
  duration: const Duration(milliseconds: 500),
  opacity: isVisible ? 1.0 : 0.0,
  child: Text('Hello'),
);

Implicit Animation Widgets อื่นๆ: AnimatedPadding, AnimatedPositioned, AnimatedDefaultTextStyle

Explicit Animation — สร้าง animation เอง (ควบคุมละเอียด)

ต้องสร้าง AnimationController เอง — ควบคุม timing, direction, repeat ได้:

class _MyWidgetState extends State<MyWidget>
    with SingleTickerProviderStateMixin {  // ← จำเป็นสำหรับ vsync
  late AnimationController _controller;
 
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,                              // sync กับ frame rate
      duration: const Duration(milliseconds: 300),
      lowerBound: 0,
      upperBound: 1,
    );
    _controller.forward();  // เริ่ม animation
  }
 
  @override
  void dispose() {
    _controller.dispose();  // ← ต้อง dispose!
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      child: const Icon(Icons.star),  // child ไม่ rebuild
      builder: (context, child) => SlideTransition(
        position: Tween(
          begin: const Offset(0, 0.3),
          end: const Offset(0, 0),
        ).animate(CurvedAnimation(
          parent: _controller,
          curve: Curves.easeInOut,
        )),
        child: child,
      ),
    );
  }
}

เมื่อไหร่ใช้ Explicit:

  • ต้องการ repeat/reverse animation
  • animation ซับซ้อน (ต่อกันหลายจังหวะ)
  • ต้องควบคุม timing ละเอียด

Hero Animation — animate ระหว่าง 2 หน้าจอ

widget “บิน” จากหน้าหนึ่งไปอีกหน้า — ใช้ tag เดียวกัน:

// หน้าที่ 1 (list)
Hero(
  tag: meal.id,  // ← tag ต้องตรงกัน
  child: Image.network(meal.imageUrl),
);
 
// หน้าที่ 2 (detail) — ใช้ tag เดียวกัน
Hero(
  tag: meal.id,  // ← tag เดียวกัน → Flutter animate ให้
  child: Image.network(meal.imageUrl),
);

เมื่อ navigate จากหน้า 1 ไปหน้า 2 → Image จะ animate “บิน” จากตำแหน่งเดิมไปตำแหน่งใหม่

เลือกแบบไหนดี

ImplicitExplicitHero
ความยากง่ายซับซ้อนง่าย
ควบคุมจำกัดเต็มที่อัตโนมัติ
ใช้เมื่อเปลี่ยนค่า propertyต้องการ repeat/ควบคุม timingtransition ระหว่างหน้า
ตัวอย่างปุ่มเปลี่ยนสี, fade in/outloading spinner, splashรูปบินจาก list ไป detail

Key Points

  • เริ่มจาก Implicit ก่อน — ง่ายและเพียงพอสำหรับงานส่วนใหญ่
  • Explicit ใช้ AnimationController + SingleTickerProviderStateMixin + ต้อง dispose()
  • Hero ใช้ tag เดียวกันใน 2 หน้า → Flutter animate transition ให้อัตโนมัติ
  • AnimatedSwitcher ต้องมี key ต่างกันบน child ถึงจะ animate
  • Flutter — framework ที่รองรับ animation
  • Widgets — AnimatedSwitcher, Hero, AnimatedContainer เป็น widgets
  • Navigation — Hero animation ทำงานร่วมกับ Navigator