Flutter, Google’s UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, is known for its flexibility and powerful custom drawing capabilities. Creating animated custom paintings can significantly enhance the visual appeal and user experience of your apps. This article delves into how you can create animated custom paintings in Flutter with detailed examples.
What are Animated Custom Paintings in Flutter?
Animated custom paintings involve drawing on a Canvas using the CustomPaint widget in Flutter, with the drawings changing over time. These animations can range from simple transitions to complex, interactive visualizations, providing a unique and engaging UI.
Why Use Animated Custom Paintings?
- Unique UI/UX: Offers the ability to create unique user interfaces and experiences beyond standard widgets.
- Performance: Optimized drawing operations can provide better performance compared to complex widget compositions.
- Flexibility: Allows precise control over the rendering process, ideal for graphs, charts, and visual effects.
How to Create Animated Custom Paintings in Flutter
Creating animated custom paintings involves these primary steps:
Step 1: Setting Up a Custom Painter Class
You need to create a custom painter class that extends CustomPainter. This class is where you define how to draw on the canvas.
import 'package:flutter/material.dart';
import 'dart:math';
class AnimatedCirclePainter extends CustomPainter {
final Animation<double> animation;
AnimatedCirclePainter({required this.animation}) : super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
final radius = size.width / 3 * animation.value;
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
Explanation:
AnimatedCirclePainterextendsCustomPainterand takes anAnimation<double>as input.- The
paintmethod defines how to draw the circle, with the radius changing based on theanimation.value. super(repaint: animation)ensures that the painter repaints whenever the animation value changes.shouldRepaintis overridden to optimize the painting process. In this example, we returnfalsebecause the repaint is handled by the animation.
Step 2: Integrating the Custom Painter into a Widget
Integrate the custom painter using the CustomPaint widget in your Flutter app.
import 'package:flutter/material.dart';
class AnimatedCircle extends StatefulWidget {
@override
_AnimatedCircleState createState() => _AnimatedCircleState();
}
class _AnimatedCircleState extends State<AnimatedCircle> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: CustomPaint(
size: Size(200, 200),
painter: AnimatedCirclePainter(animation: _animation),
),
);
}
}
Explanation:
AnimatedCircleis a stateful widget that manages the animation controller.- An
AnimationControlleris initialized ininitStatewith a duration of 2 seconds. - The
Tweencreates an animation that ranges from 0.0 to 1.0. _controller.repeat(reverse: true)makes the animation loop back and forth.- The
CustomPaintwidget uses theAnimatedCirclePainterand passes in the_animation.
Step 3: Run the App
Add the AnimatedCircle widget to your main.dart file or any part of your application.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Animated Circle'),
),
body: Center(
child: AnimatedCircle(),
),
),
),
);
}
Now, when you run your Flutter app, you will see an animated circle that grows and shrinks.
Example: Animated Line Graph
Let’s create a more complex animated custom painting example: an animated line graph.
Step 1: Custom Painter Class
import 'package:flutter/material.dart';
class AnimatedLineGraphPainter extends CustomPainter {
final Animation<double> animation;
final List<double> dataPoints;
AnimatedLineGraphPainter({required this.animation, required this.dataPoints})
: super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
if (dataPoints.isEmpty) return;
final paint = Paint()
..color = Colors.green
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
final path = Path();
final double xIncrement = size.width / (dataPoints.length - 1);
final double maxDataPoint = dataPoints.reduce(max);
final double heightScale = size.height / maxDataPoint;
for (int i = 0; i < dataPoints.length; i++) {
final double x = i * xIncrement;
final double y = size.height - dataPoints[i] * heightScale;
if (i == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
// Animate the path
final animatedPath = createAnimatedPath(path, animation.value);
canvas.drawPath(animatedPath, paint);
}
Path createAnimatedPath(Path originalPath, double animationProgress) {
final animatedPath = Path();
final pathMetrics = originalPath.computeMetrics();
for (PathMetric pathMetric in pathMetrics) {
final pathLength = pathMetric.length;
final extractLength = pathLength * animationProgress;
if (extractLength == 0.0) return animatedPath;
final partialPath = pathMetric.extractPath(0.0, extractLength, startWithMoveTo: true);
animatedPath.addPath(partialPath, Offset.zero);
}
return animatedPath;
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
Explanation:
AnimatedLineGraphPainterextendsCustomPainterand requires anAnimation<double>and a list ofdataPoints.- The
paintmethod draws a line graph based on thedataPoints. - The method
createAnimatedPathuses path metrics to progressively reveal the path, creating an animation effect.
Step 2: Integrating the Custom Painter into a Widget
import 'package:flutter/material.dart';
import 'animated_line_graph_painter.dart';
class AnimatedLineGraph extends StatefulWidget {
final List<double> dataPoints;
AnimatedLineGraph({required this.dataPoints});
@override
_AnimatedLineGraphState createState() => _AnimatedLineGraphState();
}
class _AnimatedLineGraphState extends State<AnimatedLineGraph> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(300, 200),
painter: AnimatedLineGraphPainter(animation: _animation, dataPoints: widget.dataPoints),
);
}
}
Explanation:
AnimatedLineGraphis a stateful widget that takes a list ofdataPointsas input.- The
AnimationControlleris set up to animate from 0.0 to 1.0 over 3 seconds. - The
CustomPaintwidget renders theAnimatedLineGraphPainterwith the_animationanddataPoints.
Step 3: Run the App
import 'package:flutter/material.dart';
import 'animated_line_graph.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Animated Line Graph'),
),
body: Center(
child: AnimatedLineGraph(dataPoints: [10, 30, 20, 60, 40, 80, 50]),
),
),
),
);
}
With these steps, you’ll have an animated line graph that draws itself smoothly from start to end.
Tips for Optimization
- Use
shouldRepaint: Implement theshouldRepaintmethod to prevent unnecessary redraws. - Minimize Calculations: Perform calculations outside of the
paintmethod to improve performance. - Layered Canvases: Use multiple
CustomPaintwidgets layered on top of each other to isolate different parts of the drawing.
Conclusion
Animated custom paintings in Flutter provide an effective way to create visually appealing and highly customized user interfaces. By understanding the principles of CustomPainter, AnimationController, and how to efficiently draw on the Canvas, you can create unique and engaging animations that elevate the user experience of your Flutter applications.