Created
February 21, 2022 07:35
-
-
Save Barttje/9f887534d91d1bf29cb724ec62391412 to your computer and use it in GitHub Desktop.
Example of animation with different beginning and ending
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:collection/collection.dart'; | |
import 'package:flutter/rendering.dart'; | |
void main() { | |
runApp(const ExampleApp()); | |
} | |
class ExampleApp extends StatelessWidget { | |
const ExampleApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData( | |
primarySwatch: Colors.green, | |
), | |
home: Scaffold( | |
appBar: AppBar( | |
title: const Text('Animation CustomPainter'), | |
), | |
body: const Center(child: ExampleWidget())), | |
); | |
} | |
} | |
class ExampleWidget extends StatefulWidget { | |
const ExampleWidget({Key? key}) : super(key: key); | |
@override | |
_ExampleWidgetState createState() => _ExampleWidgetState(); | |
} | |
class _ExampleWidgetState extends State<ExampleWidget> with SingleTickerProviderStateMixin { | |
late Animation<double> _animation; | |
late Tween<double> _tween; | |
late AnimationController _animationController; | |
final List<CircleModel> _circles = [ | |
CircleModel(const Offset(50, 100), Colors.red), | |
CircleModel(const Offset(150, 100), Colors.blue), | |
CircleModel(const Offset(250, 100), Colors.green) | |
]; | |
@override | |
void initState() { | |
super.initState(); | |
_animationController = AnimationController(duration: const Duration(milliseconds: 500), vsync: this); | |
_tween = Tween(begin: 50, end: 50); | |
_animation = _tween.animate(_animationController) | |
..addListener(() { | |
setState(() {}); | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
List<Widget> widgets = []; | |
widgets.addAll(_circles.map((e) => CircleWrapper( | |
center: e.center, | |
color: e.color, | |
csKey: e.key, | |
))); | |
widgets.add(CustomPaint( | |
painter: LinePainter(Offset(_animation.value, 150)), | |
)); | |
return Listener( | |
onPointerDown: (PointerEvent details) { | |
handleClick(details); | |
}, | |
child: Container( | |
color: Colors.grey[50], | |
width: 300, | |
height: 250, | |
child: Stack( | |
children: widgets, | |
), | |
), | |
); | |
} | |
handleClick(PointerEvent details) { | |
final circle = _circles.firstWhereOrNull((element) => isClicked(details, element.key)); | |
if (circle != null) { | |
_tween.begin = _tween.end; | |
_animationController.reset(); | |
_tween.end = circle.center.dx; | |
_animationController.forward(); | |
} | |
} | |
bool isClicked(final PointerEvent details, final GlobalKey key) { | |
final result = BoxHitTestResult(); | |
final RenderObject? circleBox = key.currentContext?.findRenderObject(); | |
if (circleBox != null && circleBox is RenderBox) { | |
Offset localClick = circleBox.globalToLocal(details.position); | |
if (circleBox.hitTest(result, position: localClick)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
} | |
class CircleWrapper extends StatelessWidget { | |
final Offset center; | |
final Color color; | |
final GlobalKey csKey; | |
const CircleWrapper({Key? key, required this.center, required this.color, required this.csKey}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return CustomPaint( | |
key: csKey, | |
painter: CirclePainter(center, color), | |
child: Container(), | |
); | |
} | |
} | |
class LinePainter extends CustomPainter { | |
final Offset center; | |
LinePainter(this.center); | |
@override | |
void paint(Canvas canvas, Size size) { | |
canvas.drawLine( | |
Offset(center.dx - 40, center.dy), Offset(center.dx + 40, center.dy), createPaintForColor(Colors.grey)); | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) => true; | |
} | |
class CirclePainter extends CustomPainter { | |
final Offset center; | |
final Color color; | |
CirclePainter(this.center, this.color); | |
@override | |
void paint(Canvas canvas, Size size) { | |
canvas.drawCircle(center, 30, createPaintForColor(color)); | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) => false; | |
@override | |
bool hitTest(Offset position) { | |
final Path path = Path(); | |
path.addRect(Rect.fromCircle(center: center, radius: 30)); | |
return path.contains(position); | |
} | |
} | |
Paint createPaintForColor(Color color) { | |
return Paint() | |
..color = color | |
..strokeCap = StrokeCap.round | |
..style = PaintingStyle.stroke | |
..strokeWidth = 15; | |
} | |
class CircleModel { | |
final Offset center; | |
final Color color; | |
final GlobalKey key = GlobalKey(); | |
CircleModel(this.center, this.color); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment