dificult to click the marker in graph

I am using SfCartesianChart but facing issue when click on the marker .it is very difficult to click on the marker ,but for last and first marker it is only clickable when we click on left side of the marker 


6 Replies

YG Yuvaraj Gajaraj Syncfusion Team April 22, 2022 02:49 PM UTC

Hi Vikram,


Greetings from Syncfusion. We ensured your scenario, but the tapping on the marker works fine at all points. We are not sure what type of series you have used and the marker size you have used. We suspect that to show the tooltip you might have tapped on the point (marker render above the point). To ensure this we have used the onPointTap callback in the series. Also, we request you to ensure your scenario with the latest version of the chart widget.


Code snippet:

LineSeries<ChartSampleData, DateTime>(

  onPointTap: (args) {

   print('Tapped on point index: ' + args.pointIndex.toString());

  },

  /// Other required properties

)


UG: https://help.syncfusion.com/flutter/cartesian-charts/callbacks#onpointtap


We have also attached a sample below for your reference. If you still face any concerns kindly get back to us by replicating your scenario in the sample below.


Regards,

Yuvaraj.


Attachment: f174543_b6f37c20.zip


VI vikram April 25, 2022 04:48 AM UTC

Hi Yuvaraj,

 i am using tooltip render for showing custom tooltip .But it is very difficult to click the last marker of the series 
here is my code Please let me know the solution for this

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:Player160/constants/app_colors.dart';
import 'package:Player160/constants/assest_path.dart';

class CustomeSfCartesianChart extends StatefulWidget {
final Object data;
final String type;
final dynamic avg;
final int? scrollChageIndex;

CustomeSfCartesianChart(
{Key? key,
required this.data,
required this.type,
this.avg,
this.scrollChageIndex})
: super(key: key);
@override
_CustomeSfCartesianChartState createState() =>
_CustomeSfCartesianChartState();
}

class _CustomeSfCartesianChartState extends State<CustomeSfCartesianChart> {
late TooltipBehavior _tooltipBehavior;
dynamic selectIndex = 0;
bool showTooptip = false;
bool callOnce = true;
bool showlastTooptip = false;
dynamic dx = 0;
dynamic dy = 0;
dynamic dataX = 0;
dynamic dataY = 0;
List<SalesData> personalMetricData = [];
dynamic headerValue = '';
dynamic suffixValue = '';
dynamic subtitle = '';
dynamic boxColor = '';
late GlobalKey<State> globalKey;
double? oldAxisVisibleMin, oldAxisVisibleMax;

setUpData() {
var mapData = widget.data as Map;
print('Data item2${widget.data}');
personalMetricData.clear();
for (Map<String, dynamic> i in mapData['data']) {
personalMetricData.add(SalesData.fromJson(i));
}

// Key which uses to access the chart state.
globalKey = GlobalKey<State>();
if (personalMetricData.isNotEmpty) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
setState(() {
callOnce = true;
oldAxisVisibleMin = personalMetricData.length > 6
? personalMetricData.length - 6.toDouble()
: 0.toDouble();
oldAxisVisibleMax = personalMetricData.length == 1
? 1
: personalMetricData.length - 1.toDouble();
showTooptip = true;
showlastTooptip = true;
dataX = 300.0;
dataY = 50.0;
selectIndex = personalMetricData.length - 1;
subtitle =
'${personalMetricData[selectIndex].percentage.toDouble().toStringAsFixed(1)}%';
boxColor = personalMetricData[selectIndex].percentage.isNegative
? AppColors.toolTipRed
: AppColors.parrotGreen;
headerValue =
'${personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}';
suffixValue = widget.type;
personalMetricData;
// '${widget.type == 'topSpeed' ? 'km/h' : widget.type == 'totalDistance' ? 'km' : widget.type == 'possesion' ? '%' : 'min'}';
});
});
}
}

@override
void initState() {
_tooltipBehavior = TooltipBehavior(enable: true);
super.initState();
setUpData();
}

@override
void didUpdateWidget(CustomeSfCartesianChart oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.data != widget.data) {
setUpData();
}
}

tool(TooltipArgs args) {
if (personalMetricData.isNotEmpty) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
setState(() {
showTooptip = true;
showlastTooptip = false;
dx = args.locationX;
dy = args.locationY;
selectIndex = args.pointIndex;
subtitle =
'${personalMetricData[selectIndex].percentage.toInt().toDouble().toStringAsFixed(1)}%';
boxColor = personalMetricData[selectIndex].percentage.isNegative
? AppColors.toolTipRed
: AppColors.parrotGreen;
headerValue =
'${personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}';
suffixValue = widget.type;
// '${widget.type == 'topSpeed' ? 'km/h' : widget.type == 'totalDistance' ? 'km' : widget.type == 'possesion' ? '%' : 'min'}';
});
});
}
}

markerRender(MarkerRenderArgs args) {
if (personalMetricData.isNotEmpty) {
dynamic markerIndex = args.pointIndex;
if (personalMetricData[markerIndex].percentage.isNegative) {
args.color = AppColors.toolTipRed;
args.borderWidth = 0.0;
args.markerHeight = 12.0;
args.markerWidth = 12.0;
args.borderColor = AppColors.toolTipRed;
} else {
args.color = AppColors.parrotGreen;
args.markerHeight = 12.0;
args.markerWidth = 12.0;
args.borderWidth = 0.0;
args.borderColor = AppColors.parrotGreen;
}
}
}

getfirstmarkerPoint(double x, double y) {
if (callOnce) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
if (personalMetricData.isNotEmpty) {
setState(() {
dataX = x;
dataY = y;
callOnce = false;
});
}
});
}
}

@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.2,
width: MediaQuery.of(context).size.width * 0.8,
child: personalMetricData.isNotEmpty
? Stack(
children: [
SfCartesianChart(
onChartTouchInteractionDown:
(ChartTouchInteractionArgs args) {
setState(() {
showTooptip = false;
});
},
plotAreaBorderWidth: 0,
onActualRangeChanged: (ActualRangeChangedArgs args) {
if (args.orientation == AxisOrientation.horizontal) {
oldAxisVisibleMin = args.visibleMin.toDouble();
oldAxisVisibleMax = args.visibleMax.toDouble();
}
},
// enableAxisAnimation: true,
zoomPanBehavior: ZoomPanBehavior(
// Enables pinch zooming
enablePanning: true,
),
primaryXAxis: CategoryAxis(
interval: 1,
visibleMinimum: oldAxisVisibleMin,
visibleMaximum: oldAxisVisibleMax,
majorGridLines: const MajorGridLines(
width: 0,
color: AppColors.white,
),
majorTickLines: const MajorTickLines(
width: 0,
color: AppColors.white,
),
labelStyle: const TextStyle(
fontSize: 10,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: AppColors.black),
labelPosition: ChartDataLabelPosition.outside,
edgeLabelPlacement: EdgeLabelPlacement.none,
labelAlignment: LabelAlignment.center,
labelPlacement: LabelPlacement.onTicks,
arrangeByIndex: true,
tickPosition: TickPosition.inside,
axisLine: const AxisLine(
color: AppColors.white,
width: 0,
)),
primaryYAxis: NumericAxis(
isVisible: false,
rangePadding: ChartRangePadding.normal,
plotBands: <PlotBand>[
PlotBand(
textStyle: const TextStyle(
color: AppColors.grey8,
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400),
// horizontalTextPadding: '-50%',
textAngle: 0,
isVisible: true,
start: widget.avg.toInt(),
end: widget.avg.toInt(),
borderWidth: 0.5,
borderColor: AppColors.grey8,
),
],
),
tooltipBehavior: TooltipBehavior(
canShowMarker: true,
enable: true,
),
onTooltipRender: (TooltipArgs args) => tool(args),
// Callback to customize the marker
onMarkerRender: (MarkerRenderArgs args) =>
markerRender(args),
series: <ChartSeries>[
LineSeries<SalesData, String>(
onCreateRenderer:
(ChartSeries<dynamic, dynamic> series) {
return CustomLineSeriesRenderer(
series, getfirstmarkerPoint);
},
enableTooltip: true,
markerSettings: const MarkerSettings(shape:DataMarkerType.circle,
isVisible: true, width: 12.0, height: 12.0),
dataSource: personalMetricData,
pointColorMapper: (SalesData sales, _) =>
sales.color,
xValueMapper: (SalesData sales, _) => sales.date,
yValueMapper: (SalesData sales, _) =>
sales.data.toInt())
]),
Positioned(
left: selectIndex == personalMetricData.length - 6
? MediaQuery.of(context).size.width * 0.07
: selectIndex == 0
? MediaQuery.of(context).size.width * 0.15
: 0.0,
child: Transform.translate(
offset: Offset(
showlastTooptip
? dataX.toDouble() - 65
: dx.toDouble() - 65,
showlastTooptip
? dataY.toDouble() - 40
: dy.toDouble() - 40),
child: showTooptip
? Stack(
children: <Widget>[
Container(
child: selectIndex == 0
? Image.asset(
AssetPath.bubbleLeft,
color: boxColor,
width: MediaQuery.of(context)
.size
.width *
0.22,
fit: BoxFit.contain,
)
: Image.asset(
AssetPath.bubbleRight,
color: boxColor,
width: MediaQuery.of(context)
.size
.width *
0.22,
fit: BoxFit.contain,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
top: 3.0, left: 15.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Text('$headerValue',
textScaleFactor: 1.0,
style: TextStyle(
color: boxColor ==
AppColors
.parrotGreen
? AppColors.black
: AppColors.white,
fontSize: 14.0,
fontFamily: 'Lato',
fontWeight:
FontWeight.w400)),
Padding(
padding: const EdgeInsets.only(
left: 5.0),
child: Text(
'$suffixValue',
textScaleFactor: 1.0,
style: TextStyle(
color: boxColor ==
AppColors
.parrotGreen
? AppColors.black
: AppColors.white,
fontSize: 10.0,
fontFamily: 'Lato',
fontWeight:
FontWeight.w400),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 3.0, left: 15.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Icon(
boxColor == AppColors.parrotGreen
? Icons.arrow_upward
: Icons
.arrow_downward_outlined,
size: 13,
color: boxColor ==
AppColors.parrotGreen
? AppColors.black
: AppColors.white,
),
Text(
'($subtitle)',
textScaleFactor: 1.0,
style: TextStyle(
color: boxColor ==
AppColors.parrotGreen
? AppColors.black
: AppColors.white,
fontSize: 12.0,
fontFamily: 'Lato',
fontWeight: FontWeight.w400),
),
],
),
),
],
),
],
)
: Container(
color: AppColors.white,
),
))
],
)
: Container(
color: AppColors.white,
));
}
}

class CustomLineSeriesRenderer extends LineSeriesRenderer {
CustomLineSeriesRenderer(this.series, this.callBack);
ChartSeries<dynamic, dynamic> series;
final Function callBack;
@override
void drawDataMarker(int index, Canvas canvas, Paint fillPaint,
Paint strokePaint, double pointX, double pointY,
[CartesianSeriesRenderer? seriesRenderer]) {
if (index == series.dataSource!.length - 1) {
callBack(pointX, pointY);
}
else if(series.dataSource!.length - 1>5 && (index+(series.dataSource!.length- 1)-5) == series.dataSource!.length - 1){
callBack(pointX, pointY);
}
super.drawDataMarker(
index, canvas, fillPaint, strokePaint, pointX, pointY, seriesRenderer);
}
}

class SalesData {
SalesData(
this.date,
this.data,
this.percentage,
this.color,
);

final String date;
final dynamic data;
final dynamic percentage;
final dynamic color;

factory SalesData.fromJson(Map<String, dynamic> dummydata) {
return SalesData(
DateFormat.yMMMd()
.format(DateTime.parse(dummydata['date']))
.toString()
.split(',')[0],
// dummydata['date'].toString().split(',')[0],
dummydata['data'],
dummydata['percentage'],
dummydata['color'],
// dummydata[]
);
}
}



VI vikram April 25, 2022 04:58 AM UTC

if i  will use this i didn't get the locationX and locationY of the  the marker .i need locationX and locationY  data to display my custom tooltip



SK Sriram Kiran Senthilkumar Syncfusion Team April 27, 2022 04:07 PM UTC

Hi Vikram,


We tried to make the provided sample and runnable and on further checking with it we found that, the custom tooltip is getting rendered on tapping the last data point marker in the chart and to confirm from our side, we have also attached the screen recording below for reference. As we are not sure on exact scenario you have reported in your query, we kindly request you to share us with more information in detail regarding your requirement and if possible, kindly explain to us what you trying to achieve in detail so that it will be helpful in further analysis to provide the solutions sooner.


Regards,

Sriram Kiran


Attachment: screenrecording_7588f5d6.zip


VI vikram April 28, 2022 04:57 AM UTC

Hi Please check the screen recording when you click on the last marker and first market  .first time the pop was not opening  because you click on the right side of last marker and left side of first market .this is the issue i want to resolve .why it is not working if we click on the right side of the last marker and left side of the first marker.



YG Yuvaraj Gajaraj Syncfusion Team April 29, 2022 11:12 AM UTC

Hi Vikram,


We have analyzed your query, and we would like to let you know that the tapping will get triggered when tapping inside the plot area only. It will not get triggered when tapping outside of the plot area. This is why when you click the right side of the last marker and the left side of the first marker, the event will not get triggered. To know the plot area of the chart, we have given a different color to specify the areas. In the below-attached screen record, we have given yellow color to the chart area and inside the yellow region is the plot area which is white-colored. This is our default behavior of the chart.


Regards,

Yuvaraj.


Attachment: screen_record_857a37d7.zip

Loader.
Up arrow icon