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;
}
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();
});
}
}
}