Home > Software engineering >  How to make a spinning circle animation during a countdown in flutter?
How to make a spinning circle animation during a countdown in flutter?

Time:07-25

I have a time widget that counts down 30 seconds. I have a circle around the time (I attached a screenshot below). I need to animate this circle so that when the time is counted down for 30 seconds, the circle spins, only the time reaches 00:00 the circle stops and does not spin, by pressing the button, the time counts down again for 30 seconds and the circle starts spinning. Tell me how to make an animation of a spinning circle?

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

  @override
  Widget build(BuildContext context) {
    context.read<TimerBloc>().add(const TimerStarted(duration: 30));
    return BlocBuilder<TimerBloc, TimerState>(
      buildWhen: (prev, state) => prev.runtimeType != state.runtimeType,
      builder: (context, state) {
        return Center(
          child: Stack(
            alignment: AlignmentDirectional.center,
            children: [
              SizedBox(
                width: 70,
                height: 70,
                child: CustomPaint(
                  painter: RingPainter(
                      progress: 0.3,
                      taskCompletedColor: Colors.white,
                      taskNotCompletedColor:
                          constants.Colors.greyDark.withOpacity(0.5)),
                ),
              ),
              _timerText(context),
            ],
          ),
        );
      },
    );
  }

  Widget _timerText(BuildContext context) {
    final duration = context.select((TimerBloc bloc) => bloc.state.duration);
    final minutesStr =
        ((duration / 60) % 60).floor().toString().padLeft(2, '0');
    final secondsStr = (duration % 60).floor().toString().padLeft(2, '0');
    return Text(
      '$minutesStr:$secondsStr',
      style: constants.Styles.smallTextStyleWhite,
      // style: Theme.of(context).textTheme.headline1,
    );
  }
}

class RingPainter extends CustomPainter {
  // 1. add a constructor and properties that can be set from the parent widget
  RingPainter({
    required this.progress,
    required this.taskNotCompletedColor,
    required this.taskCompletedColor,
  });
  // a value between 0 and 1
  final double progress;
  // background color to use when the task is not completed
  final Color taskNotCompletedColor;
  // foreground color to use when the task is completed
  final Color taskCompletedColor;

  @override
  void paint(Canvas canvas, Size size) {
    // 2. configure the paint and drawing properties
    final strokeWidth = size.width / 20.0;
    final center = Offset(size.width / 2, size.height / 2);
    final radiusBackgroundPaint = (size.width - strokeWidth) / 2;
    final radiusForegroundPaint = (size.width - strokeWidth) / 2.3;

    // 3. create and configure the background paint
    final backgroundPaint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = strokeWidth
      ..color = taskNotCompletedColor
      ..style = PaintingStyle.fill;
    // 4. draw a circle
    canvas.drawCircle(center, radiusBackgroundPaint, backgroundPaint);

    // 5. create and configure the foreground paint
    final foregroundPaint = Paint()
      ..isAntiAlias = true
      ..strokeWidth = strokeWidth
      ..color = taskCompletedColor
      ..style = PaintingStyle.stroke;
    // 6. draw an arc that starts from the top (-pi / 2)
    // and sweeps and angle of (2 * pi * progress)
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radiusForegroundPaint),
      -math.pi * 0.65,
      1.5 * math.pi * 0.2,
      false,
      foregroundPaint,
    );

    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radiusForegroundPaint),
      -math.pi * .14,
      1.5 * math.pi * 0.2,
      false,
      foregroundPaint,
    );

    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radiusForegroundPaint),
      -math.pi * 1.65,
      1.5 * math.pi * 0.2,
      false,
      foregroundPaint,
    );

    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radiusForegroundPaint),
      math.pi * 0.85,
      1.5 * math.pi * 0.2,
      false,
      foregroundPaint,
    );
  }

  // 7. only return true if the old progress value
  // is different from the new one
  @override
  bool shouldRepaint(covariant RingPainter oldDelegate) =>
      oldDelegate.progress != progress;
}

class CirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final strokeWidth = size.width / 15.0;
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;
    var paint1 = Paint()
      ..color = const Color(0xff63aa65)
      ..style = PaintingStyle.fill;
    //a circle
    canvas.drawCircle(center, radius, paint1);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

enter image description here

CodePudding user response:

Use a FutureBuilder, the future is a this: Future.delayed(Duration(minutes: 1)) After a minute, the builder will update to complete. Change the minutes to whatever you want, you can do minutes, seconds, hours, etc.

In the builder, have an if statement to check the status. in the else, return a CircularProgressIndicator widget. That's what you want.

See the docs: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

FYI, you have way more code than you need.

CodePudding user response:

You can use AnimatedRotation widget and animate its turns like this

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: AnimatedCircle());
  }
}

class AnimatedCircle extends StatefulWidget {
  const AnimatedCircle({Key? key}) : super(key: key);

  @override
  State<AnimatedCircle> createState() => _AnimatedCircleState();
}

class _AnimatedCircleState extends State<AnimatedCircle> {
  double rotation = 0;
  int seconds = 30;
  @override
  void initState() {
    // TODO: implement initState

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            MaterialButton(
                color: Colors.blue,
                child: Text("Start Animation"),
                onPressed: () {
                  seconds = 30;
                  rotation = 0;
                  startAnimation();
                }),
            SizedBox(
              height: 20,
            ),
            MaterialButton(
                color: Colors.blue,
                child: Text("Stop Animation"),
                onPressed: () {
                  seconds = 0;
                  setState(() {});
                }),
            SizedBox(
              height: 20,
            ),
            AnimatedRotation(
              turns: rotation,
              duration: Duration(seconds: 1),
              curve: Curves.linear,
              child: Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ),//replace this container with the circle widget you currently have
            ),
            SizedBox(
              height: 20,
            ),
            Text("Seconds: $seconds")
          ],
        ),
      ),
    );
  }

  startAnimation() {
    rotation  ;
    seconds--;
    setState(() {});
    if (seconds >= 0) {
      Future.delayed(Duration(seconds: 1), () {
        startAnimation();
      });
    }
  }
}

  • Related