ข้ามไปยังเนื้อหา

Animations

animation ใน Flutter แยกเป็นสองตระกูล และการเลือกให้ถูกคือทักษะเกือบทั้งหมด:

  • Implicit — คุณเปลี่ยนค่า Flutter animate ไปหา ค่าใหม่ให้อัตโนมัติ ไม่มี controller ไม่มี lifecycle ให้จัดการ
  • Explicit — คุณขับ animation เองด้วย AnimationController ควบคุมได้เต็มที่ (repeat, reverse, sync หลายค่า, ตอบสนอง gesture) แลกกับ ticker และการ dispose

เริ่มที่ implicit แล้วค่อยไป explicit เมื่อ implicit ทำสิ่งที่ต้องการไม่ได้

widget ตระกูล AnimatedFoo (AnimatedContainer, AnimatedOpacity, AnimatedPadding, …) รับ duration และ animate ทุกครั้งที่ property เป้าหมายเปลี่ยน

AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100, // เปลี่ยนค่านี้ (เช่นใน setState)...
height: 100,
color: Colors.blue,
// ...แล้ว Flutter จะ animate จาก width เดิมไป width ใหม่อย่างลื่น
)

TweenAnimationBuilder generalize สิ่งนี้: ให้ Tween กับค่าเป้าหมาย จะ animate ค่าและ rebuild builder ของคุณ — implicit animation สำหรับ property ใด ๆ

AnimationController ผลิตค่าจาก 0 ถึง 1 ตลอด duration โดยถูก tick ด้วย vsync Tween map 0..1 ไปยัง range ของคุณ และ AnimatedBuilder rebuild เฉพาะส่วนที่ animate

class _PulseState extends State<Pulse> with SingleTickerProviderStateMixin {
late final AnimationController _c = AnimationController(
vsync: this, // ticker (เหตุที่ต้องใช้ mixin)
duration: const Duration(seconds: 1),
)..repeat(reverse: true);
@override
void dispose() { _c.dispose(); super.dispose(); } // dispose controller เสมอ
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _c,
builder: (context, child) => Opacity(opacity: _c.value, child: child),
child: const FlutterLogo(), // build ครั้งเดียว ไม่ rebuild ทุก tick
);
}
}
flowchart LR
  ctrl["AnimationController
tick 0 ถึง 1 ผ่าน vsync"] --> tween["Tween / Curve
map 0..1 ไปยัง range ของคุณ"]
  tween --> ab["AnimatedBuilder
rebuild เฉพาะส่วนที่ animate"]
  ab --> ui["widget update ทุก tick"]
pipeline ของ explicit animation

สองกฎที่กันบั๊กที่พบบ่อย: controller ต้องมี vsync (mix in SingleTickerProviderStateMixin) และคุณต้อง dispose() controller ทิ้ง — controller ที่ leak จะ tick ต่อไปเรื่อย ๆ ส่งส่วนที่ไม่เปลี่ยนเป็น child เพื่อไม่ให้ถูก rebuild ทุก tick

Curve (Curves.easeInOut, Curves.elasticOut, …) reshape เวลาเชิงเส้นให้เป็นการเคลื่อนไหวที่เป็นธรรมชาติ ทั้งสองตระกูลรับ curve ได้ การใส่ curve คือความต่างระหว่างการ slide แบบกลไกกับการเคลื่อนไหวที่รู้สึกว่าออกแบบมา

implicit animation คืออะไร?
AnimationController ต้องมีอะไรจึงจะ tick ได้?
จะเกิดอะไรถ้าลืม dispose AnimationController?
ควรเลือก explicit animation เหนือ implicit เมื่อไร?