Advertisement
  1. Web Design
  2. Android SDK

Google Flutter с нуля: анимирование виджетов

Scroll to top
Read Time: 8 min
This post is part of a series called Google Flutter From Scratch.
Google Flutter From Scratch: Grids, Lists, and Data Sources
Google Flutter From Scratch: Using Firebase Services

() translation by (you can also view the original English article)

При правильном использовании, анимации могут сыграть значительную роль в том, как пользователь видит ваше приложение. Приложение с множеством быстрых, еле-заметных анимаций выглядит более законченным и профессиональным, чем то, которое без анимации. На таком переполненном рынке как Google Play, это может сыграть роль в успехе или провале.

Flutter наверно только один гибридный движок для разработки приложений, доступные сегодня. С ним вы можете создать сложные анимации, которые могут выполняться постоянно в 60 кадров в секунду. В этом уроке, я помогу вам понять основы анимирования виджетов с Flutter. Я так же представлю вам несколько новых виджетов, которые упростят вам анимационный код.

1. Подготовка виджета к анимации

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

Чтобы создать виджет, который можно легко анимировать, начните с создания класса, который расширяет класс StatefulWidget и переопределяет метод createState(). Этот метод, должен возвращать экземпляр State.

1
class MyApp extends StatefulWidget {
2
  @override
3
  State<StatefulWidget> createState() {
4
    return new MyState();
5
  }
6
}

Для анимации вашего виджета, объект state, который вы ассоциируете с вашим «stateful widget», должен не только расширять класс State, но ещё должен использовать примесь SingleTickerProviderStateMixin. Как видно из названия, примесь (mixin) предлагает объект Ticker, который постоянно выдаёт callback-и, обычно известные как тики.  Так как тики генерируются постоянно с равными промежутками времени, вы можете использовать их, для настройки отображения каждого кадра анимации.

1
class MyState extends State<MyApp>
2
              with SingleTickerProviderStateMixin {
3
  @override
4
  Widget build(BuildContext context) {
5
    // More code here

6
  }
7
}

2. Создание анимации Tween

Анимация tween является одной из простецких анимаций, которую вы можете создать с помощью Flutter. При её создании, всё, что вам потребуется сделать это предоставить два разных значения: начальное значение и конечное. Затем фреймворк автоматически сгенерирует набор промежуточных значений, от начального значения, плавно нарастающие до конечного значения. Постепенно применяя эти промежуточные значения на какое-либо свойство вашего виджета, вы анимируете это данное свойство.

Давайте создадим простую tween анимацию, которая будет перемещать виджет от верхнего левого угла экрана в правый верхний угол. Другими словами, давайте анимируем свойство left нашего виджета.

Для создания и управления анимацией, вам понадобится объекты Animation и AnimationController. Добавьте их в качестве переменных к вашему состоянию (state):

1
Animation<double> animation;
2
AnimationController controller;

Вы должны инициализировать оба объекта, переопределив метод initState() вашего класса. В этом методе, вызываем конструктор класса AnimationController, чтобы инициализировать контроллер. Потребуется указать объект TickerProvider. А так как state уже использует примесь SingleTickerProviderStateMixin, вы можете просто использовать this. Дополнительно, Вы можете использовать свойство duration, чтобы задать продолжительность анимации.

Следующий код создаёт контроллер анимации с продолжительностью четыре секунды.

1
@override
2
void initState() {
3
    super.initState();
4
    controller = new AnimationController(vsync: this,
5
        duration: new Duration(seconds: 4));
6
7
    // More code here

8
}

Теперь, вы можете создать объект Tween, определив начальное и конечное значения вашей анимации.

1
Tween tween = new Tween<double>(begin: 10.0, end: 180.0);

Для связи объекта Tween с объектом AnimationController, вы должны вызвать метод animate().  Данный метод вернет значение, которое является объектом Animation, и которое вы можете сохранить как вторую переменную вашего класса.

1
animation = tween.animate(controller);

Объект Animation генерирует событие анимации на каждый тик тикера, которые вы должны обработать, чтобы ваша анимация сработала. Для этого, вы можете использовать собственный метод addListener(). Дополнительно, в обработчике события, вы должны вызвать метод setState(), чтобы состояние вашего виджета обновилось и он перерисовался. В следующем коде показано как:

1
animation.addListener(() {
2
    setState(() {
3
    });
4
});

Обратите внимание, что вам не нужно писать никакого кода в методе setState(), если конечно вам не нужно обновить состояние другой переменной.

После этого, для запуска анимации, вам нужно вызвать метод forward() от контроллера анимации.

1
controller.forward();

Анимация готова. Однако, вы ещё не назначили её на какой-либо виджет, который отрисовывается на экране. Поэтому теперь, я рекомендую вам применить его на виджет Positioned, содержащий виджет Material Icon.  Для этого, при создании виджета, просто задайте value объекта Animation в качестве свойства left виджета.

То есть, добавляем следующий код, который переопределяет метод build() для состояния:

1
@override
2
Widget build(BuildContext context) {
3
    return new Container(
4
      color: Colors.white,
5
      child: new Stack(
6
        children: <Widget>[
7
          new Positioned(
8
              child: new Material(
9
                child: new Icon(Icons.airport_shuttle,
10
                  textDirection: TextDirection.ltr,
11
                  size: 81.0
12
                )
13
              ),
14
            left: animation.value, // Animated value

15
            top: 30.0  // Fixed value

16
          )
17
        ],
18
      textDirection: TextDirection.ltr,)
19
    );
20
}

Заметьте, что в дереве виджета имеется виджет Stack, потому что виджет Positioned обязательно должен быть встроен в другой виджет.

Вы можете запустить ваше приложение, чтобы увидеть анимацию.

3. Обработка событий состояний анимации

Если вы хотите быть уведомлённым об окончании анимации, вы можете прикрепить объект AnimationStatusListener к объекту Animation. В прослушивателе — если текущий статус анимации completed или dismissed, значит анимация окончена.

В Flutter анимацию Tween можно обратить. Вот почему там две разные константы статуса, означающие окончание анимации. Если текущее состояние — completed, это означает, что анимация окончена на конечном значении tween.  Если статус dismissed, это означает, что анимация окончена на начальном значении. Используя два эти статуса и методы forward() и reverse(), вы можете запросто создать анимации от начала или с конца.

Следующий код, который вы можете добавить в метод initState(), показывает вам как обратить и повторить анимацию, созданную в предыдущем шаге:

1
animation.addStatusListener((status) {
2
    if(status == AnimationStatus.completed)
3
        controller.reverse();
4
    else if(status == AnimationStatus.dismissed)
5
        controller.forward();
6
});

Если вы снова запустите приложение, вы должны увидеть постоянно повторяющуюся анимацию.

4. Использование анимированных виджетов

Движок Flutter предлагает несколько запросто анимируемых виджетов, которые вы можете использовать, чтобы сделать код ваших анимаций менее подробным и более пригодным для повторного использования. Все они являются подклассами класса AnimatedWidget и в своих конструкторах ожидают объекты Animation или AnimationController.

Один из наиболее используемых анимированных виджетов — RotationTransition. Он позволяет быстро применить анимацию вращения на дочерние элементы. Чтобы его использовать, сначала создайте новый контроллер анимации. Следующий код создаёт такой с продолжительностью в 6 секунд:

1
controller = new AnimationController(vsync: this,
2
                    duration: new Duration(seconds: 6));

На этот раз, чтобы запустить анимацию, вместо метода forward(), используем метод repeat(). Это гарантирует, что анимация будет повторяться бесконечно.

1
controller.repeat();

Дабы не усложнять, вы можете использовать виджет Text как дочерний элемент виджета RotationTransition. Поэтому создадим соответствующую последовательность виджетов.  При создании виджета RotationTransition, убедитесь, что вы указали значение его свойства turns как объект AnimationController, который вы только что создали. Дополнительно вы можете разместить оба виджета внутри виджета Center. Вот так:

1
@override
2
Widget build(BuildContext context) {
3
    return new Center(
4
        child: new RotationTransition(
5
            turns: controller,
6
            child: new Text("\u{1F43A}",
7
              textDirection: TextDirection.ltr,
8
              style: new TextStyle(fontSize: 85.0),)
9
        )
10
    );
11
}

В коде выше, я использовал юникода для эмодзи в качестве содержимого для виджета Text. Это возможно благодаря тому, что Flutter поддерживает эмодзи прямо «из коробки».

При повторном запуске приложения, вы должны видеть что-то вроде этого:

Виджет ScaleTransition очень похож на виджет RotationTransition. Как вы могли догадаться, с ним вы можете анимировать масштаб его дочерних элементов. При его использовании, всё что вам нужно это передать объект AnimationController в его свойство scale. Следующий код показывает как:

1
@override
2
Widget build(BuildContext context) {
3
    return new Center(
4
        child: new ScaleTransition(
5
            scale: controller,
6
            child: new Text("\u{1F43A}",
7
              textDirection: TextDirection.ltr,
8
              style: new TextStyle(fontSize: 85.0),)
9
        )
10
    );
11
}

Теперь вы можете видеть, как масштаб виджета Text меняется при анимации.

Если вам интересно, почему мы не создали ни одного объекта Tween для приведённых выше анимаций, так это потому, по умолчанию класс AnimationController использует 0.0 и 1.0 в качестве начального begin и конечного end значений.

5. Использование кривых

Все анимации, которые мы создали ранее следуют по прямой извилистой. В результате они не выглядят очень реалистично. Изменив способ, которым объект Tween генерирует промежуточные значения, вы можете изменить анимацию.

У Flutter есть класс CurvedAnimation, который позволяет вам применять нелинейные кривые для ваших tween-ов. При использовании с классом Curves, который предлагает множество кривых, таких как easeIn и easeOut, вы можете создать анимации, которые выглядят более естественно.

Чтобы создать объект CurvedAnimation, вам понадобиться родительский объект AnimationController. Вы можете использовать один из контроллеров, который вы создали ранее или создать новый.  Следующий код создаёт новый контроллер, продолжительность которого установлена на пять секунд, и объект CurvedAnimation, чьё свойство curve задано на кривую bounceOut:

1
controller = new AnimationController(
2
    vsync: this,
3
    duration: new Duration(seconds: 5)
4
);
5
6
CurvedAnimation curvedAnimation = new CurvedAnimation(
7
    parent: controller,
8
    curve: Curves.bounceOut
9
);

Теперь вы можете создать объект Tween и применить к нему объект CurvedAnimation, вызвав метод animate(). Когда анимация будет готова, не забудьте добавить слушателя анимации, обновить состояние, а затем вызвать метод forward() для её запуска.

1
Tween myTween = new Tween<double>(begin: 150.0, end: 450.0);
2
animation = myTween.animate(curvedAnimation);
3
4
animation.addListener(() {
5
  setState(() {
6
  });
7
});
8
9
controller.forward();

Чтобы увидеть анимацию, давайте применим её к свойству top виджета Positioned. Вы можете добавить туда любые дочерние виджеты. В следующем коде я добавляю виджет Text, отображающий другой эмодзи.

1
@override
2
Widget build(BuildContext context) {
3
    return new Stack(
4
      children: [
5
        new Positioned(
6
          child: new Text("\u{26BE}",
7
              textDirection: TextDirection.ltr,
8
              style: new TextStyle(
9
                fontSize: 70.0
10
              )
11
          ),
12
          left: 50.0,
13
          top: animation.value  // Animated property

14
        )
15
      ],
16
      textDirection: TextDirection.ltr,
17
    );
18
}

После перезапуска, ваше приложение должно показывать следующую анимацию.

Вывод

Вы познали основы создания tween анимации с использованием движка Flutter. В этом уроке, вы также выучили, как заставить их выглядеть более естественно, используя кривые.  Надо понимать, что в медленном режиме, который включён по умолчанию для разработки, анимация может показаться немного лаганутой и нестабильной. Только в версии релиза, вы можете увидеть истинную производительность.

За большей информацией обратитесь к официальной документации.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.