Основы анимации в Flutter

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

Требования

Для работы с cross-screen анимацией нужно знать, как создавать маршруты. Вы можете просмотреть документацию, если пока что вы еще не знакомы с основами создания маршрутов.

Линейная анимация

Основные три части анимации – это ticker для управления временем, controller для регистрации параметров (таких как продолжительность), а также значения, которые мы хотим изменить. Прежде чем наш виджет будет отрисован, мы можем установить в initState параметры для контроллера, определить его направление и добавить прослушивателя для сброса состояния виджетов при каждом изменении.

По умолчанию контроллер переместит изменение с 0 на 1 за то время, которое мы установим в параметре duration; мы можем отобразить controller.value в прослушивателе, чтобы увидеть, как это работает. Установив свойства upperBound или lowerBound, мы можем изменить наши начальные и конечные значения по умолчанию.

Откройте файл main.dart:

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;

  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this); // Links this controller to this widget so it won't run if the parent widget isn't rendered, to save on resources.

    controller.forward();
    controller.addListener(() => setState(() {}));
  }
}

Чтобы использовать анимацию, нам просто нужно установить все, что мы хотим, например непрозрачность, в controller.value.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Opacity(
          opacity: controller.value,
          child: Container(width: 50, height: 50, color: Colors.red),
        ),
      ),
    );
  }

Кривая анимация

Вместо скучной линейной анимации можно использовать различные виды кривой анимации, что позволяет точно контролировать, как изменяется контроллер. Для этого мы можем с помощью CurvedAnimation создать своего рода оболочку над исходным контроллером. Эта оболочка примет наш контроллер в качестве родительского и кривую, которую нужно применить. При использовании такой анимации не может быть нижней и верхней границ вне 0 и 1. В документации есть действительно широкий список опций по работе с этим видом.

Еще один полезный метод для контроллера – addStatusListener, который позволяет задействовать его жизненный цикл. При этом контроллер запускает завершенное или отклоненное событие всякий раз, когда его значение достигает своей верхней или нижней границы. Мы можем использовать этот метод с его методами forward и reverse, чтобы создать бесконечный цикл анимации.

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  void initState() {
    super.initState();
    controller = AnimationController(
        duration: Duration(seconds: 1),
        vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);

    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: Colors.red),
      ),
    );
  }
}

Анимация tween

Мы можем работать с диапазонами других составляющих, таких как цвета, с помощью анимации tween (сокращение от between, «промежуточный»). Как и в случае с предыдущим методом анимации, этот метод действует как оболочка для контроллера или анимации, где мы можем установить значение для цвета.

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;
  Animation changeColor;

  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(seconds: 1), vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
    changeColor = ColorTween(begin: Colors.red, end: Colors.blue).animate(animation);
    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: changeColor.value),
      ),
    );
  }
}

Cross-screen анимация

Еще одна замечательная техника анимации – виджет Hero, который обеспечивает переход между виджетами на разных экранах. Нужно просто обернуть каждый отдельный виджет Hero с помощью соответствующих тегов. Теги должны быть уникальными, и на экране их не может быть больше одного. Вот, например, первый экран:

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
      SizedBox(height: 1),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          Hero(tag: 'icon', child: Icon(Icons.add)),
        ]),
      Navbar()
  ]));
}

И второй:

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        SizedBox(height: 1),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            SizedBox(width: 1),
            Hero(
              tag: 'icon',
              child: Icon(Icons.add, color: Colors.red, size: 75)),
          ]),
        Navbar()
      ]),
  );
}

Заключение

На самом деле мы только прикоснулись к тому, на что способен Flutter в плане анимации. Большинство веб-технологий прошлого оставляли анимацию на потом, но команда разработчиков Flutter проделала потрясающую работу – теперь можно держать анимацию в уме при разработке своих приложений.

Читайте также: Как создать свое первое приложение Flutter

Tags:

Добавить комментарий