Reputation: 476
How do you draw a diagram style circle border with multiple values? Also animated that each value in circle expands dynamically filling 100% of the circle?
Upvotes: 5
Views: 3848
Reputation: 336
Thanks to Paulius Greičiūnas answer, I implemented a more general way to paint a circle in different colors. You only have to specify the occurrences of the colors as a map and a size of the circle.
class MultipleColorCircle extends StatelessWidget {
final Map<Color, int> colorOccurrences;
final double height;
final Widget? child;
@override
MultipleColorCircle(
{required this.colorOccurrences, this.height = 20, this.child});
Widget build(BuildContext context) => Container(
height: height,
width: height,
child: CustomPaint(
size: Size(20, 20),
child: Center(child: child),
painter: _MultipleColorCirclePainter(
colorOccurrences: colorOccurrences,
height: height,
)),
);
}
class _MultipleColorCirclePainter extends CustomPainter {
final Map<Color, int> colorOccurrences;
final double height;
@override
_MultipleColorCirclePainter(
{required this.colorOccurrences, required this.height});
double pi = math.pi;
@override
void paint(Canvas canvas, Size size) {
double strokeWidth = 1;
Rect myRect =
Rect.fromCircle(center: Offset(height / 2, height / 2), radius: height);
double radianStart = 0;
double radianLength = 0;
int allOccurrences = 0;
//set denominator
colorOccurrences.forEach((color, occurrence) {
allOccurrences += occurrence;
});
colorOccurrences.forEach((color, occurrence) {
double percent = occurrence / allOccurrences;
radianLength = 2 * percent * math.pi;
canvas.drawArc(
myRect,
radianStart,
radianLength,
false,
Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke);
radianStart += radianLength;
});
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
With a map e.g. {Colors.blue: 2, Colors.green: 1}
you will get a circle with 1/3 green and 2/3 blue.
Note, that you can also define a child, so that the circle has content in it. Here is an example, which I used in my calendar, of multiple circles with content in it.
Upvotes: 3
Reputation: 476
Animation can be handled by TweenAnimationBuilder and it will be played on build. To achieve desired result we must use customPainter.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TweenAnimationBuilder(
duration: const Duration(seconds: 2),
tween: Tween(begin: 0.0, end: 1.0),
curve: Curves.easeOutCubic,
builder: (BuildContext context, dynamic value, Widget child) {
return CustomPaint(
painter: OpenPainter(
totalQuestions: 300,
learned: 75,
notLearned: 75,
range: value),
);
},
),
),
);
}
}
class OpenPainter extends CustomPainter {
final learned;
final notLearned;
final range;
final totalQuestions;
double pi = math.pi;
OpenPainter({this.learned, this.totalQuestions, this.notLearned, this.range});
@override
void paint(Canvas canvas, Size size) {
double strokeWidth = 7;
Rect myRect = const Offset(-50.0, -50.0) & const Size(100.0, 100.0);
var paint1 = Paint()
..color = Colors.red
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
var paint2 = Paint()
..color = Colors.green
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
var paint3 = Paint()
..color = Colors.yellow
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
double firstLineRadianStart = 0;
double _unAnswered = (totalQuestions - notLearned - learned) * range / totalQuestions;
double firstLineRadianEnd = (360 * _unAnswered) * math.pi / 180;
canvas.drawArc(
myRect, firstLineRadianStart, firstLineRadianEnd, false, paint1);
double _learned = (learned) * range / totalQuestions;
double secondLineRadianEnd = getRadians(_learned);
canvas.drawArc(myRect, firstLineRadianEnd, secondLineRadianEnd, false, paint2);
double _notLearned = (notLearned) * range / totalQuestions;
double thirdLineRadianEnd = getRadians(_notLearned);
canvas.drawArc(myRect, firstLineRadianEnd + secondLineRadianEnd, thirdLineRadianEnd, false, paint3);
// drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
}
double getRadians(double value) {
return (360 * value) * pi / 180;
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Hopefully someone will find this helpfull :) Feel free to improve on this! Happy coding !
Upvotes: 7