79184665

Date: 2024-11-13 11:28:40
Score: 1.5
Natty:
Report link

Here is one example which i tried and successfully run in my android studio

1. Main Timer Widget

import 'dart:math';
import 'package:flutter/material.dart';

class CircularTimerWithTaxi extends StatefulWidget {
  final int durationInSeconds;
  final VoidCallback? onTimerComplete;
  final double size;
  final Color trackColor;
  final Color progressColor;

  const CircularTimerWithTaxi({
    Key? key,
    required this.durationInSeconds,
    this.onTimerComplete,
    this.size = 300,
    this.trackColor = const Color(0xFFE0E0E0),
    this.progressColor = const Color(0xFF1A237E),
  }) : super(key: key);

  @override
  State<CircularTimerWithTaxi> createState() => _CircularTimerWithTaxiState();
}

class _CircularTimerWithTaxiState extends State<CircularTimerWithTaxi>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _setupAnimation();
  }

  void _setupAnimation() {
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: widget.durationInSeconds),
    );

    _animation = Tween<double>(
      begin: 0.0,
      end: 2 * pi,
    ).animate(_controller);

    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        widget.onTimerComplete?.call();
      }
    });

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return Stack(
          alignment: Alignment.center,
          children: [
            // Track and Progress
            CustomPaint(
              size: Size(widget.size, widget.size),
              painter: TrackPainter(
                progress: _controller.value,
                trackColor: widget.trackColor,
                progressColor: widget.progressColor,
              ),
            ),
            
            // Moving Car
            Transform(
              alignment: Alignment.center,
              transform: Matrix4.identity()
                ..translate(
                  (widget.size / 2) * cos(_animation.value - pi / 2),
                  (widget.size / 2) * sin(_animation.value - pi / 2),
                )
                ..rotateZ(_animation.value),
              child: const Icon(
                Icons.local_taxi,
                color: Colors.amber,
                size: 30,
              ),
            ),
            
            // Timer Text
            Text(
              '${((1 - _controller.value) * widget.durationInSeconds).ceil()}s',
              style: const TextStyle(
                fontSize: 40,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        );
      },
    );
  }
}

2. Custom Track Painter

class TrackPainter extends CustomPainter {
  final double progress;
  final Color trackColor;
  final Color progressColor;

  TrackPainter({
    required this.progress,
    required this.trackColor,
    required this.progressColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2;
    const strokeWidth = 20.0;

    // Draw base track
    final trackPaint = Paint()
      ..color = trackColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;

    canvas.drawCircle(center, radius - (strokeWidth / 2), trackPaint);

    // Draw progress
    final progressPaint = Paint()
      ..color = progressColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round;

    canvas.drawArc(
      Rect.fromCircle(
        center: center,
        radius: radius - (strokeWidth / 2),
      ),
      -pi / 2,
      2 * pi * (1 - progress),
      false,
      progressPaint,
    );

    // Draw track markers
    final markerPaint = Paint()
      ..color = Colors.white
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    const markersCount = 40;
    for (var i = 0; i < markersCount; i++) {
      final angle = (2 * pi * i) / markersCount;
      final start = Offset(
        center.dx + (radius - strokeWidth) * cos(angle),
        center.dy + (radius - strokeWidth) * sin(angle),
      );
      final end = Offset(
        center.dx + radius * cos(angle),
        center.dy + radius * sin(angle),
      );
      canvas.drawLine(start, end, markerPaint);
    }
  }

  @override
  bool shouldRepaint(TrackPainter oldDelegate) {
    return oldDelegate.progress != progress ||
        oldDelegate.trackColor != trackColor ||
        oldDelegate.progressColor != progressColor;
  }
}

3. Example Usage

class TimerScreen extends StatelessWidget {
  const TimerScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CircularTimerWithTaxi(
          durationInSeconds: 30,
          size: 300,
          trackColor: Colors.grey[300]!,
          progressColor: Colors.blue[900]!,
          onTimerComplete: () {
            // Handle timer completion
            print('Timer finished!');
          },
        ),
      ),
    );
  }
}

Key Features

  1. Smooth Car Animation

    • Car rotates realistically while moving
    • Follows the circular path precisely
    • Maintains correct orientation throughout the animation
  2. Custom Track Design

    • Dashed line markers for visual interest
    • Configurable track and progress colors
    • Smooth progress indication
    • Round stroke caps for polished look
  3. Timer Functionality

    • Countdown display in the center
    • Customizable duration
    • Completion callback
    • Visual progress tracking

Customization Options

You can customize various aspects of the timer:

CircularTimerWithTaxi(
  durationInSeconds: 60,              // Duration in seconds
  size: 400,                          // Overall size
  trackColor: Colors.grey[200]!,      // Background track color
  progressColor: Colors.blue[800]!,   // Progress track color
  onTimerComplete: () {
    // Custom completion handling
  },
)

Advanced Customization

  1. Modify Track Appearance
// In TrackPainter class
const strokeWidth = 20.0;  // Change track thickness
const markersCount = 40;   // Change number of dash marks
  1. Change Car Icon
// Replace the Icon widget with custom widget
Transform(
  ...
  child: Image.asset(
    'assets/car_icon.png',
    width: 30,
    height: 30,
  ),
)
  1. Customize Timer Text
Text(
  '${((1 - _controller.value) * widget.durationInSeconds).ceil()}s',
  style: TextStyle(
    fontSize: 40,
    fontWeight: FontWeight.bold,
    color: Colors.blue[900],
  ),
)

Common Issues and Solutions

  1. Car Rotation Issues

    • Ensure transform origin is centered
    • Use correct mathematical calculations for rotation
    • Consider device pixel ratio
  2. Performance Optimization

    • Use shouldRepaint efficiently
    • Minimize widget rebuilds
    • Use const constructors where possible
  3. Animation Smoothness

    • Use vsync properly
    • Handle disposal correctly
    • Consider using curves for natural movement

Additional Tips

  1. State Management

    • Consider using a state management solution for complex implementations
    • Handle timer state properly when navigating
  2. Responsive Design

    • Use MediaQuery for responsive sizing
    • Consider different screen orientations
    • Handle edge cases for small screens
  3. Testing

    • Test with different durations
    • Verify completion callback
    • Check animation smoothness

Example with State Management (using Provider)

class TimerState extends ChangeNotifier {
  bool isRunning = false;
  int remainingSeconds = 0;

  void startTimer(int duration) {
    remainingSeconds = duration;
    isRunning = true;
    notifyListeners();
  }

  void stopTimer() {
    isRunning = false;
    notifyListeners();
  }
}

Remember to add necessary dependencies in your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  provider: any  # If using provider for state management

This implementation provides a solid foundation for a circular timer with car animation. You can build upon this base to add more features or customize it further based on your specific needs.

Would you like me to explain any part in more detail or add specific customizations?

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • Low reputation (1):
Posted by: Darshan Parmar