Как анимировать несколько твинов во Flutter?

Итак, я пытаюсь добиться чего-то похожего на этот пример Flutter, представленный в документации.

Но я не хочу, чтобы он подчинялся кнопке, я хочу, чтобы он сам выполнял анимацию и делал это без остановок.

По крайней мере, мне нужен Tween для высоты, ширины и цвета, однако я нахожусь в запутанной ситуации с тем, как анимировать их все, я вроде как это сделал, но знаю, что мне не хватает свойства Animation в моем виджете. , это мой результат на данный момент:

Он совершает переходы между генерируемыми случайными значениями.

Это мой код:

class AnimatedSquare extends StatefulWidget {
  const AnimatedSquare({super.key});

  @override
  State<AnimatedSquare> createState() => _AnimatedSquareState();
}

class _AnimatedSquareState extends State<AnimatedSquare>
  with SingleTickerProviderStateMixin{

  late final AnimationController controller;
  late Tween<double> containerWidth;
  late Tween<double> containerHeight;
  late Tween<Color> containerColor;
  Random random = Random();

  @override
  void initState() {
    controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 800)
    )
    ..forward()
    ..addListener(() {
      if (controller.isCompleted) {
        setNewValues();
        controller.forward();
      }
    });
    containerWidth = Tween<double>(
      begin: 250,
      end: random.nextInt(300).toDouble()
    );
    containerHeight = Tween<double>(
      begin: 250,
      end: random.nextInt(300).toDouble()
    );
    containerColor = Tween(
      begin: Colors.red,
      end: Color.fromRGBO(
        random.nextInt(255),
        random.nextInt(255),
        random.nextInt(255), 
        1
      )
    );

    super.initState();
  }

  void setNewValues() {
    containerWidth.begin = containerWidth.end;
    containerHeight.begin = containerHeight.end;
    containerColor.begin = containerColor.end;
    controller.reset();
    containerWidth.end = random.nextInt(300).toDouble();
    containerHeight.end = random.nextInt(300).toDouble();
    containerColor.end = Color.fromRGBO(
      random.nextInt(255),
      random.nextInt(255),
      random.nextInt(255),
      1
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          height: containerHeight.begin,
          width: containerWidth.begin,
          decoration: BoxDecoration(
            color: containerColor.begin
          ),
        );
      },
    );
  }

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

Может ли кто-нибудь помочь мне решить эту проблему? Я не смог найти много информации в Интернете, использовал этот вопрос в качестве последнего ресурса.

вместо этих Tween, AnimatedBuilder, AnimationController, слушателей и т. д., почему бы вам просто не использовать AnimatedContaineronEnd: ... для повторения анимации)? подробнее здесь: api.flutter.dev/flutter/widgets/AnimatedContainer-class.html - в документации говорится: «Анимированная версия Container, которая постепенно меняет свои значения с течением времени. AnimatedContainer будет автоматически анимироваться между старыми и новые значения свойств, когда они изменяются с использованием предоставленной кривой и продолжительности. Свойства, имеющие значение null, не анимируются. Его дочерние элементы и потомки не анимируются.

pskink 03.04.2024 08:10
0
1
178
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

AnimatedContainer(
    height: containerHeight.begin,
    width: containerWidth.begin,
    decoration: BoxDecoration(color: containerColor.begin),
    duration: const Duration(seconds: 2),
    curve: Curves.fastOutSlowIn,
);

используйте это как возврат на AnimatedBuilder

Вам просто нужно использовать Animation с Tween для конкретного движения/действия.

Также вам нужно слушать addStatusListener вместо прослушивателя анимации.

Это ваш код исправлен.

class _AnimatedSquareState extends State<AnimatedSquare>
    with SingleTickerProviderStateMixin {
  late final AnimationController controller;
  late Animation<double> containerWidth;
  late Animation<double> containerHeight;
  late Animation<Color?> containerColor;
  Random random = Random();

  @override
  void initState() {
    controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 800))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          setNewValues();
          controller.forward(from: 0);
        }
      })
      ..forward();
    setNewValues();

    super.initState();
  }

  void setNewValues() {
    containerWidth =
        Tween<double>(begin: 250, end: random.nextInt(300).toDouble()).animate(
      controller,
    );
    containerHeight =
        Tween<double>(begin: 250, end: random.nextInt(300).toDouble()).animate(
      controller,
    );
    containerColor = ColorTween(
            begin: Colors.red,
            end: Color.fromRGBO(random.nextInt(255), random.nextInt(255),
                random.nextInt(255), 1))
        .animate(
      controller,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: controller,
        builder: (context, child) {
          return Container(
            height: containerHeight.value,
            width: containerWidth.value,
            decoration: BoxDecoration(color: containerColor.value),
          );
        },
      ),
    );
  }

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

Метод setNewValues ​​использовался для анимации от последних случайных значений к новым случайным значениям, без этого он получает странный скачок с самого начала. Как я могу получить свойства конца и начала, если переменные установлены как анимация? похоже, ты удалил эту часть

Santiago G 03.04.2024 07:57

Неважно, я просто использовал часть вашего ответа, чтобы решить эту проблему, хотя это было полезно, спасибо!

Santiago G 03.04.2024 08:10

Вы могли бы проголосовать за мой ответ, если бы он был полезен

diegoveloper 03.04.2024 12:00
Ответ принят как подходящий

По сути, я использовал часть ответа @diegoveloper, чтобы решить свою проблему. Я просто добавил переменную Animation для каждого Tween, так как мне нужно было получить свойства end и Begin в методе setNewValues().

Окончательный код:

class AnimatedSquare extends StatefulWidget {
  const AnimatedSquare({super.key});

  @override
  State<AnimatedSquare> createState() => _AnimatedSquareState();
}

class _AnimatedSquareState extends State<AnimatedSquare>
    with SingleTickerProviderStateMixin {

  late final AnimationController controller;
  late Tween<double> containerWidth;
  late Tween<double> containerHeight;
  late Tween<Color?> containerColor;
  late Animation<double> animContainerWidth;
  late Animation<double> animContainerHeight;
  late Animation<Color?> animContainerColor;
  Random random = Random();

  @override
  void initState() {
    controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 800)
    )..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        setNewValues();
        controller.forward(from: 0);
      }
    })..forward();

    containerWidth = Tween<double>(
      begin: 250,
      end: random.nextInt(300).toDouble()
    );

    containerHeight = Tween<double>(
      begin: 250,
      end: random.nextInt(300).toDouble()
    );

    containerColor = ColorTween(
      begin: Colors.red,
      end: Color.fromRGBO(
        random.nextInt(255),
        random.nextInt(255),
        random.nextInt(255),
        1
      )
    );

    animContainerWidth = containerWidth.animate(CurvedAnimation(
      parent: controller,
      curve: Curves.fastOutSlowIn
    ));
    animContainerHeight = containerHeight.animate(CurvedAnimation(
      parent: controller,
      curve: Curves.fastOutSlowIn
    ));
    animContainerColor = containerColor.animate(CurvedAnimation(
      parent: controller,
      curve: Curves.fastOutSlowIn
    ));

    super.initState();
  }

  void setNewValues() {
    containerWidth.begin = containerWidth.end;
    containerHeight.begin = containerHeight.end;
    containerColor.begin = containerColor.end;
    controller.reset();
    containerWidth.end = random.nextInt(300).toDouble();
    containerHeight.end = random.nextInt(300).toDouble();
    containerColor.end = Color.fromRGBO(
      random.nextInt(255),
      random.nextInt(255),
      random.nextInt(255),
      1
    );

  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: controller,
        builder: (context, child) {
          return Container(
            height: animContainerHeight.value,
            width: animContainerWidth.value,
            decoration: BoxDecoration(
              color: animContainerColor.value,
              borderRadius: BorderRadius.circular(30)
            ),
          );
        },
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}
  • Результат:

Большое спасибо!

Другие вопросы по теме