Привет! Меня зовут Айдар Мавлетбаев, я Flutter-разработчик в AGIMA. Современное приложение почти невозможно представить без анимации. И один из ее типов — Transition Animation, или анимация переходов. Ее используют, чтобы переход с одной страницы на другую был плавным и красивым. В этой статье разберем две библиотеки от Flutter: go_router и animation.
Навигация + анимация
Существует много типов анимации, которые применяют при разработке Flutter-приложений. Среди них — Rive-анимация, Hero animations, Progressindicator и т. д. С помощью всех этих типов можно создавать кастомную анимацию для любого вида работ с системой — нажатие кнопки, индикатор загрузки и другие анимации.
В этой статье я расскажу про возможности Animations. Это наиболее базовое решение, которое предоставляет стандартные анимации Material Design. Система движений Material Design состоит из четырех паттернов для перехода между компонентами. Эти паттерны призваны помочь пользователям ориентироваться и понимать приложение, усиливая взаимосвязи между элементами интерфейса.
Но перед этим напомню, что такое навигация. Это основная часть всех Flutter-приложений. Главная их задача — давать пользователю возможность перемещаться между страницами. Практически все популярные библиотеки для навигации, такие как auto_router, go_router, navigator и т. д., имеют возможность анимировать переходы между экранами.
В нашем случае будем рассматривать go_router, которую мы применяем чаще всего. Go_router будет использоваться в двух шаблонах — Shared Axis и Fade Through. В остальных шаблонах можно не использовать навигацию как обязательное требование (Container Transform, Fade).
К стандартным шаблонам Animations относятся:
Container transform;
Shared axis;
Fade through;
Fade.
Разберем каждый по отдельности.
Container transform
Шаблон преобразования контейнера предназначен для переходов между элементами пользовательского интерфейса, включающими контейнер. Этот паттерн создает видимую связь между двумя элементами пользовательского интерфейса.
Контейнер действует как постоянный элемент, а его размеры, положение и форма изменяются в процессе перехода. Содержимое контейнера прикрепляется к его верхнему краю и при трансформации масштабируется в соответствии с его шириной. Кроме того, для чередования выходящих и входящих элементов используется затухание.
Пример:
OpenContainer(
transitionType: ContainerTransitionType.fade,
openBuilder: (context, openContainer) => SecondarySecondPage(text: 'List Menu Item ${index + 1}'),
closedBuilder: (context, openContainer) {
return Container(
color: Colors.lightGreen,
child: ListTile(
leading: const Icon(Icons.person), onTap: openContainer, title: Text('List Menu Item ${index + 1}')),
);
},
),
OpenContainer принимает 3 важных параметра:
transitionType — задаем анимацию перехода.
openBuilder — состояние открытого контейнера.
closedBuilder — состояние закрытого контейнера.
Shared Axis
Паттерн Shared Axis используется для переходов между элементами пользовательского интерфейса, имеющими пространственную или навигационную связь. Этот паттерн использует общую трансформацию по осям X, Y или Z для усиления взаимосвязи между элементами.
Пример:
class SharedAxisTransitionPage extends CustomTransitionPage {
SharedAxisTransitionPage({
required super.child,
super.name,
super.arguments,
super.restorationId,
super.key,
SharedAxisTransitionType transitionType =
SharedAxisTransitionType.horizontal,
}) : super(
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
SharedAxisTransition(
transitionType: transitionType,
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child));
}
Создаем кастомный класс, который наследуется от CustomTransitionPage. Там мы вызываем наш суперметод transitionsBuilder и в него интегрируем наш SharedAxisTransition, который уже и отвечает за саму анимацию.
GoRoute(
path: 'secondary_third_page',
name: 'secondary_third_page',
pageBuilder: (context, state) => SharedAxisTransitionWrapper(child: const SecondaryThirdPage()))
Теперь создаем маршрут, в котором вместо обычного builder используем pageBuilder, потому что мы возвращаем не Widget, а именно Page.
Fade Through
Паттерн Fade Through используется для переходов между элементами пользовательского интерфейса, не имеющими тесной связи друг с другом.
Реализация схожа с реализацией Shared Axis. Единственное отличие в том, что мы используем FadeThroughTransition.
Пример:
class FadeThroughTransitionWrapper extends CustomTransitionPage {
FadeThroughTransitionWrapper({required super.child})
: super(
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) =>
FadeThroughTransition(animation: animation, secondaryAnimation: secondaryAnimation, child: child));
}
GoRoute(
path: 'secondary_first_page',
name: 'secondary_first_page',
pageBuilder: (context, state) {
final extra = state.extra as Map<String, Object>;
return FadeThroughTransitionWrapper(child: SecondaryFirstPage(text: extra['text'] as String));
})
Fade
Шаблон Fade используется для элементов пользовательского интерфейса, которые входят или выходят за границы экрана. Например, диалог, который появляется и исчезает из центра экрана.
Пример:
ElevatedButton(
onPressed: () {
showModal(
configuration: const FadeScaleTransitionConfiguration(),
context: context,
builder: (_) => const AlertDialog(
title: Text('New dialog'),
),
);
},
child: const Text('Show dialog')),
В данном примере, мы используем один единственный класс — FadeScaleTransitionConfiguration, который добавляет плавную анимацию открытия окна.
Заключение
Мы рассмотрели все четыре шаблона Animations. Посмотреть, как работают сами анимации, можно в моем открытом репозитории. Если у вас возникли вопросы, готов ответить в комментариях под текстом.
А еще у моего коллеги Саши Ворожищева есть отличный телеграм-канал про Flutter. Заходите почитать!