انیمیشن در فلاتر: از AnimatedContainer تا AnimationController

انیمیشن خوب اپ را زنده می‌کند، ولی باید درست ساخته شود. این مقاله انیمیشن‌های implicit و explicit، Hero و نکات مهم کارایی را با مثال کد توضیح می‌دهد.

۳ دقیقه مطالعه
انیمیشن در فلاتر: از AnimatedContainer تا AnimationController

انیمیشن‌های خوب اپ را «زنده» می‌کنند، ولی اگر بد پیاده‌سازی شوند اپ را کند می‌کنند. فلاتر دو خانواده انیمیشن دارد و انتخاب درستش نصف راه است.

#۱. انیمیشن‌های Implicit، ساده‌ترین راه

فقط مقدار جدید را بدهید؛ فلاتر خودش مسیر بین مقدار قدیم و جدید را انیمیت می‌کند. ویجت‌هایی مثل AnimatedContainer، AnimatedOpacity، AnimatedAlign و AnimatedSwitcher:

AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeOut,
  width: expanded ? 240 : 120,
  height: 120,
  decoration: BoxDecoration(
    color: expanded ? Colors.indigo : Colors.teal,
    borderRadius: BorderRadius.circular(expanded ? 32 : 12),
  ),
)

با هر setState که expanded را عوض کند، تغییر نرم انجام می‌شود. در ۸۰٪ موارد همین کافی است.

#۲. TweenAnimationBuilder

وقتی ویجت implicit آماده‌ای برای کارتان نیست:

TweenAnimationBuilder<double>(
  tween: Tween(begin: 0, end: progress),
  duration: const Duration(milliseconds: 600),
  builder: (context, value, _) => LinearProgressIndicator(value: value),
)

#۳. انیمیشن Explicit با AnimationController

وقتی کنترل کامل می‌خواهید (شروع، توقف، تکرار، برگشت):

class _PulseState extends State<Pulse> with SingleTickerProviderStateMixin {
  late final AnimationController _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 1),
  )..repeat(reverse: true);

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

  @override
  Widget build(BuildContext context) {
    return ScaleTransition(
      scale: Tween(begin: 0.9, end: 1.1).animate(_controller),
      child: const FlutterLogo(size: 80),
    );
  }
}

قاعده‌ی طلایی: کنترلر را حتماً dispose کنید.

#۴. Hero، انتقال بین صفحه‌ها

اگر دو ویجت در دو صفحه tag یکسان داشته باشند، فلاتر هنگام رفتن به صفحه‌ی بعد، یکی را به دیگری تبدیل (پرواز) می‌کند:

Hero(tag: 'product-$id', child: Image.network(url))

#نکته‌های کارایی

  • از AnimatedBuilder با پارامتر child استفاده کنید تا بخش ثابت درخت در هر فریم دوباره ساخته نشود.
  • بخش‌های سنگین و ثابت را با RepaintBoundary جدا کنید.
  • روی درخت بزرگ از Opacity استفاده نکنید؛ FadeTransition یا AnimatedOpacity ارزان‌تر است.
  • انیمیشن‌هایی که فقط transform یا opacity را عوض می‌کنند ارزان‌ترین‌اند؛ اندازه و چیدمان را در هر فریم عوض نکنید.

#چند اصل طراحی

  • مدت بین ۲۰۰ تا ۴۰۰ میلی‌ثانیه برای بیشتر تغییرها مناسب است.
  • از منحنی طبیعی (Curves.easeOut، easeInOut) استفاده کنید، نه خطی.
  • انیمیشن باید معنا داشته باشد: به کاربر بگوید چه اتفاقی افتاد، نه فقط تزئین باشد.

اگر از کاربری انتظار دارید «حرکت کمتر» را در تنظیمات دستگاه فعال کند، با MediaQuery.disableAnimationsOf(context) آن را در نظر بگیرید.

فلاتر را حرفه‌ای و پروژه‌محور یاد بگیر

دوره‌ی VIP فست فلاتر از صفر تا انتشار اپلیکیشن، با پشتیبانی مستقیم مدرس.

مشاهده‌ی دوره‌ی VIP