Ответ
AnimationController — это центральный дирижёр анимации в Flutter. Это специальный объект, который генерирует линейную последовательность значений (обычно от 0.0 до 1.0) за заданный промежуток времени и управляет жизненным циклом анимации (старт, пауза, остановка, повтор).
Ключевые аспекты и пример использования:
-
TickerProvider: Контроллеру требуется источник тиков (
Ticker). На практике это означает использование миксинаSingleTickerProviderStateMixin(илиTickerProviderStateMixinдля нескольких контроллеров) вStateклассе виджета. -
Связь с анимацией: Сам по себе контроллер генерирует только числа. Чтобы анимировать свойства виджета, его связывают с
AnimationилиCurvedAnimation. -
Типичный паттерн использования в StatefulWidget:
class PulsatingCircle extends StatefulWidget { const PulsatingCircle({super.key}); @override State<PulsatingCircle> createState() => _PulsatingCircleState(); } class _PulsatingCircleState extends State<PulsatingCircle> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _scaleAnimation; @override void initState() { super.initState(); // 1. Инициализация контроллера _controller = AnimationController( vsync: this, // Источник тиков duration: const Duration(milliseconds: 800), ); // 2. Создание анимации с кривой и диапазоном значений _scaleAnimation = Tween<double>(begin: 1.0, end: 1.5).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); // 3. Запуск анимации с повторением _controller.repeat(reverse: true); } @override Widget build(BuildContext context) { // 4. Использование анимации в виджете через AnimatedBuilder return AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( width: 100, height: 100, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ); }, ); } @override void dispose() { // 5. ОБЯЗАТЕЛЬНО освобождаем ресурсы _controller.dispose(); super.dispose(); } }
Почему это важно: Без AnimationController невозможно создать кастомную, управляемую по времени анимацию. Он предоставляет низкоуровневый контроль, на котором строятся все высокоуровневые анимационные виджеты (AnimatedContainer, AnimatedOpacity и т.д.).
Ответ 18+ 🔞
Слушай, давай разберём эту штуку, как будто объясняю на пальцах после третьей рюмки. AnimationController — это, блядь, главный дирижёр в этом цирке под названием «анимация во Flutter». Представь себе метроном, который туда-сюда долбит, но вместо звука он выплёвывает числа от 0.0 до 1.0. И делает он это ровно столько, сколько ты ему скажешь. Его задача — рулить всей этой движухой: старт, пауза, стоп, повтор. Без него нихуя не зашевелится.
Основные фишки, без которых он — просто кусок кода:
-
TickerProvider — его батарейка. Контроллеру нужен источник тиков, как часовому механизму. На практике это значит, что твой
Stateкласс должен быть, ёпта, «тикер-провайдером». Проще всего добавить миксинSingleTickerProviderStateMixin. Это как сказать: «Я, блядь, готов поставлять тебе тики, делай что хочешь». -
Один — генерит, другой — использует. Сам контроллер — тупой генератор чисел. Чтобы эти числа превратились в увеличение кружка или движение кнопки, их нужно скормить в
Animationили обернуть вCurvedAnimation(чтобы было не линейно, а с ускорением, как будто хуй с горы скатился). -
Как это обычно выглядит в коде (готовый рецепт):
class PulsatingCircle extends StatefulWidget { const PulsatingCircle({super.key}); @override State<PulsatingCircle> createState() => _PulsatingCircleState(); } class _PulsatingCircleState extends State<PulsatingCircle> with SingleTickerProviderStateMixin { // 1. Вот эта приблуда — батарейка late AnimationController _controller; late Animation<double> _scaleAnimation; @override void initState() { super.initState(); // 2. Рождаем самого дирижёра _controller = AnimationController( vsync: this, // Говорим: «Батарейка тут, блядь, я!» duration: const Duration(milliseconds: 800), // Длительность — 800 мс ); // 3. Делаем из чисел — полезную анимацию. От 1.0 до 1.5, да ещё и с плавным ускорением. _scaleAnimation = Tween<double>(begin: 1.0, end: 1.5).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); // 4. Запускаем эту пьяную карусель туда-сюда бесконечно _controller.repeat(reverse: true); } @override Widget build(BuildContext context) { // 5. Строим виджет, который слушает нашу анимацию и дёргается при каждом тике return AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, // Берём текущее значение (то самое от 0.0 до 1.0, преобразованное в 1.0..1.5) child: Container( width: 100, height: 100, decoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ); }, ); } @override void dispose() { // 6. САМОЕ ГЛАВНОЕ, ЕБАНЬКО! Убить контроллер, когда виджет помирает. Иначе он будет тикать в пустоту и жрать память, как мартышлюшка бананы. _controller.dispose(); super.dispose(); } }
А зачем этот геморрой? Да затем, чувак, что все эти красивые AnimatedContainer и AnimatedOpacity внутри себя используют именно этот контроллер. Это фундамент, болгарка и сварочный аппарат в одном флаконе. Хочешь сделать что-то своё, хитрое — без него никуда. Будет тебе и хиросима, и нагасаки в одном виджете, если надо. Главное — не забудь его dispose() сделать, а то потом волнение ебать, где память подтекает.