Skip to content

Instantly share code, notes, and snippets.

@m8811163008
Created November 19, 2023 06:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m8811163008/1346d2a9dfae983395256a6dba271b8b to your computer and use it in GitHub Desktop.
Save m8811163008/1346d2a9dfae983395256a6dba271b8b to your computer and use it in GitHub Desktop.
Create a pie chart with rounded ends
import 'package:component_library/component_library.dart';
import 'package:domain_models/domain_models.dart';
import 'package:flutter/material.dart';
class AppPieChart extends StatefulWidget {
const AppPieChart({
super.key,
required this.subAccount,
});
final SubAccount subAccount;
@override
State<StatefulWidget> createState() => AppPieChartState();
}
class AppPieChartState extends State<AppPieChart> {
@override
Widget build(BuildContext context) {
final l10n = ComponentLibraryLocalizations.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 2,
child: Align(
alignment: AlignmentDirectional.centerStart,
child: Stack(
alignment: Alignment.center,
children: [
AppPieChartCircleShape(
onlineDevice:
widget.subAccount.subaccountDashboard.onlineCount,
offlineDevice:
widget.subAccount.subaccountDashboard.offlineCount,
),
Text(
l10n.appPieChartTitle(
widget.subAccount.subaccountDashboard.workersCount,
),
style: context.textThemeExtension.mobileBold16,
),
],
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Spacer(),
ChartIndicator(
color: context.colorThemeExtension.lightBlue!,
title: l10n.appPieChartIndicatorAll,
value: widget.subAccount.subaccountDashboard.workersCount),
Spaces.smallVerticalSizedBox,
ChartIndicator(
color: context.colorThemeExtension.green!,
title: l10n.appPieChartIndicatorOnline,
value: widget.subAccount.subaccountDashboard.onlineCount),
Spaces.smallVerticalSizedBox,
ChartIndicator(
color: context.colorThemeExtension.darkOrange!,
title: l10n.appPieChartIndicatorOffline,
value: widget.subAccount.subaccountDashboard.offlineCount),
const Spacer(),
MoreInfoTextButton(
onPressed: DashboardMoreInfoButtonsNavigation.of(context)
.onPieChartMoreInfoPreesed,
),
],
),
),
)
],
);
}
}
/// Create custome painter
import 'dart:math';
import 'dart:ui';
import 'package:component_library/component_library.dart';
import 'package:flutter/material.dart';
class AppPieChartCircleShape extends StatelessWidget {
const AppPieChartCircleShape(
{super.key, required this.onlineDevice, required this.offlineDevice});
final int onlineDevice;
final int offlineDevice;
@override
Widget build(BuildContext context) {
int allDevice = onlineDevice + offlineDevice;
if (allDevice == 0) {
allDevice = 1;
}
final offlineShare = offlineDevice / allDevice;
const startAngle = 3 * pi / 2;
final space = (onlineDevice == 0 || offlineDevice == 0) ? 0 : pi / 18;
final offlineSwip = offlineShare * 2 * pi;
final onlineStartAngle = startAngle + offlineShare * 2 * pi;
final onlineSwip = 2 * pi - offlineSwip;
final pieSize = Size.fromRadius(90);
return Stack(
alignment: Alignment.center,
children: [
if (offlineDevice != 0)
CustomPaint(
size: pieSize,
painter: CurvePainter(
startAngle: startAngle,
swipAngle: offlineSwip - space,
isSolid: true,
offlineColor: context.colorThemeExtension.darkOrange!,
gradient: ParsePoolThemeData.hashrateChartLinearGradient,
),
),
if (onlineDevice != 0 || (onlineDevice == 0 && offlineDevice == 0))
CustomPaint(
size: pieSize,
painter: CurvePainter(
startAngle: onlineStartAngle,
swipAngle: onlineSwip - space,
offlineColor: context.colorThemeExtension.darkOrange!,
gradient: ParsePoolThemeData.hashrateChartLinearGradient,
),
),
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
child: Stack(
children: [
if (offlineDevice != 0)
CustomPaint(
size: pieSize,
painter: CurvePainter(
startAngle: startAngle,
swipAngle: offlineSwip - space,
isSolid: true,
offlineColor: context.colorThemeExtension.darkOrange!,
gradient: ParsePoolThemeData.hashrateChartLinearGradient,
),
),
if (onlineDevice != 0 ||
(onlineDevice == 0 && offlineDevice == 0))
CustomPaint(
size: pieSize,
painter: CurvePainter(
startAngle: onlineStartAngle,
swipAngle: onlineSwip - space,
offlineColor: context.colorThemeExtension.darkOrange!,
gradient: ParsePoolThemeData.hashrateChartLinearGradient,
),
),
],
),
),
],
);
}
}
class CurvePainter extends CustomPainter {
const CurvePainter({
this.startAngle = pi / 5,
this.swipAngle = pi / 6,
this.arcRadius = 180,
this.innerLength = 10,
this.isSolid = false,
required this.offlineColor,
required this.gradient,
this.stroke = 5.0,
});
final double startAngle;
final double swipAngle;
final double arcRadius;
final double innerLength;
final bool isSolid;
final Color offlineColor;
final LinearGradient gradient;
final double stroke;
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
Path path = Path();
final rect = Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: arcRadius,
height: arcRadius,
);
paint.strokeWidth = stroke;
paint.style = PaintingStyle.stroke;
if (isSolid) {
paint.color = offlineColor;
} else {
paint.shader = gradient.createShader(rect);
}
// blurPaint.maskFilter =
// MaskFilter.blur(BlurStyle.normal, convertRadiusToSigma(5));
final center = Offset(size.width / 2, size.height / 2);
final startPoint = _calculateStartPoint(
center: center, radius: arcRadius / 2, angle: startAngle);
path.moveTo(startPoint.dx, startPoint.dy);
path.relativeArcToPoint(
_calculateRelativeArcPoint(
angle: startAngle,
isStart: true,
lineLenght: innerLength / 2,
),
radius: const Radius.circular(2.0),
clockwise: false,
);
path.addArc(rect, startAngle, swipAngle);
path.relativeArcToPoint(
_calculateRelativeArcPoint(
angle: startAngle + swipAngle, lineLenght: innerLength / 2),
radius: const Radius.circular(2.0));
path.addArc(
Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: arcRadius - innerLength,
height: arcRadius - innerLength),
startAngle,
swipAngle);
canvas.drawPath(path, paint);
// canvas.drawShadow(path, gradient.colors.first, 0.5, true);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return oldDelegate != this;
}
double convertRadiusToSigma(double radius) {
return radius * 0.57735 + 0.5;
}
Offset _calculateRelativeArcPoint(
{required double angle, double lineLenght = 5, bool isStart = false}) {
int kharejGhesmat = angle ~/ (2 * pi);
angle = angle - kharejGhesmat * (2 * pi);
late final double x;
late final double y;
if (angle >= 0 && angle <= pi / 2) {
x = isStart ? -lineLenght * cos(angle) : -lineLenght * cos(angle);
y = isStart ? -lineLenght * sin(angle) : -lineLenght * sin(angle);
} else if (angle > pi / 2 && angle < pi) {
x = lineLenght * sin(angle - pi / 2);
y = -lineLenght * cos(angle - pi / 2);
} else if (angle >= pi && angle <= 3 * pi / 2) {
x = lineLenght * cos(angle - pi);
y = lineLenght * sin(angle - pi);
} else if (angle > 3 * pi / 2 && angle < 2 * pi) {
x = -lineLenght * sin(angle - 3 * pi / 2);
y = lineLenght * cos(angle - 3 * pi / 2);
} else {
throw Exception('degree is not valid');
}
return Offset(x, y);
}
Offset _calculateStartPoint(
{required Offset center, required double radius, required double angle}) {
final double x = radius * cos(angle);
final double y = radius * sin(angle);
return Offset(x, y) + center;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment