Animation và Custom Painting Trong Flutter
Animation Cơ bản
AnimationController
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Tween Animation
final animation = Tween<double>(
begin: 0,
end: 300,
).animate(_controller);
Các Loại Animation
Implicit Animations
AnimatedContainer(
duration: Duration(milliseconds: 500),
width: _isExpanded ? 300.0 : 100.0,
height: _isExpanded ? 300.0 : 100.0,
color: _isExpanded ? Colors.blue : Colors.red,
curve: Curves.fastOutSlowIn,
)
Hero Animation
Hero(
tag: 'imageHero',
child: Image.network('url_to_image'),
)
Staggered Animations
class StaggeredAnimation extends StatelessWidget {
final Animation<double> controller;
late final Animation<double> opacity;
late final Animation<double> width;
late final Animation<double> height;
StaggeredAnimation({required this.controller}) {
opacity = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.100, curve: Curves.ease),
),
);
}
}
Custom Painting
CustomPaint và CustomPainter
class MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 4
..style = PaintingStyle.stroke;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
100,
paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Vẽ Đường Cong
void drawCurve(Canvas canvas, Size size) {
var path = Path();
path.moveTo(0, size.height / 2);
path.quadraticBezierTo(
size.width / 2,
0,
size.width,
size.height / 2,
);
canvas.drawPath(path, paint);
}
Hiệu Ứng Nâng Cao
Particle System
class Particle {
Offset position;
double speed;
double theta;
Color color;
void update() {
final dx = speed * cos(theta);
final dy = speed * sin(theta);
position += Offset(dx, dy);
}
void draw(Canvas canvas) {
final paint = Paint()..color = color;
canvas.drawCircle(position, 2, paint);
}
}
Shader Effects
final shader = LinearGradient(
colors: [Colors.blue, Colors.red],
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final paint = Paint()..shader = shader;
Performance Optimization
Repaint Boundary
RepaintBoundary(
child: CustomPaint(
painter: MyPainter(),
),
)
Caching Complex Paintings
class CachedPainter extends CustomPainter {
ui.Picture? _cachedPicture;
void _createCachedPicture(Size size) {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
// Draw complex stuff
_cachedPicture = recorder.endRecording();
}
}
Best Practices
Animation
- Sử dụng
vsync
để tránh memory leak - Dispose AnimationController khi widget bị dispose
- Sử dụng Implicit Animation khi có thể
- Tránh animation quá phức tạp trên mobile
Custom Painting
- Sử dụng RepaintBoundary để tối ưu hiệu năng
- Cache các painting phức tạp
- Tránh vẽ lại không cần thiết