Created
March 15, 2021 11:13
-
-
Save codec-abc/99a39d06de2a701e5d58554fc2fb86a3 to your computer and use it in GitHub Desktop.
Flutter speedometer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:flutter/rendering.dart'; | |
| import 'dart:developer' as developer; | |
| import 'dart:math' as math; | |
| import 'package:vector_math/vector_math.dart' as vmath; | |
| import 'package:flutter/foundation.dart' show kIsWeb; | |
| class SpeedoMeter extends CustomPainter { | |
| SpeedoMeter(); | |
| @override | |
| void paint(Canvas canvas, Size size) { | |
| //developer.log("size is " + size.toString()); | |
| var gaugeCenter = Offset(size.width / 2.0, size.height / 2.0); | |
| var zero = Offset(0, 0); | |
| canvas.save(); | |
| canvas.translate(gaugeCenter.dx, gaugeCenter.dy); | |
| var sideMaxLength = math.min(size.width / 2.0, size.height / 2.0); | |
| // developer.log("sideMaxLength is " + sideMaxLength.toString()); | |
| var gaugeRadius = sideMaxLength * 0.95; | |
| _paintBackground(canvas, gaugeCenter, size); | |
| var paint = Paint() | |
| ..shader = RadialGradient( | |
| colors: [ | |
| Colors.black, | |
| Colors.grey.shade800, | |
| ], | |
| ).createShader(Rect.fromCircle( | |
| center: zero, | |
| radius: gaugeRadius, | |
| )); | |
| canvas.drawCircle(zero, gaugeRadius, paint); | |
| for (var i = 0; i < 70; i += 10) { | |
| var angle = i / 70.0 * math.pi * 2.0; | |
| angle = angle - vmath.radians(90) + vmath.radians(4.0 * 360.0 / 7.0); | |
| var x = math.cos(angle) * gaugeRadius; | |
| var y = math.sin(angle) * gaugeRadius; | |
| var newCenter = Offset(x, y); | |
| if (i < 50) { | |
| _paintGaugeLabel(i.toString(), canvas, newCenter, angle, Colors.white, | |
| sideMaxLength); | |
| } else { | |
| _paintGaugeLabel(i.toString(), canvas, newCenter, angle, | |
| Colors.red.shade800, sideMaxLength); | |
| } | |
| } | |
| for (var i = 0; i < 60; i++) { | |
| if (i % 10 == 0) { | |
| continue; | |
| } | |
| var angle = i / 70.0 * math.pi * 2.0; | |
| angle = angle - vmath.radians(90) + vmath.radians(4.0 * 360.0 / 7.0); | |
| var x = math.cos(angle) * gaugeRadius; | |
| var y = math.sin(angle) * gaugeRadius; | |
| var newCenter = Offset(x, y); | |
| if (i < 50) { | |
| _paintSubGaugeLabel(i.toString(), canvas, newCenter, angle, Colors.white, | |
| sideMaxLength); | |
| } else { | |
| _paintSubGaugeLabel(i.toString(), canvas, newCenter, angle, | |
| Colors.red.shade800, sideMaxLength); | |
| } | |
| } | |
| paint = Paint() | |
| ..style = PaintingStyle.stroke | |
| ..color = Colors.black | |
| ..strokeWidth = gaugeRadius * 0.02 | |
| ..isAntiAlias = true; | |
| canvas.drawCircle(zero, gaugeRadius, paint); | |
| canvas.restore(); | |
| var needleAngle = vmath.radians(90); | |
| _paintNeedle(canvas, gaugeCenter, sideMaxLength, needleAngle); | |
| } | |
| void _paintNeedle( | |
| Canvas canvas, Offset center, double sideMaxLength, double needleAngle) { | |
| canvas.save(); | |
| canvas.translate(center.dx, center.dy); | |
| canvas.rotate(needleAngle); | |
| var paint = Paint() | |
| ..style = PaintingStyle.fill | |
| ..color = Colors.red.shade700 | |
| ..isAntiAlias = true; | |
| var scaleFactor = sideMaxLength / 500.0; | |
| var diffX = 100.0; | |
| var firstPointX = 0.0 * scaleFactor; | |
| var firstPointY = 0.0 * scaleFactor; | |
| var secondPointX = 10.0 * scaleFactor; | |
| var secondPointY = 10.0 * scaleFactor; | |
| var thirdPointX = 5.0 * scaleFactor; | |
| var thirdPointY = sideMaxLength * 0.90; | |
| var fourthPointX = 0.0; | |
| var fourthPointY = sideMaxLength * 0.90; | |
| canvas.translate(-diffX, 0); | |
| var path = Path(); | |
| path.addPolygon([ | |
| Offset(firstPointX + diffX, firstPointY), | |
| Offset(secondPointX + diffX, secondPointY), | |
| Offset(thirdPointX + diffX, thirdPointY), | |
| Offset(fourthPointX + diffX, fourthPointY), | |
| Offset(-thirdPointX + diffX, thirdPointY), | |
| Offset(-secondPointX + diffX, secondPointY), | |
| ], true); | |
| canvas.drawPath(path, paint); | |
| var paint2 = Paint() | |
| ..style = PaintingStyle.fill | |
| ..color = Colors.red.shade600 | |
| ..isAntiAlias = true; | |
| var path2 = Path(); | |
| path2.addPolygon([ | |
| Offset(-firstPointX + diffX, firstPointY), | |
| Offset(-secondPointX + diffX, secondPointY), | |
| Offset(-thirdPointX + diffX, thirdPointY), | |
| Offset(-fourthPointX + diffX, fourthPointY), | |
| ], true); | |
| canvas.drawPath(path2, paint2); | |
| canvas.restore(); | |
| } | |
| void _paintSubGaugeLabel(String text, Canvas canvas, Offset labelCenter, | |
| double angle, Color color, double sideMaxLength) { | |
| canvas.save(); | |
| canvas.translate(labelCenter.dx, labelCenter.dy); | |
| canvas.rotate(angle); | |
| var paint = Paint() | |
| ..style = PaintingStyle.fill | |
| ..color = color | |
| ..isAntiAlias = true; | |
| double rectLength = sideMaxLength / 500.0 * 20.0; | |
| double rectStrokeSize = sideMaxLength / 500.0 * 6.0; | |
| canvas.drawRect( | |
| Rect.fromCenter( | |
| center: Offset(-rectLength / 2.0, 0), width: rectLength, height: rectStrokeSize), | |
| paint); | |
| canvas.restore(); | |
| } | |
| void _paintGaugeLabel(String text, Canvas canvas, Offset labelCenter, | |
| double angle, Color color, double sideMaxLength) { | |
| canvas.save(); | |
| canvas.translate(labelCenter.dx, labelCenter.dy); | |
| canvas.rotate(angle); | |
| var paint = Paint() | |
| ..style = PaintingStyle.fill | |
| ..color = color | |
| ..isAntiAlias = true; | |
| double rectLength = sideMaxLength / 500.0 * 60.0; | |
| double rectStrokeSize = sideMaxLength / 500.0 * 15.0; | |
| double labelOffsetScaleFactor = 0.8; | |
| canvas.drawRect( | |
| Rect.fromCenter( | |
| center: Offset(-rectLength / 2.0, 0), width: rectLength, height: rectStrokeSize), | |
| paint); | |
| canvas.restore(); | |
| final TextPainter textPainter = TextPainter( | |
| text: TextSpan( | |
| text: text, | |
| style: TextStyle(color: color, fontSize: sideMaxLength / 15.0)), | |
| textAlign: TextAlign.end, | |
| textDirection: TextDirection.ltr) | |
| ..layout(maxWidth: 1000); | |
| var offsets = textPainter.getBoxesForSelection( | |
| TextSelection(baseOffset: 0, extentOffset: text.length))[0]; | |
| var delta = | |
| Offset(offsets.right - offsets.left, offsets.bottom - offsets.top); | |
| var centeredLabelOffset = labelCenter; | |
| centeredLabelOffset = centeredLabelOffset.scale(labelOffsetScaleFactor, labelOffsetScaleFactor); | |
| centeredLabelOffset = centeredLabelOffset - delta / 2.0; | |
| textPainter.paint(canvas, centeredLabelOffset); | |
| } | |
| void _paintBackground(Canvas canvas, Offset gaugeCenter, Size size) { | |
| var paint = Paint() | |
| ..style = PaintingStyle.fill | |
| ..color = Colors.white | |
| ..isAntiAlias = true; | |
| if (!kIsWeb) { | |
| canvas.drawColor(Colors.black, BlendMode.clear); | |
| } | |
| canvas.drawRect( | |
| Rect.fromCenter( | |
| center: gaugeCenter, width: size.width, height: size.height), | |
| paint); | |
| paint = paint..color = Colors.black; | |
| } | |
| @override | |
| bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
| // TODO: implement shouldRepaint | |
| developer.log("shouldRepaint"); | |
| return true; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment