Animation và Custom Painting Trong Flutter
 · 3 min read
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
