The graph points and the matches points are not located in a matched position in case of the player attended in 2-5 matches.

Hi Team 

I wanted to create graph's x leble as i attached in the screen shot please check .if i have 2 data  so graph points taking whole width. 
Please check the screen shot and provide me any solution for this please.


Attachment: Screenshot_20220519_at_12.16.55_PM_fdaeb7e7.zip

4 Replies

VI vikram May 19, 2022 06:49 AM UTC

Team i wanted this type of lebel and graph points.


Attachment: Screenshot_20220519_at_12.11.10_PM_117bcb9a.zip


DD Dharanidharan Dharmasivam Syncfusion Team May 22, 2022 03:48 AM UTC

Hi Vikram,

Greetings from Syncfusion. If you have two points or n number of points, it will occupy the entire plot area, this is default behavior of chart. To achieve your scenario, we request to set the minimum, maximum to the axis. Also you can set the rangePadding as additional, this will add interval at start and end of the axis.

https://help.syncfusion.com/flutter/cartesian-charts/axis-types#applying-padding-to-range

Thanks,

Dharani.



VI vikram June 1, 2022 01:57 PM UTC

Hi Dharani ,

i have tried your solution still not achieved the scenario 
here is my code can you please check and let me know where i am wrong .i have also attached the screen shot what i am getting .

import 'dart:developer';

import 'package:Player160/widgets/common_graph_tooltip.dart';
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 String title;
final dynamic avg;
final int? scrollChageIndex;

CustomeSfCartesianChart(
{Key? key,
required this.data,
required this.type,
required this.title,
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 totalSavetitle = '';
dynamic boxColor = '';
late GlobalKey<State> globalKey;
double? oldAxisVisibleMin, oldAxisVisibleMax;
var mapData;
setUpData() {
mapData =
{
"title": "topSpeedKmh",
"average": 23.012456140350885,
"unit": "",
"data": [
{
"matchId": "01FDYSNHPW6RWG3JEE9W3J1PNJ",
"date": "2021-08-21T21: 00: 00.000Z",
"data": 19.53,
"title": "topSpeedKmh",
"percentage": 0,
"color": Color(0xff82c713),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FXG1HV9ZX84JMHFYP0YXKHWV",
"date": " 2021-09-13T21: 00: 00.000Z",
"data": 25.39,
"title": " topSpeedKmh",
"percentage": 30.00512032770097,
"color": Color(0xff82c713),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FXFF224E0BQ69T0JSPCGZEXB",
"date": "2021-09-13T21: 00: 00.000Z",
"data": 25.39,
"title": "topSpeedKmh",
"percentage": 0.0,
"color": Color(0xffe32326),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FXFAD3TYW5674BFWHE42VRZ0",
"date": "2021-10-04T19: 16: 39.827Z",
"data": 16.82,
"title": "topSpeedKmh",
"percentage": -33.753446238676645,
"color": Color(0xffe32326),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FV02J3WY71H10YKWW14RQP5K",
"date": "2021-12-12T22: 00: 00.000Z",
"data": 16.36,
"title": "topSpeedKmh",
"percentage": -2.7348394768133226,
"color": Color(0xff82c713),
"opponentTeamLogo":
"https: //api.staging.track160.com/api/v0/data/account-01FV01SA2TFRZVZSAMAB44RY4N/media/890ad535-041d-415f-a48b-adf9e9d71ee6",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0
},
{
"matchId": "01FTDZGMXT4NDN0FRHH3QQ60M3",
"date": "2021-12-12T22: 00: 00.000Z",
"data": 16.36,
"title": "topSpeedKmh",
"percentage": 0.0,
"color": Color(0xff82c713),
"opponentTeamLogo":
"https: //api.staging.track160.com/api/v0/data/account-01FT8143DFTR1FPHTA0TK6JTK7/media/652724fc-96bf-4c2d-83e2-512a46ae01a2",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0
},
{
"matchId": "01FV7HT34CR7YHEZ16QY1P995W",
"date": "2021-12-20T22: 00: 00.000Z",
"data": 23.75,
"title": "topSpeedKmh",
"percentage": 45.17114914425429,
"color": Color(0xff82c713),
"opponentTeamLogo":
"https: //api.staging.track160.com/api/v0/data/account-01FV01SA2TFRZVZSAMAB44RY4N/media/9fd8b147-dd41-43f9-8e2b-af86fddf3560",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0
},
{
"matchId": "01FTZ791ZXA9VX7TNK741N9H84",
"date": "2021-12-20T22: 00: 00.000Z",
"data": 23.75,
"title": "topSpeedKmh",
"percentage": 0.0,
"color": Color(0xffe32326),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FX800K4WYFV7K3E3NZYKH87S",
"date": "2022-01-02T22: 00: 00.000Z",
"data": 23.1,
"title": "topSpeedKmh",
"percentage": -2.7368421052631517,
"color": Color(0xff82c713),
"opponentTeamLogo":
"https: //api.staging.track160.com/api/v0/data/account-01FV01SA2TFRZVZSAMAB44RY4N/media/4cb53661-2de2-434b-84c0-396ec5d7db1c",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0
},
{
"matchId": "01FY1MJN2E1B7GEN28CR7SB8GY",
"date": "2022-01-02T22: 00: 00.000Z",
"data": 23.1,
"title": "topSpeedKmh",
"percentage": 0.0,
"color": Color(0xff82c713),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01FXQB2HRNKW437BX2TX27AKVH",
"date": "2022-01-21T22: 00: 00.000Z",
"data": 28.99,
"title": "topSpeedKmh",
'percentage': 25.497835497835485,
"color": Color(0xffe32326),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
{
"matchId": "01G00CRPZAWK05P24KHHHZZKGS",
"date": "2022-01-31T10: 00: 00Z",
"data": 28.85,
"title": "topSpeedKmh",
"percentage": -0.4829251466022664,
"color": Color(0xffe32326),
"opponentTeamLogo": "",
"totalShots": 0,
"accuratePasses": 0,
"totalPasses": 0,
"placeholderImage": "assets/images/[email protected]"
},
]
}
as Map;
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;
//for display all data
// 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.toStringAsFixed(1).contains('.0') ? personalMetricData[selectIndex].percentage.toStringAsFixed(1).split('.0')[0] : personalMetricData[selectIndex].percentage.toStringAsFixed(1)}%';
boxColor = personalMetricData[selectIndex].percentage.isNegative
? AppColors.toolTipRed
: AppColors.parrotGreen;
headerValue = personalMetricData[selectIndex].data.runtimeType == int
? '${personalMetricData[selectIndex].data}'
: '${personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}';
suffixValue = widget.type;
if (widget.title == 'totalSaves') {
totalSavetitle =
'${personalMetricData[selectIndex].data.runtimeType == int ? personalMetricData[selectIndex].data : personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}/${personalMetricData[selectIndex].data.runtimeType == int ? personalMetricData[selectIndex].totalShots : personalMetricData[selectIndex].totalShots.toDouble().toStringAsFixed(1)}';
}
if (widget.title == 'passAccuracy') {
totalSavetitle =
'${personalMetricData[selectIndex].accuratePasses.runtimeType == int ? personalMetricData[selectIndex].accuratePasses : personalMetricData[selectIndex].accuratePasses.toDouble().toStringAsFixed(1)}/${personalMetricData[selectIndex].totalPasses.runtimeType == int ? personalMetricData[selectIndex].totalPasses : personalMetricData[selectIndex].totalPasses.toDouble().toStringAsFixed(1)}';
}
//for display all data
// personalMetricData;
personalMetricData = personalMetricData.length > 6
? personalMetricData.sublist(
personalMetricData.length - 6, personalMetricData.length)
: personalMetricData;
});
});
}
}

@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.toStringAsFixed(1).contains('.0') ? personalMetricData[selectIndex].percentage.toStringAsFixed(1).split('.0')[0] : personalMetricData[selectIndex].percentage.toStringAsFixed(1)}%';
boxColor = personalMetricData[selectIndex].percentage.isNegative
? AppColors.toolTipRed
: AppColors.parrotGreen;
headerValue = personalMetricData[selectIndex].data.runtimeType == int
? '${personalMetricData[selectIndex].data}'
: '${personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}';
suffixValue = widget.type;
if (widget.title == 'totalSaves') {
totalSavetitle =
'${personalMetricData[selectIndex].data.runtimeType == int ? personalMetricData[selectIndex].data : personalMetricData[selectIndex].data.toDouble().toStringAsFixed(1)}/${personalMetricData[selectIndex].data.runtimeType == int ? personalMetricData[selectIndex].totalShots : personalMetricData[selectIndex].totalShots.toDouble().toStringAsFixed(1)}';
}
if (widget.title == 'passAccuracy') {
totalSavetitle =
'${personalMetricData[selectIndex].accuratePasses.runtimeType == int ? personalMetricData[selectIndex].accuratePasses : personalMetricData[selectIndex].accuratePasses.toDouble().toStringAsFixed(1)}/${personalMetricData[selectIndex].totalPasses.runtimeType == int ? personalMetricData[selectIndex].totalPasses : personalMetricData[selectIndex].totalPasses.toDouble().toStringAsFixed(1)}';
}
});
});
}
}

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;
});
}
});
}
}

chartTouchInteractionDown(ChartTouchInteractionArgs args) {
if (showTooptip) {
setState(() {
showTooptip = false;
});
} else {
return null;
}
}

@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.18,
width: MediaQuery.of(context).size.width * 0.8,
child: personalMetricData.isNotEmpty
? Stack(
children: [
Padding(
padding: const EdgeInsets.only(top: 5.0),
child: SfCartesianChart(
onChartTouchInteractionDown:
(ChartTouchInteractionArgs args) =>
chartTouchInteractionDown(args),
plotAreaBorderWidth: 0,
//for display all data
// plotAreaBackgroundColor: AppColors.red,
// 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: DateTimeAxis(
rangePadding: ChartRangePadding.normal,
dateFormat: DateFormat.MMMd(),
minimum: mapData['data'].isNotEmpty?DateTime.parse(mapData['data'][0]['date']):DateTime(2022,12,30),
maximum: mapData['data'].isNotEmpty?DateTime.parse(mapData['data'][mapData['data'].length-1]['date']):DateTime(2024,12,30),
//for display all data
// interval: 1.0,
// 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.outside,
axisLine: const AxisLine(
color: AppColors.white,
width: 0,
)),
primaryYAxis: NumericAxis(
minimum: 0.0,
isVisible: false,
plotBands: <PlotBand>[
PlotBand(
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, DateTime>(
// 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(
//adding (MediaQuery.of(context).size.width * 0.15) for MA-370
// MediaQuery.of(context).size.width * 0.07
left: selectIndex == personalMetricData.length - 6
? MediaQuery.of(context).size.width * 0.14
: 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() - 45
: dy.toDouble() - 45),
child: showTooptip
? CommonGraphTooltip(
extraTitle: totalSavetitle,
boxColor: boxColor,
changePercentage: subtitle,
data: headerValue,
unit: suffixValue,
title: widget.title,
)
: 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, this.totalShots,
this.accuratePasses, this.totalPasses);

final DateTime date;
final dynamic data;
final dynamic percentage;
final dynamic color;
final dynamic totalShots;
final dynamic accuratePasses;
final dynamic totalPasses;

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


Attachment: Simulator_Screen_Shot__iPhone_11__20220601_at_07.25.58.png_e4ea26f7.zip


YG Yuvaraj Gajaraj Syncfusion Team June 2, 2022 03:11 PM UTC

Hi Vikram,


In your sample, you have achieved your requirement with the help of setting visibleMinimum and visibleMaximum then you get the empty space in the front part of the series. For removing the axis label we suggest you the axisLabelFormatter callback and return the empty string to remove the axis label. We have attached the code snippet below to remove the axis label using axisLabelFormatter.


Code snippet:

primaryXAxis: DateTimeAxis(

  axisLabelFormatter: (args){

    if(/*Condition*/){

      return ChartAxisLabel('', args.textStyle);

    }

    else {

      return ChartAxisLabel(args.text, args.textStyle);

    }

  }

),


Regards,

Yuvaraj.


Loader.
Up arrow icon