Skip to content

Instantly share code, notes, and snippets.

@gladimdim
Created February 15, 2022 13:13
Show Gist options
  • Save gladimdim/f242f0d75646f6b08bd837996e2f85b6 to your computer and use it in GitHub Desktop.
Save gladimdim/f242f0d75646f6b08bd837996e2f85b6 to your computer and use it in GitHub Desktop.
Circular progress bar with animations
import 'package:flutter/material.dart';
import 'dart:math' as Math;
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SampleItemDetailsView(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'Hello, World!',
style: Theme.of(context).textTheme.headline4,
);
}
}
/// Displays detailed information about a SampleItem.
class SampleItemDetailsView extends StatefulWidget {
const SampleItemDetailsView({Key? key}) : super(key: key);
static const routeName = '/sample_item';
@override
State<SampleItemDetailsView> createState() => _SampleItemDetailsViewState();
}
class _SampleItemDetailsViewState extends State<SampleItemDetailsView> {
double progress = 0.0;
double step = 0.05;
@override
Widget build(BuildContext context) {
return Column(
children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 200,
height: 200,
child: CircleProgressBarAnimated(
progress: progress,
),
),
Text("Progress: ${(progress * 100).toInt()}%"),
],
),
TextButton(
onPressed: () => _changeProgress(step), child: Text("+${(step * 100).toInt()}%")),
TextButton(
onPressed: () => _changeProgress(-step), child: Text("-${(step * 100).toInt()}%")),
],
);
}
void _changeProgress(double change) {
setState(() {
progress += change;
print(progress);
if (progress >= 1) {
progress = 1;
}
if (progress < 0) {
progress = 0;
}
});
}
}
class CircleProgressBarAnimated extends StatefulWidget {
const CircleProgressBarAnimated(
{Key? key,
this.duration = const Duration(milliseconds: 200),
required this.progress})
: super(key: key);
final Duration duration;
final double progress;
@override
_CircleProgressBarAnimatedState createState() =>
_CircleProgressBarAnimatedState();
}
class _CircleProgressBarAnimatedState extends State<CircleProgressBarAnimated>
with TickerProviderStateMixin {
late final AnimationController _controller;
late Animation animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration,
);
animation = Tween<double>(begin: 0, end: widget.progress).animate(_controller);
_controller.forward();
}
@override
void didUpdateWidget(CircleProgressBarAnimated oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.progress != widget.progress) {
animation = Tween<double>(begin: oldWidget.progress, end: widget.progress).animate(_controller);
_controller.reset();
_controller.forward();
}
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) =>
CircleProgressBar(progress: animation.value),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class CircleProgressBar extends StatelessWidget {
final double progress;
final double progressWidth;
const CircleProgressBar({Key? key, required this.progress, this.progressWidth = 20}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CirclePainter(
progress,
progressWidth,
),
);
}
}
class CirclePainter extends CustomPainter {
final double progress;
final double width;
CirclePainter(this.progress,this.width);
double degToRad(double deg) => deg * (Math.pi / 180.0);
double get progressInDeg => 359.999 * progress;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.green
..strokeWidth = width
..style = PaintingStyle.stroke;
final path = Path()
..arcTo(
Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
height: size.height - width,
width: size.width - width),
degToRad(0),
degToRad(progressInDeg),
false);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CirclePainter oldDelegate) {
return oldDelegate.progress != progress;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment