How to change Marker positon change in the charts so that it shows above the charts area
Hi,
I want to change the position of the marker in the trackball such that it shows on the top of the chart. Currently I can customize the look of the marker but I cannot change the y offset.
Thanks in advance.
Ekta
Attachment: IMG_1405.jpg_e3cf1187.zip
Hi Ekta,
We have prepared a sample and achieved the mentioned requirement by rendering the trackball tooltip and trackball marker above the chart area. This was done using the CustomPaint Widget, based on the position obtained and calculated from the onTrackballPositionChanging callback. In this sample, we used the onActualRangeChanged callback to obtain the visibleMaximum of the y-axis, the pointToPixel method to convert the point values to pixel values, and the ValueNotifier Widget to update the respective chart and CustomPainter when the position is changed.
Kindly refer the code snippet below :
|
class MyHomePage extends StatefulWidget { const MyHomePage({super.key});
@override State<MyHomePage> createState() => _MyHomePageState(); }
class _MyHomePageState extends State<MyHomePage> { late ChartSeriesController _chartSeriesController; late List<ChartSampleData> _chartSeriesData; late TrackballBehavior _trackballBehavior; Offset? _chartOriginPosition; num? yMaxPixelValue; int pointIndex = 0;
final ValueNotifier<int> count = ValueNotifier(0); final ValueNotifier<Offset?> _topEndChartPosition = ValueNotifier(null); final ValueNotifier<Offset?> _dataPointPosition = ValueNotifier(null); final GlobalKey<SfCartesianChartState> _chartGlobalKey = GlobalKey(); final GlobalKey<State> _containerGlobalKey = GlobalKey(); final EdgeInsets _chartPadding = const EdgeInsets.all(10);
@override void initState() { _chartSeriesData = [ ChartSampleData(DateTime(2001), 40), ChartSampleData(DateTime(2002), 80), ChartSampleData(DateTime(2003), 40), ChartSampleData(DateTime(2004), 60), ChartSampleData(DateTime(2005), 50), ChartSampleData(DateTime(2006), 90), ChartSampleData(DateTime(2007), 110), ChartSampleData(DateTime(2008), 70), ChartSampleData(DateTime(2009), 120), ChartSampleData(DateTime(2010), 100), ];
_trackballBehavior = TrackballBehavior( enable: true, lineWidth: 0, activationMode: ActivationMode.singleTap, tooltipAlignment: ChartAlignment.near, tooltipSettings: const InteractiveTooltip(enable: false), ); super.initState(); }
@override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ Center( child: SizedBox( width: 500, height: 300, child: SfCartesianChart( key: _chartGlobalKey, onActualRangeChanged: (rangeChangedArgs) { if (rangeChangedArgs.orientation == AxisOrientation.vertical) { CartesianChartPoint point = CartesianChartPoint(0, rangeChangedArgs.visibleMax); yMaxPixelValue = _chartSeriesController.pointToPixel(point).dy; } }, onTrackballPositionChanging: (TrackballArgs trackballArgs) { _topEndChartPosition.value = Offset( trackballArgs.chartPointInfo.markerXPos!, yMaxPixelValue!.toDouble() - (_chartPadding.top + _chartPadding.bottom), );
DateTime xValue = _chartSeriesData[ trackballArgs.chartPointInfo.dataPointIndex!] .x; num yValue = _chartSeriesData[ trackballArgs.chartPointInfo.dataPointIndex!] .y;
CartesianChartPoint point = CartesianChartPoint( xValue.millisecondsSinceEpoch.toDouble(), yValue, ); _dataPointPosition.value = _chartSeriesController.pointToPixel(point);
pointIndex = trackballArgs.chartPointInfo.dataPointIndex!; count.value = count.value + 1; }, onChartTouchInteractionUp: (ChartTouchInteractionArgs tapArgs) { _topEndChartPosition.value = null; _dataPointPosition.value = null; count.value = 0; }, margin: _chartPadding, primaryXAxis: DateTimeAxis(), primaryYAxis: NumericAxis(), series: <CartesianSeries<ChartSampleData, DateTime>>[ LineSeries<ChartSampleData, DateTime>( onRendererCreated: (controller) { _chartSeriesController = controller; }, dataSource: _chartSeriesData, xValueMapper: (ChartSampleData sales, int index) => sales.x, yValueMapper: (ChartSampleData sales, int index) => sales.y, color: Colors.cyan, ), ], trackballBehavior: _trackballBehavior, ), ), ), const SizedBox(height: 10), ValueListenableBuilder( valueListenable: _topEndChartPosition, builder: (BuildContext context, Offset? value, Widget? child) { final RenderBox chartBox = _chartGlobalKey.currentContext! .findRenderObject() as RenderBox; // This is global position. _chartOriginPosition = chartBox.localToGlobal(Offset.zero); return CustomPaint( painter: CirclePainter( topEndChartPosition: _topEndChartPosition.value, dataPointPosition: _dataPointPosition.value, chartOriginPosition: _chartOriginPosition!, globalKey: _chartGlobalKey, chartPadding: _chartPadding, markerFillStyle: PaintingStyle.fill, lineFillStyle: PaintingStyle.fill, ), ); }, ), ValueListenableBuilder( valueListenable: count, builder: (BuildContext context, int? value, Widget? child) { double width = 150; double height = 100; TextStyle textStyle = TextStyle( fontWeight: FontWeight.w700, color: Colors.blueGrey.shade500, fontSize: 14, letterSpacing: 1, ); if (count.value > 0) { return Positioned( left: (_topEndChartPosition.value!.dx + _chartOriginPosition!.dx) - (width / 2) + _chartPadding.left, top: (_topEndChartPosition.value!.dy + _chartOriginPosition!.dy) - height + _chartPadding.bottom, height: height, width: width, child: Container( key: _containerGlobalKey, height: height, width: width, color: Colors.black12, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Padding( padding: const EdgeInsets.all(5.0), child: Text( 'TOTAL', style: textStyle, ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(5.0), child: RichText( text: TextSpan( text: _chartSeriesData[pointIndex].y.toString(), style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 20, color: Colors.black, ), children: [ TextSpan( text: ' steps', style: textStyle, ), ], ), ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(5.0), child: Text( DateFormat('MMM dd, yyyy') .format(_chartSeriesData[pointIndex].x), style: textStyle, ), ), ), ], ), ), ); } else { return Container(); } }, ), ], ), ); } }
class CirclePainter extends CustomPainter { CirclePainter({ required this.topEndChartPosition, required this.dataPointPosition, required this.chartOriginPosition, required this.globalKey, required this.chartPadding, required this.markerFillStyle, required this.lineFillStyle, this.markerColor, this.markerRadius, this.lineColor, this.lineWidth, });
final Offset? topEndChartPosition; final Offset? dataPointPosition; final Offset chartOriginPosition; final GlobalKey? globalKey; final EdgeInsets chartPadding; final PaintingStyle markerFillStyle; final PaintingStyle lineFillStyle; final Color? markerColor; final double? markerRadius; final Color? lineColor; final double? lineWidth;
@override void paint(Canvas canvas, Size size) { if (globalKey != null) { if (topEndChartPosition != null && dataPointPosition != null) { canvas.drawLine( dataPointPosition!.translate( chartOriginPosition.dx + chartPadding.left, chartOriginPosition.dy + chartPadding.top, ), topEndChartPosition!.translate( chartOriginPosition.dx + chartPadding.left, chartOriginPosition.dy + chartPadding.top, ), Paint() ..strokeWidth = lineWidth ?? 3.0 ..color = lineColor ?? Colors.grey ..style = lineFillStyle, ); canvas.drawCircle( topEndChartPosition!.translate( chartOriginPosition.dx + chartPadding.left, chartOriginPosition.dy + chartPadding.top + ((markerRadius ?? 5.0) * 2), ), markerRadius ?? 5.0, Paint() ..color = markerColor ?? Colors.red ..style = markerFillStyle, ); } } }
@override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }
class ChartSampleData { final DateTime x; final num y;
ChartSampleData(this.x, this.y); } |
Snapshot :
Also shared the sample below for your reference and you can modify the sample according to your needs. If you have further queries, please get back to us.
Regards,
Hari Hara Sudhan. K.
Attachment: 185027_4d00e2e2.zip
Hello,
This code doesnt work with version ^24.2.8. I am getting error in CartesianChartPoint(0, rangeChangedArgs.visibleMax) and If I explicitly provide the params CartesianChartPoint(x: 0, y: rangeChangedArgs.visibleMax):, I get type mismatch error at run time. Can you help me with the changes,
I am getting the following error at run time -
Hi Ekta,
We have validated the reported issue and modified the code snippet with 25.1.38. We have shared the code snippet and sample below for your reference. Please let us know if you have any further requirements.
Code Snippet:
|
onActualRangeChanged: (rangeChangedArgs) { if (rangeChangedArgs.orientation == AxisOrientation.horizontal) { _xMin = DateTime.fromMillisecondsSinceEpoch( rangeChangedArgs.visibleMin); } if (rangeChangedArgs.orientation == AxisOrientation.vertical) { CartesianChartPoint<DateTime> point = CartesianChartPoint<DateTime>( x: _xMin, y: rangeChangedArgs.visibleMax, ); yMaxPixelValue = _chartSeriesController.pointToPixel(point).dy; } },
onTrackballPositionChanging: (TrackballArgs trackballArgs) { _topEndChartPosition.value = Offset( trackballArgs.chartPointInfo.markerXPos!, yMaxPixelValue!.toDouble() - (_chartPadding.top + _chartPadding.bottom), ); DateTime xValue = _chartSeriesData[ trackballArgs.chartPointInfo.dataPointIndex!] .x; num yValue = _chartSeriesData[ trackballArgs.chartPointInfo.dataPointIndex!] .y; CartesianChartPoint<DateTime> point = CartesianChartPoint<DateTime>( x: xValue, y: yValue, ); _dataPointPosition.value = _chartSeriesController.pointToPixel(point); pointIndex = trackballArgs.chartPointInfo.dataPointIndex!; count.value = count.value + 1; },
canvas.drawLine( dataPointPosition!.translate( chartOriginPosition.dx - chartPadding.top + 50, chartOriginPosition.dy + chartPadding.bottom, ), topEndChartPosition!.translate( chartOriginPosition.dx - chartPadding.top + 50, chartOriginPosition.dy + chartPadding.bottom, ), linePaint, );
|
Regards,
Lokesh P.
Attachment: forum_185027__e0a3d95c.zip
- 3 Replies
- 3 Participants
-
EK Ekta
- Oct 16, 2023 08:42 PM UTC
- Apr 2, 2024 01:19 PM UTC