Trang chủ > Bài viết > Animation và Custom Painting Trong Flutter

Animation và Custom Painting Trong Flutter

Lập trình
5 phút đọc
Animation và Custom Painting Trong Flutter
Tác giả

Tác giả

Chuyên gia lập trình

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

Tài Liệu Tham Khảo