Что такое AnimationController в Flutter?

Ответ

AnimationController — это центральный дирижёр анимации в Flutter. Это специальный объект, который генерирует линейную последовательность значений (обычно от 0.0 до 1.0) за заданный промежуток времени и управляет жизненным циклом анимации (старт, пауза, остановка, повтор).

Ключевые аспекты и пример использования:

  1. TickerProvider: Контроллеру требуется источник тиков (Ticker). На практике это означает использование миксина SingleTickerProviderStateMixin (или TickerProviderStateMixin для нескольких контроллеров) в State классе виджета.

  2. Связь с анимацией: Сам по себе контроллер генерирует только числа. Чтобы анимировать свойства виджета, его связывают с Animation или CurvedAnimation.

  3. Типичный паттерн использования в 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. И делает он это ровно столько, сколько ты ему скажешь. Его задача — рулить всей этой движухой: старт, пауза, стоп, повтор. Без него нихуя не зашевелится.

Основные фишки, без которых он — просто кусок кода:

  1. TickerProvider — его батарейка. Контроллеру нужен источник тиков, как часовому механизму. На практике это значит, что твой State класс должен быть, ёпта, «тикер-провайдером». Проще всего добавить миксин SingleTickerProviderStateMixin. Это как сказать: «Я, блядь, готов поставлять тебе тики, делай что хочешь».

  2. Один — генерит, другой — использует. Сам контроллер — тупой генератор чисел. Чтобы эти числа превратились в увеличение кружка или движение кнопки, их нужно скормить в Animation или обернуть в CurvedAnimation (чтобы было не линейно, а с ускорением, как будто хуй с горы скатился).

  3. Как это обычно выглядит в коде (готовый рецепт):

    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() сделать, а то потом волнение ебать, где память подтекает.