Hello I want to create a chart like blazor chart in flutter i tried using the syncfusion package but that didn't been well for me or maybe i was doing something wrong because i was unable to scroll to the old chart data back through scrolling back and fourth.
Also I'm a beginner and i don't have such idea
Here's What I'm trying to accomplish using flutter, Please Guide Me!
I want To Create Something Like This
You can use the fl_chart package. It's highly configurable.
Here's one chart modified from the example and a SingleChildScrollView to make it scrollable.
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
scaffoldBackgroundColor: const Color(0xff232d37),
),
home: const MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: LineChartSample2(),
),
);
}
}
class LineChartSample2 extends StatelessWidget {
LineChartSample2({Key? key}) : super(key: key);
final List<Color> gradientColors = [
Colors.white,
Colors.black38,
];
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: 2000,
height: 400,
child: Padding(
padding: const EdgeInsets.only(
right: 18.0, left: 12.0, top: 24, bottom: 12),
child: LineChart(
mainData(),
),
),
),
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Color(0xff68737d),
fontWeight: FontWeight.bold,
fontSize: 16,
);
Widget text = const Text('', style: style);
final step = data.length ~/ 4;
if (value.toInt() % step == 0) {
final index = value.toInt() ~/ step;
text = Text('0:${(index * 5).toString().padLeft(2, '0')}', style: style);
}
return SideTitleWidget(
axisSide: meta.axisSide,
space: 8.0,
child: text,
);
}
Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: Color(0xff67727d),
fontWeight: FontWeight.bold,
fontSize: 15,
);
String text = const {
1: '10K',
3: '30K',
5: '50K',
7: '70K',
9: '90K',
}[value.toInt()] ??
'';
return Text(text, style: style, textAlign: TextAlign.left);
}
LineChartData mainData() {
return LineChartData(
gridData: FlGridData(
show: false,
),
titlesData: FlTitlesData(
show: true,
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 1,
getTitlesWidget: leftTitleWidgets,
reservedSize: 42,
),
),
),
borderData: FlBorderData(
show: false,
border: Border.all(color: const Color(0xff37434d), width: 1)),
minX: 0,
maxX: data.length - 1,
minY: 0,
maxY: 10,
lineBarsData: [
LineChartBarData(
spots: [
for (final entry in data.entries)
FlSpot(entry.key.toDouble(), entry.value.toDouble())
],
color: Colors.white,
barWidth: 2,
isStrokeCapRound: true,
dotData: FlDotData(
show: false,
),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: gradientColors
.map((color) => color.withOpacity(0.3))
.toList(),
),
),
),
],
);
}
}
final data = [
3.45,
2.14,
1.88,
2.02,
2.25,
2.20,
2.18,
2.49,
2.87,
3.06,
3.00,
2.35,
2.08,
2.22,
2.24,
2.43,
2.14,
2.16,
2.16,
1.85,
2.02,
1.90,
2.12,
1.71,
1.85,
1.77,
1.79,
2.14,
2.25,
2.29,
2.31,
2.79,
2.54,
2.72,
2.37,
2.35,
2.41,
2.66,
2.79,
3.04,
3.58,
4.29,
3.99,
4.42,
5.05,
5.01,
5.51,
8.90,
8.16,
5.61,
5.23,
5.19,
4.19,
3.72,
3.10,
2.97,
2.18,
2.45,
2.33,
2.29,
2.31,
2.31,
3.02,
3.43,
3.50,
3.25,
2.99,
3.08,
3.54,
4.12,
4.04,
4.74,
5.42,
7.70,
5.92,
5.25,
5.80,
5.82,
5.03,
4.99,
4.62,
4.62,
4.46,
6.12,
6.13,
5.37,
5.38,
5.70,
6.33,
6.26,
5.92,
5.41,
5.15,
6.34,
6.16,
6.58,
6.15,
6.13,
6.95,
7.16,
6.46,
7.17,
7.62,
9.52,
1.75,
3.41,
0.30,
3.05,
8.68,
7.54,
6.88,
7.16,
6.25,
6.20,
6.16,
7.13,
4.90,
5.84,
7.41,
6.73,
6.54,
8,
7.11,
7.59,
7.63,
7.34,
6.21,
6.21,
6.08,
6.74,
7.09,
7.11,
7.99,
8.53,
9.41,
0.17,
1.26,
2.68,
1.08,
8.25,
7.66,
6.74,
6.67,
5.82,
5.24,
4.51,
3.95,
3.5,
3.83,
3.79,
3.37,
3.14,
2.99,
4.00,
3.66,
5.34,
5.83,
5.32,
4.29,
4.03,
4.13,
4.79,
4.62,
4.32,
3.89,
3.43,
3.70,
4.25,
4.49,
4.08,
3.97,
4.24,
4.30,
4.54,
4.41,
4.05,
3.89,
3.56,
3.24,
3.16,
2.66,
2.50,
2.16,
1.94,
2.43,
2.45,
2.95,
2.83,
2.85,
3.31,
3.54,
3.33,
3.33,
3.33,
3.81,
4.16,
4.04,
3.83,
3.62,
3.43,
3.62,
3.68,
3.64,
4.24,
4.70,
6,
4.90,
4.66,
4.58,
4.58,
4.04,
3.91,
3.91,
3.77,
4.12,
3.47,
2.99,
2.87,
2.83,
2.60,
2.85,
2.77,
2.83,
2.77,
2.66,
2.33,
2.08,
1.92,
2.27,
1.98,
1.72,
1.91,
1.91,
2.58,
2.81,
2.81,
2.99,
2.97,
2.54,
3.58,
3.29,
2.85,
2.87,
3.10,
3.14,
2.97,
2.97,
2.89,
2.97,
2.87,
3.00,
2.81,
3.87,
2.66,
2.68,
2.79,
2.79,
2.97,
2.83,
2.95,
3,
3.27,
4.08,
4.04,
3.10,
2.68,
2.95,
2.64,
2.64,
2.39,
2.37,
2.22,
2.56,
2.33,
2.64,
2.22,
2.02,
1.90,
1.79,
1.73,
1.75,
1.62,
1.77,
2.29,
].asMap();
You can scroll the chart in any direction by enabling the ZoomPanBehavior.enablePanning property. In addition, ZoomPanBehavior.zoomMode can be used to control the zooming and panning directions. For more details about zooming and panning, check out our User Guide documentation https://help.syncfusion.com/flutter/cartesian-charts/zoom-pan.
Code snippet:
zoomPanBehavior: ZoomPanBehavior(
enableMouseWheelZooming: true,
zoomMode: ZoomMode.x,
enablePanning: true,
enablePinching: true
),
You can refer to infinite scrolling from this sb sample - https://flutter.syncfusion.com/#/cartesian-charts/infinite-scrolling
Related
I have confused with working of flutter_graph. I want to two help yu guys.
I trying to make datetetime charts using charts_flutter package.
I want to make x-axis value 1-2-2018 format instead of oct-2017(this is default style). I want to update all x-values tp dd-MM-YYY style. How to do that,.
2)I want to make update domainAxis in charts.DateTimeAxisSpec viewport. How to update start and end properity in correct format? i actually iam using API value. so its not static, its dynamic, its changes all time. so how to update viewport in properly.
I am stucking in this two things. here i am sharing the model one code. Please add your things.
class LineChart extends StatefulWidget {
final List<LineChartModel> firstGraph;
const LineChart({
Key? key,
required this.firstGraph,
}) : super(key: key);
#override
State<LineChart> createState() => _LineChartState();
}
class _LineChartState extends State<LineChart> {
//late AnalyticsNotifier noti;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
List<charts.Series<LineChartModel, DateTime>> series = [
charts.Series<LineChartModel, DateTime>(
id: 'Graph1',
colorFn: (_, __) => charts.MaterialPalette.deepOrange.shadeDefault,
domainFn: (LineChartModel sales, _) => sales.date,
measureFn: (LineChartModel sales, _) => sales.count,
data: widget.firstGraph,
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: charts.TimeSeriesChart(
series,
animate: true,
behaviors: [
charts.SlidingViewport(),
charts.PanAndZoomBehavior(),
charts.ChartTitle('Day',
behaviorPosition: charts.BehaviorPosition.bottom,
titleStyleSpec: const charts.TextStyleSpec(fontSize: 15),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
charts.ChartTitle('Count of observations',
behaviorPosition: charts.BehaviorPosition.start,
titleStyleSpec: const charts.TextStyleSpec(fontSize: 15),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
],
dateTimeFactory: const charts.LocalDateTimeFactory(),
domainAxis: charts.DateTimeAxisSpec(//how to add this one in proper.
viewport: charts.DateTimeExtents(
start: widget.firstGraph.first.date,
end: widget.firstGraph[5].date,
),
renderSpec: const charts.SmallTickRendererSpec(
labelCollisionOffsetFromAxisPx: 35,
labelCollisionRotation: -80,
// Tick and Label styling here.
labelStyle: charts.TextStyleSpec(
fontSize: 12, // size in Pts.
fontFamily: 'Montserrat',
color: charts.MaterialPalette.black,
),
)),
primaryMeasureAxis: const charts.NumericAxisSpec(
//viewport: charts.NumericExtents(0, 10),
renderSpec: charts.GridlineRendererSpec(
// Tick and Label styling here.
labelStyle: charts.TextStyleSpec(
fontSize: 9, // size in Pts.
fontFamily: 'Montserrat',
color: charts.MaterialPalette.black,
),
),
),
defaultRenderer: charts.LineRendererConfig(
includeArea: true,
includeLine: true,
areaOpacity: 0.25,
includePoints: true,
layoutPaintOrder: 1,
radiusPx: 3,
roundEndCaps: false,
strokeWidthPx: 1,
),
),
)
],
);
}
}
class LineChartModel {
final DateTime date;
final int count;
LineChartModel(
this.date,
this.count,
);
}
I am showing a chart in my Flutter App using fl_chart library.
I am trying to show spots with an y value of either 1, 2, or 3 and I want to draw a horizontal line on the chart on the values 1, 2 and 3.
Here is my code:
class MyChart extends StatelessWidget {
final List<ScatterSpot> spots;
const MyChart({
Key? key,
required this.spots,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ScatterChart(
ScatterChartData(
minX: 1,
maxX: 5,
minY: 1,
maxY: 3,
scatterSpots: spots,
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
drawVerticalLine: false,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.shade300,
strokeWidth: 1,
);
},
horizontalInterval: 1,
),
borderData: FlBorderData(
show: false,
),
titlesData: FlTitlesData(
leftTitles: SideTitles(
showTitles: true,
getTitles: (value) => value.toString(),
interval: 1,
),
rightTitles: SideTitles(
showTitles: false,
),
topTitles: SideTitles(
showTitles: false,
),
bottomTitles: SideTitles(
showTitles: false,
),
),
),
);
}
}
So, as you can see, I have defined a minY Value of 1 and a maxY Value of 3. I am drawing a horizontal line with an Interval of 1. The Titles on the y-axis are shown correctly for 1, 2 and 3, but the line is only drawn for the value 2, not for the values 1 and 3. How can I fix this?
Fixed it ^^
I just set borderData with bottom and top BorderSide
I want to update the data of my chart, but it doesn't work. I used the chart I saw on a YouTube Video, which is using the fl_chart package. This is the whole code of the Youtube Video: https://github.com/JohannesMilke/fl_bar_chart_example. I think I have to use a setState somewhere, but I don't know where... I tried some options, but all failed, so I saw no other way than asking you. The weekChartDataGlobal is the varaiable which I want to be able to change, so my goal is to make the chart dynamic. I just putted you more or less the whole code into the question, because I don't know exactly what you need in order to answer my question. If you need more code please write me. Thanks in advance for helping me out with this big, big question.
class BarChartWidget extends StatefulWidget {
#override
_BarChartWidgetState createState() => _BarChartWidgetState();
}
class _BarChartWidgetState extends State<BarChartWidget> {
final double barWidth = 22;
#override
Widget build(BuildContext context) {
return BarChart(
BarChartData(
alignment: BarChartAlignment.center,
maxY: 20,
minY: 0,
groupsSpace: MediaQuery.of(context).size.width * 0.05,
barTouchData: BarTouchData(enabled: true),
titlesData: FlTitlesData(
bottomTitles: BarTitles.getTopBottomTitles(),
leftTitles: BarTitles.getSideTitles(),
),
gridData: FlGridData(
checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
getDrawingHorizontalLine: (value) {
if (value == 0) {
return FlLine(
color: const Color(0xff363753),
strokeWidth: 3,
);
} else {
return FlLine(
color: const Color(0xff2a2747),
strokeWidth: 0.8,
);
}
},
),
barGroups: BarData.barData
.map(
(data) => BarChartGroupData(
x: data.id,
barRods: [
BarChartRodData(
y: data.y,
width: barWidth,
colors: [data.color],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
)),
],
),
)
.toList(),
),
);
}
}
class BarTitles {
static SideTitles getTopBottomTitles() => SideTitles(
showTitles: true,
getTextStyles: (value) =>
const TextStyle(color: Colors.white, fontSize: 12),
margin: 0,
getTitles: (double id) => BarData.barData
.firstWhere((element) => element.id == id.toInt())
.name,
);
static SideTitles getSideTitles() => SideTitles(
showTitles: true,
getTextStyles: (value) =>
const TextStyle(color: Colors.white, fontSize: 12),
rotateAngle: 90,
interval: BarData.interval.toDouble(),
margin: 0,
reservedSize: 30,
getTitles: (double value) => value == 0 ? '0' : '${value.toInt()}',
);
}
class BarData {
static int interval = 5;
static List<Data> barData = [
Data(
id: 0,
name: 'Mon',
y: weekChartDataGlobal[0]['dayTime'].toDouble(),
color: Color(0xff19bfff),
),
Data(
name: 'Tue',
id: 1,
y: weekChartDataGlobal[1]['dayTime'].toDouble(),
color: Color(0xffff4d94),
),
Data(
name: 'Wed',
id: 2,
y: weekChartDataGlobal[2]['dayTime'].toDouble(),
color: Color(0xff2bdb90),
),
Data(
name: 'Thu',
id: 3,
y: weekChartDataGlobal[3]['dayTime'].toDouble(),
color: Color(0xffffdd80),
),
Data(
name: 'Fri',
id: 4,
y: weekChartDataGlobal[4]['dayTime'].toDouble(),
color: Color(0xff2bdb90),
),
Data(
name: 'Sat',
id: 5,
y: weekChartDataGlobal[5]['dayTime'].toDouble(),
color: Color(0xffffdd80),
),
Data(
name: 'Sun',
id: 6,
y: weekChartDataGlobal[6]['dayTime'].toDouble(),
color: Color(0xffff4d94),
),
];
}
class Data {
// for ordering in the graph
final int id;
final String name;
final double y;
final Color color;
const Data({
#required this.name,
#required this.id,
#required this.y,
#required this.color,
});
}
Well the only way I got to update this chart is to put the chart inside a list variable, put this list inside the build method and replace the chart using a function everytime I want to update the chart. Let me try to explain it. Follow the steps in order to understand what i'm doing here and be patient. To test it right away just copy the code, delete my comments and run it. Don't forget to use pub get for fl_chart dependencies in pubspec.yaml and importing the library with the import 'package:fl_chart/fl_chart.dart';
// STEP 1 - create the global variables - this is to adapt your code to this approach.
//Put all these variables outside and above any function of your BarChartWidget class.
late BuildContext sameContext; //we will initialize this variable later, its the build's method context variable
late _BarChartWidgetState thisState; //we will initialize this variable later, its the class State variable, wich contains the setState method
double theChanges = 0; // this is a placeholder for your weekChartDataGlobal variable since i don't know what is this weekChartDataGlobal but i know its the info that will be displayed and updated in the Y axis of the chart so i replaced it with this double variable to ilustrate this approach
final double barWidth = 22; // your barWidth variable made global
// STEP 4 - here is the method to get a new chart. It's the exact same code of our theChart list
//but once we throw the old BarChart widget away we need to get another and this new one will call all
//its methods again and be updated with the new Y values from theChanges in new Data objects.
//So this method just return the BarChart widget.
Widget getTheChart(){
return BarChart(
BarChartData(
alignment: BarChartAlignment.center,
maxY: 20,
minY: 0,
groupsSpace: MediaQuery.of(sameContext).size.width * 0.05,
barTouchData: BarTouchData(enabled: true),
titlesData: FlTitlesData(
bottomTitles: BarTitles.getTopBottomTitles(),
leftTitles: BarTitles.getSideTitles(),
),
gridData: FlGridData(
checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
getDrawingHorizontalLine: (value) {
if (value == 0) {
return FlLine(
color: const Color(0xff363753),
strokeWidth: 3,
);
} else {
return FlLine(
color: const Color(0xff2a2747),
strokeWidth: 0.8,
);
}
},
),
barGroups: BarData.barData
.map(
(data) => BarChartGroupData(
x: data.id,
barRods: [
BarChartRodData(
y: data.y,
width: barWidth,
colors: [data.color],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
)),
],
),
).toList(),
),
);
}
// STEP 2 - Here i putted all the widgets of your page inside a list variable called theChart,
//this is to make things easier to control, so we can update the BarChart widget easily.
//In this list i'm putting your BarChart widget and a Button widget below it for you to be able to see
//the chart being updated. I created this list too as a global variable.
//Note that since this list is being declared outside the build method, we are using
//sameContext variable instead of context and thisState.setState((){}) instead of setState((){}).
//As you can see in the list, the BarChart widget is placed at 0 and the Container holding the
//MaterialButton is placed at 1.
List<Widget> theChart = <Widget>[
BarChart(
BarChartData(
alignment: BarChartAlignment.center,
maxY: 20,
minY: 0,
//sameContext variable being used here
groupsSpace: MediaQuery.of(sameContext).size.width * 0.05,
barTouchData: BarTouchData(enabled: true),
titlesData: FlTitlesData(
bottomTitles: BarTitles.getTopBottomTitles(),
leftTitles: BarTitles.getSideTitles(),
),
gridData: FlGridData(
checkToShowHorizontalLine: (value) => value % BarData.interval == 0,
getDrawingHorizontalLine: (value) {
if (value == 0) {
return FlLine(
color: const Color(0xff363753),
strokeWidth: 3,
);
} else {
return FlLine(
color: const Color(0xff2a2747),
strokeWidth: 0.8,
);
}
},
),
barGroups: BarData.barData
.map(
(data) => BarChartGroupData(
x: data.id,
barRods: [
BarChartRodData(
y: data.y,
width: barWidth,
colors: [data.color],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
)),
],
),
).toList(),
),
),
//this is the button widget, click to increase Y (theChanges variable) by 5,
//long press to decrease Y (theChanges variable) by 5
Container(
margin: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: MaterialButton(
color: Colors.lightBlue,
textColor: Colors.white,
child: Text("Press me!"),
onPressed: () {
//when you press the button we enter this method. Here we are outside the build method wich contains
//the context and the state variables so we need to use our state variable to call the setState
//method. This is the key point of the whole thing of updating the chart. We change the variable
//holding the Y value in the chart, here i'm using my theChanges variable but in your code it's that weekChartDataGlobal.
//Then we call the function BarData.updateData() (check step 3) to update the BarData static list variable wich contains the actual
//objects holding the Y values in the chart, and then we REPLACE THE BarChart WIDGET. That's right we throw
//the old widget away and replace it with a new one with the new values. Widgets atributes are final, we
//can't change them once they're settled so we need to replace them, the sooner you accept it the sooner you'll understand all this.
thisState.setState((){
theChanges += 5;
BarData.updateData();
theChart.removeAt(0);
theChart.insert(0, getTheChart()); // check step 4
});
},
onLongPress: (){
thisState.setState((){
theChanges -= 5;
BarData.updateData();
theChart.removeAt(0);
theChart.insert(0, getTheChart());
});
},
),
),
].toList();
class BarChartWidget extends StatefulWidget {
#override
_BarChartWidgetState createState() => _BarChartWidgetState();
}
class _BarChartWidgetState extends State<BarChartWidget> {
#override
Widget build(BuildContext context) {
// at this method your page will actually be built, here is where all the action will happen
//indeed so we
sameContext = context; // initialize our global context variable
thisState = this; // initialize our global State variable
//here we put theChart list variable inside a ListView and the ListView inside a container.
//The build method will get each widget inside the list and place it in order
return Container(
child: ListView.builder(
itemCount: theChart.length,
itemBuilder: (context, index){
return theChart[index];
},
),
);
}
}
class BarTitles {
static SideTitles getTopBottomTitles() => SideTitles(
showTitles: true,
getTextStyles: (value) =>
const TextStyle(color: Colors.white, fontSize: 12),
margin: 0,
getTitles: (double id) => BarData.barData
.firstWhere((element) => element.id == id.toInt())
.name,
);
static SideTitles getSideTitles() => SideTitles(
showTitles: true,
getTextStyles: (value) =>
const TextStyle(color: Colors.white, fontSize: 12),
rotateAngle: 90,
interval: BarData.interval.toDouble(),
margin: 0,
reservedSize: 30,
getTitles: (double value) => value == 0 ? '0' : '${value.toInt()}',
);
}
// STEP 3 here i replaced your weekChartDataGlobal with my theChanges variable to ilustrate the
//update and created the updateData() method.
class BarData {
static int interval = 5;
static List<Data> barData = [
Data(
id: 0,
name: 'Mon',
//y: weekChartDataGlobal[0]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff19bfff),
),
Data(
name: 'Tue',
id: 1,
//y: weekChartDataGlobal[1]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffff4d94),
),
Data(
name: 'Wed',
id: 2,
//y: weekChartDataGlobal[2]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff2bdb90),
),
Data(
name: 'Thu',
id: 3,
//y: weekChartDataGlobal[3]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffffdd80),
),
Data(
name: 'Fri',
id: 4,
//y: weekChartDataGlobal[4]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff2bdb90),
),
Data(
name: 'Sat',
id: 5,
//y: weekChartDataGlobal[5]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffffdd80),
),
Data(
name: 'Sun',
id: 6,
//y: weekChartDataGlobal[6]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffff4d94),
),
];
//this is another key point. Note that here i'm just clearing the whole static list and filling it
//again with the Data objects. I just copied the code above to create the static list and
//pasted down there. Why? Widgets attributes are final, their Y parameter won't change even if
//you update the theChanges variable so you need to throw them all away and place new ones
//with the new value of theChanges variable.
static void updateData(){
barData.clear();
barData = [
Data(
id: 0,
name: 'Mon',
//y: weekChartDataGlobal[0]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff19bfff),
),
Data(
name: 'Tue',
id: 1,
//y: weekChartDataGlobal[1]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffff4d94),
),
Data(
name: 'Wed',
id: 2,
//y: weekChartDataGlobal[2]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff2bdb90),
),
Data(
name: 'Thu',
id: 3,
//y: weekChartDataGlobal[3]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffffdd80),
),
Data(
name: 'Fri',
id: 4,
//y: weekChartDataGlobal[4]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xff2bdb90),
),
Data(
name: 'Sat',
id: 5,
//y: weekChartDataGlobal[5]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffffdd80),
),
Data(
name: 'Sun',
id: 6,
//y: weekChartDataGlobal[6]['dayTime'].toDouble(),
y: theChanges,
color: Color(0xffff4d94),
),
];
}
}
class Data {
// for ordering in the graph
final int id;
final String name;
final double y;
final Color color;
const Data({
required this.name,
required this.id,
required this.y,
required this.color,
});
}
In case anyone doesn't want to do such a complex workaround, I simply added a ValueKey to my graph constructor. Then attached this to my selector widget, which changes the timeframe my graph is showing, e.g. day, month, week.
e.g.
String _yourVariableThatChangesHere = "monthView";
GTBarChart(
key: ValueKey(_yourVariableThatChangesHere),
data: ...
)
Then when your _yourVariableThatChangesHere changes, the graph gets rebuilt when you call setState.
This is SfCartesianChart and I want to make it dynamic as per the rest API data given below but when I put the dynamic data to it, the graph shows only null in the legend text and no data shown but I've posted the required data. Kindly help me it's right or not.
SfCartesianChart _getSpacingColumnChart() {
return SfCartesianChart(
// borderColor: Colors.red,
// borderWidth: 2,
// Sets 15 logical pixels as margin for all the 4 sides.
margin: EdgeInsets.all(0),
plotAreaBorderWidth: 0,
title: ChartTitle(
// text: 'Inventory - Finished Products',
// textStyle: TextStyle(
// fontSize: 18.0,
// color: Colors.blueAccent,
// ),
text: widget.title,
// backgroundColor: Colors.lightGreen,
// borderColor: Colors.blue,
borderWidth: 2,
// Aligns the chart title to left
alignment: ChartAlignment.center,
// ignore: deprecated_member_use
textStyle: ChartTextStyle(
color: Colors.blueAccent,
// fontFamily: 'Roboto',
// fontStyle: FontStyle.italic,
fontSize: 11,
)),
primaryXAxis: CategoryAxis(
majorGridLines: MajorGridLines(width: 0),
),
primaryYAxis: NumericAxis(
// maximum: 150,
// minimum: 0,
interval: 25,
axisLine: AxisLine(width: 0),
majorTickLines: MajorTickLines(size: 0)),
palette: <Color>[
Color.fromRGBO(15, 207, 105, 1.0),
Color.fromRGBO(242, 209, 106, 1.0),
Color.fromRGBO(0, 72, 205, 1.0)
],
series: _getDefaultColumn(),
legend: Legend(
isVisible: true,
// Legend will be placed at the bottom
position: LegendPosition.bottom,
// Overflowing legend content will be wraped
overflowMode: LegendItemOverflowMode.wrap),
// tooltipBehavior: TooltipBehavior(
// enable: true,
// canShowMarker: true,
// header: '',
// format: 'point.y marks in point.x'),
tooltipBehavior: TooltipBehavior(enable: true),
);
}
List<ColumnSeries<ColumnChartDataModel, String>> _getDefaultColumn() {
List<ColumnChartDataModel> chartData = <ColumnChartDataModel>[];
for (Map i in widget.data)
chartData
.add(ColumnChartDataModel.fromJson(i) // Deserialization step #3
);
print('chartDataNewchartDataNew=>${widget.data}');
return <ColumnSeries<ColumnChartDataModel, String>>[
ColumnSeries<ColumnChartDataModel, String>(
/// To apply the column width here.
width: isCardView ? 0.8 : _columnWidth,
/// To apply the spacing betweeen to two columns here.
spacing: isCardView ? 0.2 : _columnSpacing,
animationDuration: 3000,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(2), topRight: Radius.circular(2)),
dataSource: chartData,
// color: const Color.fromRGBO(252, 216, 20, 1),
xValueMapper: (ColumnChartDataModel sales, _) => sales.x,
yValueMapper: (ColumnChartDataModel sales, _) => sales.y,
dataLabelSettings: DataLabelSettings(
isVisible: true,
labelAlignment: ChartDataLabelAlignment.top,
textStyle: TextStyle(fontSize: 10, color: Colors.white)),
name: 'In'),
ColumnSeries<ColumnChartDataModel, String>(
dataSource: chartData,
width: isCardView ? 0.8 : _columnWidth,
spacing: isCardView ? 0.2 : _columnSpacing,
animationDuration: 3000,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(2), topRight: Radius.circular(2)),
// color: const Color.fromRGBO(169, 169, 169, 1),
xValueMapper: (ColumnChartDataModel sales, _) => sales.x,
yValueMapper: (ColumnChartDataModel sales, _) =>
sales.secondSeriesYValue,
dataLabelSettings: DataLabelSettings(
isVisible: true,
labelAlignment: ChartDataLabelAlignment.top,
textStyle: TextStyle(fontSize: 10, color: Colors.white)),
name: 'Out'),
ColumnSeries<ColumnChartDataModel, String>(
dataSource: chartData,
width: isCardView ? 0.8 : _columnWidth,
spacing: isCardView ? 0.2 : _columnSpacing,
animationDuration: 3000,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(2), topRight: Radius.circular(2)),
// color: const Color.fromRGBO(205, 127, 50, 1),
xValueMapper: (ColumnChartDataModel sales, _) => sales.x,
yValueMapper: (ColumnChartDataModel sales, _) =>
sales.thirdSeriesYValue,
dataLabelSettings: DataLabelSettings(
isVisible: true,
labelAlignment: ChartDataLabelAlignment.top,
textStyle: TextStyle(fontSize: 10, color: Colors.white)),
name: 'Stock')
];
}
this page is the model data
Model Data
/// Package import
import 'package:flutter/material.dart';
/// Base class of the sample's stateful widget class
abstract class ColumnChartModel extends StatefulWidget {
/// base class constructor of sample's stateful widget class
const ColumnChartModel({Key key}) : super(key: key);
}
/// Base class of the sample's state class
abstract class ColumnChartModelState extends State<ColumnChartModel> {
/// Holds the information of current page is card view or not
bool isCardView;
#override
void initState() {
isCardView = true;
super.initState();
}
/// Get the settings panel content.
Widget buildSettings(BuildContext context) {
return null;
}
}
///Chart sample data
class ColumnChartDataModel {
/// Holds the datapoint values like x, y, etc.,
ColumnChartDataModel(
this.x,
this.y,
this.xValue,
this.yValue,
this.secondSeriesYValue,
this.thirdSeriesYValue,
this.pointColor,
this.size,
this.text,
this.open,
this.close,
this.low,
this.high,
this.volume);
/// Holds x value of the datapoint
final dynamic x;
/// Holds y value of the datapoint
final num y;
/// Holds x value of the datapoint
final dynamic xValue;
/// Holds y value of the datapoint
final num yValue;
/// Holds y value of the datapoint(for 2nd series)
final num secondSeriesYValue;
/// Holds y value of the datapoint(for 3nd series)
final num thirdSeriesYValue;
/// Holds point color of the datapoint
final Color pointColor;
/// Holds size of the datapoint
final num size;
/// Holds datalabel/text value mapper of the datapoint
final String text;
/// Holds open value of the datapoint
final num open;
/// Holds close value of the datapoint
final num close;
/// Holds low value of the datapoint
final num low;
/// Holds high value of the datapoint
final num high;
/// Holds open value of the datapoint
final num volume;
// factory ColumnChartDataModel.fromJson(Map<String, dynamic> json) => ColumnChartDataModel(
// x: json["x"],
// y: json["y"],
// secondSeriesYValue: json["secondSeriesYValue"],
// thirdSeriesYValue: json["thirdSeriesYValue"],
// );
factory ColumnChartDataModel.fromJson(Map<String, dynamic> parsedJson) {
return ColumnChartDataModel(
parsedJson['x'].toString(),
parsedJson['y'] as num,
parsedJson['secondSeriesYValue'] as num,
parsedJson['thirdSeriesYValue'] as num,
parsedJson['xValue'] as dynamic,
parsedJson['yValue'] as num,
parsedJson['pointColor'] as Color,
parsedJson['size'] as num,
parsedJson['text'] as String,
parsedJson['open'] as num,
parsedJson['close'] as num,
parsedJson['low'] as num,
parsedJson['high'] as num,
parsedJson['volume'] as num);
}
}
widget.data == [{productName: abcd-4, totalIn: 10, totalOut: 40, currentStock: 270}, {productName: afegwt-10 Pairs, totalIn: 110, totalOut: 80, currentStock: 530}]
widget.title == "some text"
Please help me..
Thankx in advance.
In the json data the key names should be matched as per the data model created. or you have to put separate instead of .fromJson().
I have been working with the online gallery of Flutter charts (https://google.github.io/charts/flutter/gallery.html) but I'm struggling to add a title for x & y axis values.
Can somebody help me or tell me how to add the labels to the graph?
Its possible using behaviors property, check the code
var chart = charts.LineChart(seriesList,
behaviors: [
new charts.ChartTitle('Dimension',
behaviorPosition: charts.BehaviorPosition.bottom,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Dose, mg',
behaviorPosition: charts.BehaviorPosition.start,
titleStyleSpec: chartsCommon.TextStyleSpec(fontSize: 11),
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea)
],
defaultRenderer: new charts.LineRendererConfig(includePoints: true));
Source https://google.github.io/charts/flutter/example/behaviors/chart_title
use the 'behavior' list for set title of chart
Widget build(BuildContext context) {
return new charts.LineChart(
seriesList,
animate: animate,
behaviors: [
new charts.ChartTitle('Top title text',
subTitle: 'Top sub-title text',
behaviorPosition: charts.BehaviorPosition.top,
titleOutsideJustification: charts.OutsideJustification.start,
innerPadding: 18),
new charts.ChartTitle('Bottom title text',
behaviorPosition: charts.BehaviorPosition.bottom,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('Start title',
behaviorPosition: charts.BehaviorPosition.start,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
new charts.ChartTitle('End title',
behaviorPosition: charts.BehaviorPosition.end,
titleOutsideJustification:
charts.OutsideJustification.middleDrawArea),
],
);
}
You can do it by using behaviors using line annotations iterating your list data and make a new LineAnnotationSegment array but you should be aware that some titles may overlap when the next time point is very close.
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
#override
Widget build(BuildContext context) {
return charts.TimeSeriesChart(seriesList, animate: false, behaviors: [
charts.RangeAnnotation( data.map((e) => charts.LineAnnotationSegment(
e.timestamp, charts.RangeAnnotationAxisType.domain,
middleLabel: '\$${e.price}')).toList()),
]);
}
Nevertheless you can use a callback to paint when the user clicks the line by painting either a custom text at the bottom or as a custom label using behaviors like this:
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
final data = [
LinearPrices(DateTime(2020, 9, 19), 5),
LinearPrices(DateTime(2020, 9, 26), 15),
LinearPrices(DateTime(2020, 10, 3), 20),
LinearPrices(DateTime(2020, 10, 10), 17),
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Chart'),
),
body: ChartPricesItem(data),
));
}
}
class ChartPricesItem extends StatefulWidget {
final List<LinearPrices> data;
ChartPricesItem(this.data);
static List<charts.Series<LinearPrices, DateTime>> _createSeries(
List<LinearPrices> data) {
return [
charts.Series<LinearPrices, DateTime>(
id: 'Prices',
colorFn: (_, __) => charts.MaterialPalette.deepOrange.shadeDefault,
domainFn: (LinearPrices sales, _) => sales.timestamp,
measureFn: (LinearPrices sales, _) => sales.price,
data: data,
)
];
}
#override
_ChartPricesItemState createState() => _ChartPricesItemState();
}
class _ChartPricesItemState extends State<ChartPricesItem> {
DateTime _time;
double _price;
// Listens to the underlying selection changes, and updates the information relevant
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
DateTime time;
double price;
// We get the model that updated with a list of [SeriesDatum] which is
// simply a pair of series & datum.
if (selectedDatum.isNotEmpty) {
time = selectedDatum.first.datum.timestamp;
price = selectedDatum.first.datum.price;
}
// Request a build.
setState(() {
_time = time;
_price = price;
});
}
#override
Widget build(BuildContext context) {
final simpleCurrencyFormatter =
charts.BasicNumericTickFormatterSpec.fromNumberFormat(
NumberFormat.compactSimpleCurrency());
var behaviors;
// Check if the user click over the line.
if (_time != null && _price != null) {
behaviors = [
charts.RangeAnnotation([
charts.LineAnnotationSegment(
_time,
charts.RangeAnnotationAxisType.domain,
labelDirection: charts.AnnotationLabelDirection.horizontal,
labelPosition: charts.AnnotationLabelPosition.margin,
labelStyleSpec:
charts.TextStyleSpec(fontWeight: FontWeight.bold.toString()),
middleLabel: '\$$_price',
),
]),
];
}
var chart = charts.TimeSeriesChart(
ChartPricesItem._createSeries(widget.data),
animate: false,
// Include timeline points in line
defaultRenderer: charts.LineRendererConfig(includePoints: true),
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: _onSelectionChanged,
)
],
// This is the part where you paint label when you click over the line.
behaviors: behaviors,
// Sets up a currency formatter for the measure axis.
primaryMeasureAxis: charts.NumericAxisSpec(
tickFormatterSpec: simpleCurrencyFormatter,
tickProviderSpec:
charts.BasicNumericTickProviderSpec(zeroBound: false)),
/// Customizes the date tick formatter. It will print the day of month
/// as the default format, but include the month and year if it
/// transitions to a new month.
///
/// minute, hour, day, month, and year are all provided by default and
/// you can override them following this pattern.
domainAxis: charts.DateTimeAxisSpec(
tickFormatterSpec: charts.AutoDateTimeTickFormatterSpec(
day: charts.TimeFormatterSpec(
format: 'd', transitionFormat: 'dd/MM/yyyy'),
minute: charts.TimeFormatterSpec(
format: 'mm', transitionFormat: 'dd/MM/yyyy HH:mm'))),
);
var chartWidget = Padding(
padding: EdgeInsets.all(16),
child: SizedBox(
height: 200.0,
child: chart,
),
);
final children = <Widget>[chartWidget];
// If there is a selection, then include the details.
if (_time != null) {
children.add(Padding(
padding: EdgeInsets.only(top: 4.0),
child: Text(DateFormat('dd/MM/yyyy hh:mm').format(_time),
style: Theme.of(context).textTheme.bodyText1)));
}
return SingleChildScrollView(
child: Column(
children: <Widget>[
const SizedBox(height: 8),
Text("Product Prices", style: Theme.of(context).textTheme.headline5),
Column(children: children),
],
),
);
}
}
/// Sample linear data type.
class LinearPrices {
final DateTime timestamp;
final double price;
LinearPrices(this.timestamp, this.price);
}
This is the result: