Анимации при правильном использовании могут существенно повлиять на восприятие вашего приложения пользователями. Приложение с множеством быстрых, тонких анимаций имеет тенденцию выглядеть намного более отточенным и профессиональным, чем без него. В Google Play, переполненном рынке, это может означать разницу между успехом и провалом.
Flutter, пожалуй, единственная платформа для разработки гибридных приложений, доступная на сегодняшний день, которая позволяет создавать сложные анимации, которые могут работать постоянно со скоростью 60 кадров в секунду. В этом уроке я помогу вам понять основы анимации виджетов Flutter. Я также познакомлю вас с несколькими новыми виджетами, которые могут упростить ваш код анимации.
1. Подготовка виджета для анимации
Среда Flutter предполагает, что вы будете следовать функциональному, реактивному подходу к программированию. Поэтому, чтобы иметь возможность анимировать виджет, вы должны иметь возможность обновлять его состояние многократно, через определенные промежутки времени.
Чтобы создать виджет, который легко анимировать, начните с создания класса, который расширяет класс StatefulWidget
и переопределяет его createState()
. Внутри метода убедитесь, что вы возвращаете экземпляр State
.
1
2
3
4
5
6
|
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyState();
}
}
|
Чтобы быть анимируемым, объект состояния, который вы связываете с вашим виджетом с состоянием, должен не только расширять класс State
, он также должен использовать SingleTickerProviderStateMixin
именем SingleTickerProviderStateMixin
. Как следует из его названия, mixin предлагает объект Ticker
, который многократно генерирует обратные вызовы, обычно известные как тики. Поскольку метки генерируются неоднократно через равные промежутки времени, вы можете использовать их, чтобы решить, когда будут отображаться отдельные кадры вашей анимации.
1
2
3
4
5
6
7
|
class MyState extends State<MyApp>
with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
// More code here
}
}
|
2. Создание анимации твина
Анимация анимации — одна из самых простых анимаций, которую вы можете создать с помощью Flutter. При создании все, что вам нужно сделать, это предоставить два разных значения: начальное значение и конечное значение. Структура затем автоматически генерирует набор промежуточных значений — или промежуточных значений — которые начинаются с начального значения и плавно растут, чтобы соответствовать конечному значению. Постепенно применяя эти промежуточные значения к свойству вашего виджета, вы анимируете это свойство.
Давайте теперь создадим простую анимацию движения, которая перемещает виджет из верхнего левого угла экрана в верхний правый угол экрана. Другими словами, давайте оживим left
свойство виджета.
Для создания и управления анимацией вам понадобится объект AnimationController
объект AnimationController
. Добавьте их как переменные-члены вашего состояния:
1
2
|
Animation<double> animation;
AnimationController controller;
|
Вы должны инициализировать оба объекта, переопределив метод initState()
вашего класса. Внутри метода вызовите конструктор класса AnimationController
для инициализации контроллера. Он ожидает объект TickerProvider
качестве одного из своих входных данных. Поскольку штат уже использует SingleTickerProviderStateMixin
, вы можете передать его ему. Кроме того, вы можете использовать свойство duration
чтобы указать продолжительность анимации.
Следующий код создает контроллер анимации, продолжительность которого составляет четыре секунды:
1
2
3
4
5
6
7
8
|
@override
void initState() {
super.initState();
controller = new AnimationController(vsync: this,
duration: new Duration(seconds: 4));
// More code here
}
|
На этом этапе вы можете создать объект 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
2
3
4
|
animation.addListener(() {
setState(() {
});
});
|
Обратите внимание, что вам не нужно писать код внутри setState()
если у вас нет других переменных состояния для обновления.
Наконец, чтобы запустить анимацию, вы должны вызвать метод forward()
контроллера анимации.
1
|
controller.forward();
|
Анимация готова. Однако вы все еще не применили его к любому виджету, который рисуется на экране. Сейчас я предлагаю вам применить его к виджету « Positioned
содержащему виджет « Icon
материала». Для этого при создании виджета просто задайте для его свойства left
value
объекта Animation
.
Соответственно, добавьте следующий код, который переопределяет метод build()
, в состояние:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@override
Widget build(BuildContext context) {
return new Container(
color: Colors.white,
child: new Stack(
children: <Widget>[
new Positioned(
child: new Material(
child: new Icon(Icons.airport_shuttle,
textDirection: TextDirection.ltr,
size: 81.0
)
),
left: animation.value, // Animated value
top: 30.0 // Fixed value
)
],
textDirection: TextDirection.ltr,)
);
}
|
Обратите внимание, что в вышеприведенном дереве виджетов есть виджет Stack
поскольку Positioned
виджет всегда должен быть встроен в него.
Вы можете запустить свое приложение сейчас, чтобы увидеть анимацию.
3. Обработка событий состояния анимации
Если вы хотите получать уведомления о завершении анимации, вы можете прикрепить объект AnimationStatusListener
к вашему объекту Animation
. Внутри слушателя, если текущий статус анимации completed
или dismissed
, вы можете быть уверены, что анимация закончилась.
Анимации анимации во Флаттере обратимы. Вот почему есть две разные константы состояния, обозначающие конец анимации. Если текущий статус completed
, это означает, что анимация закончилась с конечным значением анимации. Если он dismissed
, это означает, что анимация закончилась с начальным значением. Используя два состояния и методы forward()
и reverse()
, вы можете легко создавать туда-сюда между анимациями.
Следующий код, который вы можете добавить в метод initState()
, показывает, как отменить и повторить анимацию, созданную на предыдущем шаге:
1
2
3
4
5
6
|
animation.addStatusListener((status) {
if(status == AnimationStatus.completed)
controller.reverse();
else if(status == AnimationStatus.dismissed)
controller.forward();
});
|
Если вы снова запустите приложение, анимация будет повторяться бесконечно.
4. Использование анимированных виджетов
Платформа Flutter предлагает несколько легко анимируемых виджетов, которые вы можете использовать, чтобы сделать анимационный код немного менее многословным и более пригодным для повторного использования. Все они являются подклассами класса AnimatedWidget
и ожидают объекты Animation
или AnimationController
в своих конструкторах.
Одним из наиболее часто используемых анимированных виджетов является виджет RotationTransition
. Это позволяет быстро применять анимацию вращения к своим дочерним элементам. Чтобы использовать его, сначала создайте новый контроллер анимации. Следующий код создает код, длительность которого равна шести секундам:
1
2
|
controller = new AnimationController(vsync: this,
duration: new Duration(seconds: 6));
|
Чтобы запустить анимацию на этот раз, вместо метода forward()
используйте метод repeat()
. Это гарантирует, что анимация повторяется бесконечно.
1
|
controller.repeat();
|
Для простоты вы можете использовать виджет Text
как дочерний виджет RotationTransition
. Поэтому создайте дерево виджетов соответственно. При создании виджета RotationTransition
убедитесь, что вы установили значение его свойства turns
для только что созданного объекта AnimationController
. При желании вы можете разместить оба виджета внутри виджета Center
. Вот как:
01
02
03
04
05
06
07
08
09
10
11
|
@override
Widget build(BuildContext context) {
return new Center(
child: new RotationTransition(
turns: controller,
child: new Text(«\u{1F43A}»,
textDirection: TextDirection.ltr,
style: new TextStyle(fontSize: 85.0),)
)
);
}
|
В приведенном выше коде я использовал кодировку эмодзи Unicode в качестве содержимого виджета « Text
. Это разрешено, потому что Flutter поддерживает эмодзи прямо из коробки.
При повторном запуске приложения вы должны увидеть что-то вроде этого на своем экране:
Очень похож на виджет RotationTransition
является виджет ScaleTransition
. Как вы уже догадались, он позволяет вам оживить масштаб своих детей. При его создании все, что вам нужно сделать, это передать объект AnimationController
в его свойство scale
. Следующий код показывает вам, как вы:
01
02
03
04
05
06
07
08
09
10
11
|
@override
Widget build(BuildContext context) {
return new Center(
child: new ScaleTransition(
scale: controller,
child: new Text(«\u{1F43A}»,
textDirection: TextDirection.ltr,
style: new TextStyle(fontSize: 85.0),)
)
);
}
|
Теперь вы сможете увидеть изменение масштаба Text
виджета во время анимации.
Если вам интересно, почему мы не создали никаких объектов Tween
для вышеуказанных анимаций, это потому, что по умолчанию класс AnimationController
использует 0.0 и 1.0 в качестве begin
и end
значений.
5. Использование кривых
Все анимации, которые мы создали на предыдущих этапах, следовали линейной кривой. В результате они выглядят не очень реалистично. Изменяя способ, которым объект Tween
генерирует промежуточные значения, вы можете изменить это.
Flutter имеет класс CurvedAnimation
, который позволяет вам применять нелинейные кривые к вашим подросткам. Когда вы используете его с классом Curves
, который предлагает различные кривые, такие как easeIn
и easeOut
, вы можете создавать анимации, которые кажутся более естественными.
Чтобы создать объект CurvedAnimation
, вам понадобится объект AnimationController
в качестве родителя. Вы можете использовать один из контроллеров, созданных на предыдущих этапах, или создать новый. Следующий код создает новый контроллер, длительность которого составляет пять секунд, и объект CurvedAnimation
, свойство curve
которого установлено в кривую bounceOut
:
1
2
3
4
5
6
7
8
9
|
controller = new AnimationController(
vsync: this,
duration: new Duration(seconds: 5)
);
CurvedAnimation curvedAnimation = new CurvedAnimation(
parent: controller,
curve: Curves.bounceOut
);
|
Теперь вы можете создать объект Tween
и применить к CurvedAnimation
объект CurvedAnimation
, вызвав его метод CurvedAnimation
animate()
. Как только анимация будет готова, не забудьте добавить слушателя к анимации, обновить состояние и затем вызвать метод forward()
чтобы запустить его.
1
2
3
4
5
6
7
8
9
|
Tween myTween = new Tween<double>(begin: 150.0, end: 450.0);
animation = myTween.animate(curvedAnimation);
animation.addListener(() {
setState(() {
});
});
controller.forward();
|
Чтобы увидеть анимацию, давайте применим ее к top
свойству виджета с Positioned
. Вы можете добавить любой дочерний виджет внутри него. В следующем коде я добавляю Text
виджет, отображающий другой эмодзи.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
@override
Widget build(BuildContext context) {
return new Stack(
children: [
new Positioned(
child: new Text(«\u{26BE}»,
textDirection: TextDirection.ltr,
style: new TextStyle(
fontSize: 70.0
)
),
left: 50.0,
top: animation.value // Animated property
)
],
textDirection: TextDirection.ltr,
);
}
|
После горячего перезапуска ваше приложение должно показать следующую анимацию:
Вывод
Теперь вы знакомы с основами создания анимации движения с использованием фреймворка Flutter. В этом уроке вы также узнали, как сделать их более естественными с помощью кривых. Поймите, что в медленном режиме, который является режимом по умолчанию во время разработки, анимация может показаться немного запаздывающей и неустойчивой. Только в режиме релиза вы можете увидеть их истинную производительность.
Чтобы узнать больше, обратитесь к официальной документации .