← Назад к вопросам
Расскажи про самую сложную анимацию которую реализовывал в Flutter
1.3 Junior🔥 141 комментариев
#Flutter виджеты#Анимации
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Сложная анимация в Flutter: Интерактивный Graph Visualizer
Я реализовывал анимированную визуализацию графа зависимостей пакетов в Flutter приложении. Это была действительно сложная задача, требующая комбинации нескольких техник.
Особенности реализации
1. Многоуровневая анимация
- Анимировались позиции узлов (nodes) по Force-Directed Graph алгоритму
- Рёбра (edges) между узлами должны были плавно следовать за перемещением
- Появление/исчезновение элементов с fade-in/fade-out эффектами
- Масштабирование и поворот узлов при интерактивном взаимодействии
2. Интерактивность + анимация
class AnimatedGraphNode extends StatefulWidget {
final Offset position;
final String label;
final Duration animationDuration;
const AnimatedGraphNode({
required this.position,
required this.label,
this.animationDuration = const Duration(milliseconds: 500),
});
@override
State<AnimatedGraphNode> createState() => _AnimatedGraphNodeState();
}
class _AnimatedGraphNodeState extends State<AnimatedGraphNode>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _positionAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: widget.animationDuration);
_setupAnimation();
}
void _setupAnimation() {
_positionAnimation = Tween<Offset>(
begin: widget.position,
end: widget.position,
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
_controller.forward();
}
@override
void didUpdateWidget(AnimatedGraphNode oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.position != widget.position) {
_positionAnimation = Tween<Offset>(
begin: _positionAnimation.value,
end: widget.position,
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
_controller.forward(from: 0);
}
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _positionAnimation,
builder: (context, child) {
return Positioned(
left: _positionAnimation.value.dx,
top: _positionAnimation.value.dy,
child: child!,
);
},
child: GestureDetector(
onTap: () => _onNodeTap(),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue[400],
boxShadow: [BoxShadow(blurRadius: 8)],
),
child: Center(child: Text(widget.label)),
),
),
);
}
void _onNodeTap() {
// Обработка взаимодействия
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
3. Рисование рёбер графа
class GraphEdgePainter extends CustomPainter {
final List<Edge> edges;
final Map<String, Offset> nodePositions;
GraphEdgePainter(this.edges, this.nodePositions);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.grey[400]!
..strokeWidth = 2.0;
for (final edge in edges) {
final start = nodePositions[edge.from];
final end = nodePositions[edge.to];
if (start != null && end != null) {
canvas.drawLine(start, end, paint);
}
}
}
@override
bool shouldRepaint(GraphEdgePainter oldDelegate) {
return oldDelegate.nodePositions != nodePositions ||
oldDelegate.edges != edges;
}
}
4. Физическая симуляция
Использовал Simulation и SpringDescription для реалистичного движения узлов:
final spring = SpringDescription.withDampingRatio(
mass: 1.0,
stiffness: 100.0,
ratio: 0.7,
);
final simulation = SpringSimulation(spring, 0, 100, 0);
Вызовы и решения
Производительность: Граф содержал 100+ узлов, поэтому использовал:
RepaintBoundaryдля оптимизации перерисовки- Батчинг обновлений позиций узлов
CustomPainterвместо виджетов для рёбер
Синхронизация: Анимации узлов и рёбер должны быть синхронизированы:
- Единый
AnimationControllerдля координации Listenable.merge()для слушания нескольких анимаций
Интерактивность: Пользователь мог перетаскивать узлы:
- Останавливал текущую анимацию при gestures
- Плавно переходил к новой позиции с инерцией
Этот опыт научил меня глубоко понимать animation в Flutter и оптимизации производительности при работе с комплексными сценами.