Flutter 绘制具有 3 多种颜色和值的圆形边框
Flutter Draw a circle border with 3 multiple colors and values
如何绘制具有多个值的图表样式的圆形边框?还为圆圈中的每个值动态扩展以填充 100% 的圆圈设置动画?
动画可以由 TweenAnimationBuilder 处理,它将在构建时播放。
为了达到预期的效果,我们必须使用 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;
}
希望有人会觉得这很有帮助 :) 随时改进它!编码愉快!
感谢 Paulius Greičiūnas 的回答,我实现了一种更通用的方法来用不同的颜色绘制圆圈。您只需将颜色的出现指定为地图和圆的大小。
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;
}
使用地图,例如{Colors.blue: 2, Colors.green: 1}
你会得到一个 1/3 绿色和 2/3 蓝色的圆圈。
请注意,您还可以定义一个子项,以便圆圈中包含内容。这是我在日历中使用的一个示例,其中包含多个包含内容的圈子。
如何绘制具有多个值的图表样式的圆形边框?还为圆圈中的每个值动态扩展以填充 100% 的圆圈设置动画?
动画可以由 TweenAnimationBuilder 处理,它将在构建时播放。 为了达到预期的效果,我们必须使用 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;
}
感谢 Paulius Greičiūnas 的回答,我实现了一种更通用的方法来用不同的颜色绘制圆圈。您只需将颜色的出现指定为地图和圆的大小。
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;
}
使用地图,例如{Colors.blue: 2, Colors.green: 1}
你会得到一个 1/3 绿色和 2/3 蓝色的圆圈。
请注意,您还可以定义一个子项,以便圆圈中包含内容。这是我在日历中使用的一个示例,其中包含多个包含内容的圈子。