Во
Flutter анимации - это красиво, но если ими не управлять, они могут съедать ресурсы даже тогда, когда их никто не видит. Сегодня разберем два инструмента, которые помогут анимациям не тормозить интерфейс и не работать впустую.
AnimatedBuilder - перерисовываем только то, что нужно:
По умолчанию при каждом кадре анимации перестраивается весь виджет, который содержит
AnimationController. Если внутри есть тяжелые элементы, это бьет по производительности.
AnimatedBuilder решает эту проблему. Он разделяет анимируемую часть и статический контент. Статический контент передается через параметр
child и не пересоздается при каждом кадре. Перерисовывается только то, что реально меняется.
class ScaleButton extends StatefulWidget {
@override
_ScaleButtonState createState() => _ScaleButtonState();
}
class _ScaleButtonState extends State<ScaleButton> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_animation = Tween<double>(begin: 1.0, end: 1.5).animate(_controller);
}
void _onTapDown(TapDownDetails details) {
_controller.forward();
}
void _onTapUp(TapUpDetails details) {
_controller.reverse();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
child: AnimatedBuilder(
animation: _animation,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Text(
'Tap me',
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
),
builder: (context, child) {
return Transform.scale(
scale: _animation.value,
child: child,
);
},
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
В этом примере статичная кнопка (синий квадрат с текстом) не пересоздается при каждом кадре - пересоздается только анимированное масштабирование вокруг нее.
TickerMode - останавливаем анимацию, когда ее не видно:
Если пользователь свернул приложение или переключился на другой экран, а анимация продолжает работать, это пустая трата ресурсов процессора и батареи.
TickerMode позволяет отключать все анимации в определенной части дерева виджетов. Достаточно обернуть нужный участок и передать
enabled: false. Все
AnimationController, связанные с
TickerProviderStateMixin, автоматически приостановятся.
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
bool _areAnimationsEnabled = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_areAnimationsEnabled = state != AppLifecycleState.paused;
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Анимации')),
body: TickerMode(
enabled: _areAnimationsEnabled,
child: Center(
child: MyAnimatedWidget(),
),
),
);
}
}
Когда приложение уходит в фон,
_areAnimationsEnabled становится
false, и все анимации внутри
TickerMode автоматически приостанавливаются. При возвращении в приложение анимации возобновляются сами.
Вывод:
AnimatedBuilder помогает не перерисовывать статику при каждом кадре, а
TickerMode - не тратить ресурсы на анимацию, которую никто не видит. Вместе они делают приложение плавнее и экономичнее. Если вы используете анимации в своих проектах, эти два виджета должны быть в вашем арсенале.