import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../styles/theme.dart'; class Ruler extends StatelessWidget{ const Ruler({ super.key, required this.hideHeight, required this.mapScale}); final double hideHeight; final double mapScale; @override Widget build(BuildContext context) { return CustomPaint( size: Size(context.width, context.height), painter: _RulerPainter( hideHeight: hideHeight, mapScale: mapScale ), ); } } class _Scale{ var text = ''; var px = 0.0; var meter = 0; _Scale(this.text, this.px, this.meter); } class _RulerPainter extends CustomPainter { _RulerPainter({ required this.hideHeight, required this.mapScale, }); final double hideHeight; final double mapScale; static const Color _color = Colors.black; final painter = Paint() ..color = _color ..strokeWidth = 1.5; final textPainter = TextPainter( textDirection: TextDirection.ltr, textAlign: TextAlign.center, ); final path = Path(); @override void paint(Canvas canvas, Size size) { final centerX = size.width/2; const arrowHeight = 24.0; canvas.drawLine(Offset(centerX, arrowHeight), Offset(centerX, size.height - hideHeight), painter); final oneScale = _getScale(); const steps = 10.0; double heightLeft = size.height; final scaleXLeft = centerX-5; final scaleXRight = centerX + 5; final littleScale = oneScale.px / steps; final littleScaleXLeft = scaleXLeft + 2; final littleScaleXRight = scaleXRight - 2; path.moveTo(centerX, 0); path.lineTo(centerX + 5, arrowHeight); path.lineTo(centerX - 5, arrowHeight); path.close(); canvas.drawPath(path, painter); for(var i = 0; heightLeft > arrowHeight + 1;i++){ if(size.height - heightLeft < hideHeight){ heightLeft -= littleScale; continue; } if(i % steps == 0){ canvas.drawLine(Offset(scaleXLeft, heightLeft), Offset(scaleXRight, heightLeft), painter); textPainter.text = TextSpan( text: _meterToStr(i ~/ steps * oneScale.meter), style: const TextStyle( color: _color, fontSize: 14, fontWeight: FontWeight.w500, ), ); textPainter.layout(); Offset labelOffset = Offset( centerX + 10, heightLeft - textPainter.height / 2); textPainter.paint(canvas, labelOffset); }else{ canvas.drawLine(Offset(littleScaleXLeft, heightLeft), Offset(littleScaleXRight, heightLeft), painter); } heightLeft -= littleScale; } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => false; _Scale? _getOneScaleLen(int meter) { var valuePx = meter * mapScale; if (valuePx > 100) { return _Scale(_meterToStr(meter), valuePx, meter); } return null; } String _meterToStr(int meter){ if(meter < 1000){ return '$meter m'; }else{ return "${(meter.toDouble()/1000).toStringAsFixed(1)} km"; } } _Scale _getScale() { var s = 1; _Scale? out; while(out == null){ out = _getOneScaleLen(5 * s); if (out != null) { return out; } out = _getOneScaleLen(10 * s); if (out != null) { return out; } out = _getOneScaleLen(20 * s); if (out != null) { return out; } s *= 10; } return out; } } void main() async { runApp(GetMaterialApp( theme: appThemeData(), home: const Scaffold( body: Ruler(hideHeight: 20, mapScale: 1), ))); }