import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../styles/theme.dart'; import 'package:sensor/sensor.dart' as sensor; class Compass extends StatelessWidget { const Compass( {super.key, required this.compassRadians, required this.mapNorthRadians, required this.orientation, required this.diameter, this.nextPointRadians }); final double diameter; final double compassRadians; final double mapNorthRadians; final sensor.Orientation orientation; final double? nextPointRadians; int getDegrees(){ var d = (mapNorthRadians-compassRadians)*180~/pi; while(d < 0){ d+=360; } while(d > 360){ d-=360; } return d; } @override Widget build(BuildContext context) { return Stack( children: [ Transform.rotate(angle: compassRadians, child: SizedBox( width: diameter, height: diameter, child: CustomPaint(painter: _ArrowPainter()), ),), Transform.rotate(angle: mapNorthRadians, child: SizedBox( width: diameter, height: diameter, child: CustomPaint(painter: _PlatePainter()), )), nextPointRadians != null? Transform.rotate(angle: nextPointRadians!, child: SizedBox( width: diameter, height: diameter, child: CustomPaint(painter: _NextPointArrowPainter()), )): const SizedBox(), SizedBox( width: diameter, height: diameter, child: CustomPaint(painter: _OrientationPainter(orientation)), ), Container( width: diameter, alignment: Alignment.topCenter, padding: const EdgeInsets.only(top: 30), child: Text(' ${getDegrees()}°')) ], ); } } void drawArrow(Canvas canvas, Paint painter, Offset topCenter){ final path = Path(); var x = topCenter.dx; var y = topCenter.dy; path.moveTo(x, y); x += 5; y +=10; path.lineTo(x, y); x -= 5; y -= 2; path.lineTo(x, y); x -= 5; y += 2; path.lineTo(x, y); path.close(); painter.style = PaintingStyle.fill; canvas.drawPath(path, painter); } class _ArrowPainter extends CustomPainter { var textPainter = TextPainter(); final painter = Paint(); @override void paint(Canvas canvas, Size size) { var radius = size.height / 2 - 4; painter.isAntiAlias = true; painter.style = PaintingStyle.stroke; painter.color = const Color(0x5eaff59f); final center = Offset(size.height / 2, size.width / 2); final centerX = center.dx; final centerY = center.dy; canvas.drawCircle(center, radius, painter); radius -= 3; painter.style = PaintingStyle.fill; painter.color = Colors.white.withAlpha(120); canvas.drawCircle(center, radius, painter); painter.color = const Color(0xff92b686); painter.strokeWidth = 0.5; painter.style = PaintingStyle.stroke; textPainter = TextPainter( textDirection: TextDirection.ltr, textAlign: TextAlign.center, ); double angle = 0; const padding = 4; for (int i = 0; i < 360; i += 5) { angle = (i-90) * pi / 180; var start = Offset( centerX + (radius - padding - 5) * cos(angle), centerY + (radius - padding - 5) * sin(angle), ); var end = Offset( centerX + (radius - padding) * cos(angle), centerY + (radius - padding) * sin(angle), ); if (i % 30 == 0) { start = Offset( centerX + (radius - padding - 8) * cos(angle), centerY + (radius - padding - 8) * sin(angle), ); // String label = "$i"; // textPainter.text = TextSpan( // text: label, // style: const TextStyle( // color: Colors.black, // fontSize: 8, // fontWeight: FontWeight.w100, // ), // ); // textPainter.layout(); // Offset labelOffset = // Offset( // start.dx - 10 * cos(angle) - textPainter.width / 2, // start.dy - 10 * sin(angle) - textPainter.height / 2); // textPainter.paint(canvas, labelOffset); } canvas.drawLine(start, end, painter); } painter.color = Colors.red; drawArrow(canvas, painter, Offset(centerX, radius - 40)); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class _PlatePainter extends CustomPainter { final painter = Paint() ..color = const Color(0xff92b686) ..style = PaintingStyle.fill; @override void paint(Canvas canvas, Size size) { final centerX = size.width/2; final path = Path(); var x = centerX; var y = 20.0; path.moveTo(x, y); x += 5; y = 0; path.lineTo(x, y); x -= 10; path.lineTo(x, y); path.close(); canvas.drawPath(path, painter); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class _NextPointArrowPainter extends CustomPainter { final painter = Paint() ..color = const Color(0xffffcb00); @override void paint(Canvas canvas, Size size) { final centerX = size.width/2; var radius = size.height / 2 - 4; drawArrow(canvas, painter, Offset(centerX, radius-40)); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } class _OrientationPainter extends CustomPainter { _OrientationPainter(this.orientation); final sensor.Orientation orientation; final painter1 = Paint() ..color = Colors.grey ..style = PaintingStyle.fill ..blendMode = BlendMode.xor ; final painter2 = Paint() ..color = const Color(0xff38941b) ..style = PaintingStyle.fill ; @override void paint(Canvas canvas, Size size) { final centerX = size.width/2; final centerY = centerX; const radius1 = 10.0; const radius2 = radius1+2; const maxOffset = 20; var d = sqrt(orientation.x * orientation.x + orientation.y * orientation.y); var len = d / sqrt(pi/2 * pi/2 + pi/2 * pi/2) * maxOffset; var dx = 0.0; var dy = 0.0; if(d!=0){ dx = len * (orientation.y / d) / 2; dy = len * (orientation.x / d) / 2; } final c1 = Offset(centerX + dx, centerY + dy); final c2 = Offset(centerX - dx, centerY - dy); canvas.saveLayer(null, Paint()); canvas.drawCircle(c2, radius2, painter2); canvas.drawCircle(c1, radius1, painter1); canvas.restore(); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } void main() async { runApp(GetMaterialApp( theme: appThemeData(), home: Container( width: double.infinity, height: double.infinity, color: Colors.white, alignment: Alignment.center, child: Compass( diameter: 140, compassRadians: 0, mapNorthRadians: 0, orientation: sensor.Orientation() ..x = 45 ..y = 45, )))); }