[Программирование, Разработка мобильных приложений, Flutter] Flutter: флип-анимация (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Перевод подготовлен в рамках курса "Flutter Mobile Developer".
Всех желающих приглашаем на второй день двухдневного онлайн-интенсива «Создаем приложение на Flutter для Web, iOS и Android». Продолжаем писать приложение (это будет приложение с работой по сети: загрузка списка сущностей + их фильтрация + сделаем блок настроек в приложении, где можно будет менять тему приложения (цвета)). Присоединяйтесь!
Когда я впервые увидел виджет AnimationSwitcher, то подумал, что смогу его перевернуть, открыв его обратную сторону.
Я ошибался: AnimationSwitcher позволяет... переключаться между различными виджетами с заданной вами анимацией (анимация по умолчанию - затухающий переход). Этот компонент слишком универсальный для этой цели.
Я должен был внимательно читать…Его использование является весьма общим, поэтому я покажу вам, как можно сделать такую анимацию.
Я обнаружил пакет flutter, который может делать такую флип-анимацию, под названием animated_card_switcher, но, похоже, он не поддерживается должным образом, и код слишком сложен.
Вот шаги по разработке:
- Создайте передний и задний виджеты
- Используйте виджет AnimationSwitcher для анимации
- Создайте свой собственный конструктор переходов для поворота вашей карточки.
- Добавить кривые
Создание переднего и заднего виджетовВ этом примере я возьму упрощенные версии переднего и заднего виджетов, потому что это не так существенно.Единственное, о чем следует помнить, это то, что вы должны установить ключ для виджетов верхнего уровня, чтобы AnimationSwitcher обнаружил, что виджет изменился (и, следовательно, выполнил анимацию).
Как кусок пирога!Вот пример макета виджетов, который я буду использовать:
Widget __buildLayout({Key key, String faceName, Color backgroundColor}) {
return Container(
key: key,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20.0),
color: backgroundColor,
),
child: Center(
child: Text(faceName.substring(0, 1), style: TextStyle(fontSize: 80.0)),
),
);
Итак, мои виджеты будут иметь вид спереди и сзади:
Widget _buildFront() {
return __buildLayout(
key: ValueKey(true),
backgroundColor: Colors.blue,
faceName: "F",
);
}
Widget _buildRear() {
return __buildLayout(
key: ValueKey(false),
backgroundColor: Colors.blue.shade700,
faceName: "R",
);
}
Использование виджета AnimationSwitcher для анимированияТеперь мы можем использовать виджет AnimationSwitcher для анимирования перехода между передним и задним сторонами.В StatefulWidget я переопределяю метод build, чтобы создать страницу, которая будет показывать анимацию в ее центре.
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _displayFront;
bool _flipXAxis;
@override
void initState() {
super.initState();
_displayFront = true;
_flipXAxis = true;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.widget.title),
centerTitle: true,
),
body: Center(
child: Container(
constraints: BoxConstraints.tight(Size.square(200.0)),
child: _buildFlipAnimation(),
),
);
}
}
Я отделил анимацию от страницы в методе _build Flip Animation, чтобы сделать код более понятным.Вот первая версия этого метода:
Widget _buildFlipAnimation() {
return GestureDetector(
onTap: () => setState(() =>_showFrontSide = !_showFrontSide),
child: AnimatedSwitcher(
duration: Duration(milliseconds: 600),
child: _showFrontSide ? _buildFront() : _buildRear(),
),
);
}
Когда мы кликаем на виджете, мы видим, что передняя часть виджета исчезает, открывая заднюю сторону. При повторном клике задний вид исчезает, открывая переднюю сторону.
По крайней мере, что-то происходит.Мы хотим повернуть виджеты по оси Y. Надеюсь, AnimationSwitcher позволит нам переопределить переход, благодаря входному transitionBuilder.Закодируйте свой пользовательский конструктор переходов для вращения вашей карточкиИтак, вот план: мы хотим получить поворот на 180° (pi). Мы обернем наши виджеты в AnimatedBuidler и воспользуемся виджетом Transform для применения вращения.
Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation);
return AnimatedBuilder(
animation: rotateAnim,
child: widget,
builder: (context, widget) {
return Transform(
transform: Matrix4.rotationY(value),
child: widget,
alignment: Alignment.center,
);
},
);
}
Это хороший старт, но не совсем то, что необходимо. Мы видим, что при анимации перехода задний виджет находится сверху от начала до конца.
Финальная часть появляется слишком быстро.Нам нужно, чтобы передний виджет постепенно заменялся задним.Поэтому необходимо изменить две вещи:
- Порядок отображения должен быть обратным: заменяемый виджет должен быть на вершине стека.
- В середине анимации заменяемый виджет должен исчезнуть.
Для этого мы изменим вход layoutBuilder нашего экземпляра AnimationSwitcher.
layoutBuilder: (widget, list) => Stack(children: [widget, ...list]),
Затем, в середине анимации, в результате поворота на pi/2 ширина виджета станет равной 0.0. Поэтому мы заблокируем это вращение (только) для анимации предыдущего виджета.
Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation);
return AnimatedBuilder(
animation: rotateAnim,
child: widget,
builder: (context, widget) {
final isUnder = (ValueKey(_showFrontSide) != widget.key);
final value = isUnder ? min(rotateAnim.value, pi / 2) : rotateAnim.value;
return Transform(
transform: Matrix4.rotationY(value),
child: widget,
alignment: Alignment.center,
);
},
);
}
Уже лучше, но мы еще не закончили! Чтобы усилить ощущение того, что виджет вращается, мы добавим небольшой "tilt" (наклон) на виджеты.
Нам нужна глубина... Милая маленькая пушистая глубина.Это значение "наклона" должно быть равно 0,0 в начале и в конце анимации. Кроме того, поскольку мы будем применять анимацию для каждой стороны нашего виджета, то их наклон должен быть противоположным. Например, если наклон переднего виджета равен 0,2, то наклон заднего должен быть -0,2.Чтобы применить наклон к виджету, мы вручную задаем одно конкретное значение объекту Matrix4, определяющему поворот.
Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation);
return AnimatedBuilder(
animation: rotateAnim,
child: widget,
builder: (context, widget) {
final isUnder = (ValueKey(_showFrontSide) != widget.key);
var tilt = ((animation.value - 0.5).abs() - 0.5) * 0.003;
tilt *= isUnder ? -1.0 : 1.0;
final value = isUnder ? min(rotateAnim.value, pi / 2) : rotateAnim.value;
return Transform(
transform: Matrix4.rotationY(value)..setEntry(3, 0, tilt),
child: widget,
alignment: Alignment.center,
);
},
);
}
Более подробную информацию о Matrix4 можно получить здесь: https://medium.com/flutter-community/advanced-flutter-matrix4-and-perspective-transformations-a79404a0d828.Добавить кривыеНаконец, чтобы добавить немного энергии и динамики в анимацию, вы можете изменить параметры кривых входа AnimationSwitcher.
С кривыми всегда лучше!Вот моя первая попытка:
Widget _buildFlipAnimation() {
return GestureDetector(
onTap: _switchCard,
child: AnimatedSwitcher(
duration: Duration(milliseconds: 4600),
transitionBuilder: __transitionBuilder,
layoutBuilder: (widget, list) => Stack(children: [widget, ...list]),
child: _showFrontSide ? _buildFront() : _buildRear(),
switchInCurve: Curves.easeInBack,
switchOutCurve: Curves.easeOutBack,
),
);
}
Я должен показать вам в замедленном режиме в чем проблема.
О Нееееееет….Поскольку кривые не полностью идентичны, и предыдущая анимация виджета воспроизводится в обратном порядке, вы получите артефакт, при котором два виджета не накладываются как следует один на другой. Между ними будет небольшой сдвиг.
Анимация в режиме нескольких кадров…Чтобы избежать этого, мы должны использовать свойство flipped для кривой, которую мы хотим использовать.
switchInCurve: Curves.easeInBack,
switchOutCurve: Curves.easeInBack.flipped,
Sloooow Mooooo (теперь идеально)ЗаключениеКак видите, ничего особенного: я сделал эту анимацию примерно из 30 строк кода (только анимация), используя только один атрибут (отображаемая сторона виджета).Я не думаю, что имеет смысл создавать пакет для этого. Добавление зависимости в ваш код означает, что если он не будет работать при обновлении версии (например), ваш проект застопорится на определенное время. Хуже того, если зависимость больше не поддерживается, вы не сможете гарантировать, что ваш проект будет правильно компилироваться в следующие 6 месяцев, 1 или 2 года...
Копируйте и вставляйте этот пример с умом :)Так что не стесняйтесь использовать мой код. Я не поклонник копипаста, поэтому точнее выразиться (не стесняйтесь использовать код) “соответствующий моему коду”, то есть скопируйте-вставьте его, поймите и измените в соответствии с вашими потребностями!Демонстрацию, использованную в этой статье, вы найдете здесь.Следите за сообществом Flutter в Twitter
Узнать подробнее о курсе "Flutter Mobile Developer" Участвовать в двухдневном онлайн-интенсиве «Создаем приложение на Flutter для Web, iOS и Android»
===========
Источник:
habr.com
===========
===========
Автор оригинала: David Gonzalez
===========Похожие новости:
- [Big Data, Data Engineering] Курсы валют и аналитика – использование обменных курсов в Хранилище Данных
- [Программирование, Анализ и проектирование систем, Аналитика мобильных приложений] Какие ошибки совершает аналитик в первые полгода работы и как их избежать
- [Разработка мобильных приложений, Искусственный интеллект, Здоровье, IT-компании] «Сбер» запустил мобильное приложение AI Resp, диагностирующее признаки COVID-19 по дыханию и кашлю
- [Программирование, Машинное обучение, Научно-популярное, Искусственный интеллект] Дождались: IBM научит ИИ писать код и создает CodeNet (перевод)
- [Машинное обучение] Почему машинному обучению с трудом дается причинно-следственная связь? (перевод)
- [Управление разработкой, Agile] Почему использовать Agile не достаточно (перевод)
- [Программирование, Управление проектами] Неожиданная сложность простых программ
- [Программирование, Разработка мобильных приложений, Конференции, Flutter] Анонс эфира Flutter live-coding сессии
- [NoSQL, MongoDB] Иерархия потребностей по Маслоу при разработке документации (перевод)
- [Программирование, Разработка робототехники, Развитие стартапа, Робототехника] Анализ рынка детского технического образования в России / Планы на будущее
Теги для поиска: #_programmirovanie (Программирование), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_flutter, #_flutter, #_mobilnaja_razrabotka (мобильная разработка), #_animatsija (анимация), #_android, #_ios, #_blog_kompanii_otus (
Блог компании OTUS
), #_programmirovanie (
Программирование
), #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_flutter
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:31
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Перевод подготовлен в рамках курса "Flutter Mobile Developer".
Всех желающих приглашаем на второй день двухдневного онлайн-интенсива «Создаем приложение на Flutter для Web, iOS и Android». Продолжаем писать приложение (это будет приложение с работой по сети: загрузка списка сущностей + их фильтрация + сделаем блок настроек в приложении, где можно будет менять тему приложения (цвета)). Присоединяйтесь! Когда я впервые увидел виджет AnimationSwitcher, то подумал, что смогу его перевернуть, открыв его обратную сторону. Я ошибался: AnimationSwitcher позволяет... переключаться между различными виджетами с заданной вами анимацией (анимация по умолчанию - затухающий переход). Этот компонент слишком универсальный для этой цели. Я должен был внимательно читать…Его использование является весьма общим, поэтому я покажу вам, как можно сделать такую анимацию. Я обнаружил пакет flutter, который может делать такую флип-анимацию, под названием animated_card_switcher, но, похоже, он не поддерживается должным образом, и код слишком сложен.
Как кусок пирога!Вот пример макета виджетов, который я буду использовать: Widget __buildLayout({Key key, String faceName, Color backgroundColor}) {
return Container( key: key, decoration: BoxDecoration( shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(20.0), color: backgroundColor, ), child: Center( child: Text(faceName.substring(0, 1), style: TextStyle(fontSize: 80.0)), ), ); Widget _buildFront() {
return __buildLayout( key: ValueKey(true), backgroundColor: Colors.blue, faceName: "F", ); } Widget _buildRear() { return __buildLayout( key: ValueKey(false), backgroundColor: Colors.blue.shade700, faceName: "R", ); } class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool _displayFront; bool _flipXAxis; @override void initState() { super.initState(); _displayFront = true; _flipXAxis = true; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(this.widget.title), centerTitle: true, ), body: Center( child: Container( constraints: BoxConstraints.tight(Size.square(200.0)), child: _buildFlipAnimation(), ), ); } } Widget _buildFlipAnimation() {
return GestureDetector( onTap: () => setState(() =>_showFrontSide = !_showFrontSide), child: AnimatedSwitcher( duration: Duration(milliseconds: 600), child: _showFrontSide ? _buildFront() : _buildRear(), ), ); } По крайней мере, что-то происходит.Мы хотим повернуть виджеты по оси Y. Надеюсь, AnimationSwitcher позволит нам переопределить переход, благодаря входному transitionBuilder.Закодируйте свой пользовательский конструктор переходов для вращения вашей карточкиИтак, вот план: мы хотим получить поворот на 180° (pi). Мы обернем наши виджеты в AnimatedBuidler и воспользуемся виджетом Transform для применения вращения. Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation); return AnimatedBuilder( animation: rotateAnim, child: widget, builder: (context, widget) { return Transform( transform: Matrix4.rotationY(value), child: widget, alignment: Alignment.center, ); }, ); } Это хороший старт, но не совсем то, что необходимо. Мы видим, что при анимации перехода задний виджет находится сверху от начала до конца. Финальная часть появляется слишком быстро.Нам нужно, чтобы передний виджет постепенно заменялся задним.Поэтому необходимо изменить две вещи:
layoutBuilder: (widget, list) => Stack(children: [widget, ...list]),
Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation); return AnimatedBuilder( animation: rotateAnim, child: widget, builder: (context, widget) { final isUnder = (ValueKey(_showFrontSide) != widget.key); final value = isUnder ? min(rotateAnim.value, pi / 2) : rotateAnim.value; return Transform( transform: Matrix4.rotationY(value), child: widget, alignment: Alignment.center, ); }, ); } Уже лучше, но мы еще не закончили! Чтобы усилить ощущение того, что виджет вращается, мы добавим небольшой "tilt" (наклон) на виджеты. Нам нужна глубина... Милая маленькая пушистая глубина.Это значение "наклона" должно быть равно 0,0 в начале и в конце анимации. Кроме того, поскольку мы будем применять анимацию для каждой стороны нашего виджета, то их наклон должен быть противоположным. Например, если наклон переднего виджета равен 0,2, то наклон заднего должен быть -0,2.Чтобы применить наклон к виджету, мы вручную задаем одно конкретное значение объекту Matrix4, определяющему поворот. Widget __transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnim = Tween(begin: pi, end: 0.0).animate(animation); return AnimatedBuilder( animation: rotateAnim, child: widget, builder: (context, widget) { final isUnder = (ValueKey(_showFrontSide) != widget.key); var tilt = ((animation.value - 0.5).abs() - 0.5) * 0.003; tilt *= isUnder ? -1.0 : 1.0; final value = isUnder ? min(rotateAnim.value, pi / 2) : rotateAnim.value; return Transform( transform: Matrix4.rotationY(value)..setEntry(3, 0, tilt), child: widget, alignment: Alignment.center, ); }, ); } С кривыми всегда лучше!Вот моя первая попытка: Widget _buildFlipAnimation() {
return GestureDetector( onTap: _switchCard, child: AnimatedSwitcher( duration: Duration(milliseconds: 4600), transitionBuilder: __transitionBuilder, layoutBuilder: (widget, list) => Stack(children: [widget, ...list]), child: _showFrontSide ? _buildFront() : _buildRear(), switchInCurve: Curves.easeInBack, switchOutCurve: Curves.easeOutBack, ), ); } О Нееееееет….Поскольку кривые не полностью идентичны, и предыдущая анимация виджета воспроизводится в обратном порядке, вы получите артефакт, при котором два виджета не накладываются как следует один на другой. Между ними будет небольшой сдвиг. Анимация в режиме нескольких кадров…Чтобы избежать этого, мы должны использовать свойство flipped для кривой, которую мы хотим использовать. switchInCurve: Curves.easeInBack,
switchOutCurve: Curves.easeInBack.flipped, Sloooow Mooooo (теперь идеально)ЗаключениеКак видите, ничего особенного: я сделал эту анимацию примерно из 30 строк кода (только анимация), используя только один атрибут (отображаемая сторона виджета).Я не думаю, что имеет смысл создавать пакет для этого. Добавление зависимости в ваш код означает, что если он не будет работать при обновлении версии (например), ваш проект застопорится на определенное время. Хуже того, если зависимость больше не поддерживается, вы не сможете гарантировать, что ваш проект будет правильно компилироваться в следующие 6 месяцев, 1 или 2 года... Копируйте и вставляйте этот пример с умом :)Так что не стесняйтесь использовать мой код. Я не поклонник копипаста, поэтому точнее выразиться (не стесняйтесь использовать код) “соответствующий моему коду”, то есть скопируйте-вставьте его, поймите и измените в соответствии с вашими потребностями!Демонстрацию, использованную в этой статье, вы найдете здесь.Следите за сообществом Flutter в Twitter Узнать подробнее о курсе "Flutter Mobile Developer" Участвовать в двухдневном онлайн-интенсиве «Создаем приложение на Flutter для Web, iOS и Android»
=========== Источник: habr.com =========== =========== Автор оригинала: David Gonzalez ===========Похожие новости:
Блог компании OTUS ), #_programmirovanie ( Программирование ), #_razrabotka_mobilnyh_prilozhenij ( Разработка мобильных приложений ), #_flutter |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 19:31
Часовой пояс: UTC + 5