How to create two dynamic SfCartesianChart in Flutter? - flutter

I am trying to create multiple dynamic charts (SfCartesianChart from syncfusion), the data is extracted every 1 second from a dynamic json file.
I tried to create two charts; the first one works fine but the second one remains the same , it does not change.
This is what I am getting.
Here is my code
import 'dart:ui';
import 'dart:async';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:flutter/material.dart';
import 'package:applicationv1/constants.dart';
import 'package:http/http.dart' as http;
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:applicationv1/classDonnee.dart';
import 'dart:convert';
Future<Donnee> fetchDonnee() async {
print('fetch0');
final response = await http.get(Uri.parse('uri'));
if (response.statusCode == 200) {
print('fecth1');
// If the server did return a 200 OK response, then parse the JSON.
return Donnee.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response, then throw an exception.
throw Exception('Failed to load album');
}
}
class depart1 extends StatefulWidget{
const depart1({Key? key}) : super(key: key);
#override
_depart1State createState() => _depart1State();
}
class _depart1State extends State<depart1> with SingleTickerProviderStateMixin{
late List<LiveData> chartData;
late List<LiveData2> chartData2;
ChartSeriesController? _chartSeriesController;
ChartSeriesController? _chartSeriesController2;
late Future<Donnee> futureDonnee;
Timer? timer;
#override
void initState() {
print('initstate');
futureDonnee=fetchDonnee();
chartData = getChartData();
chartData2 = getChartData2();
//super.initState();
timer= Timer.periodic(const Duration(seconds:1), (Timer t){
futureDonnee=fetchDonnee();
setState(() {
});
print('initstate1');
});
super.initState();
}
#override
Widget build(BuildContext context){
Size size = MediaQuery.of(context).size;
FutureBuilder f1;
//TabController _tabController;
print('build');
Center(child: f1=FutureBuilder<Donnee>(
future: futureDonnee,
builder: (context, snapshot) {
if (snapshot.hasData){
updateDataSource(snapshot.data!.w1);
updateDataSource2(snapshot.data!.va1)
return Center(
child:SingleChildScrollView(
child: Column(
children: <Widget>[
//chart 1
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white
),
margin: const EdgeInsets.fromLTRB(20,10,20,0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
//color: Colors.white,
child: Column(
children:[
Text('puissance Active de phase 1 (W)'),
SizedBox(height: 5,),
SfCartesianChart(
series: <LineSeries<LiveData, int>>[
LineSeries<LiveData, int>(
onRendererCreated: (ChartSeriesController controller) {
_chartSeriesController = controller;
},
dataSource: chartData,
color: Colors.blue,
xValueMapper: (LiveData sales, _) => sales.time,
yValueMapper: (LiveData sales, _) => sales.speed,
dataLabelSettings: DataLabelSettings(isVisible: true)
)
],
primaryXAxis: NumericAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
//title: AxisTitle(text: 'puissance Active de pahse 1 (W)')
)
),]
),),
////////////////////chart2
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white
),
margin: const EdgeInsets.fromLTRB(20,10,20,0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(
children:[
Text('puissance Apparente de phase 1 (VA)'),
SizedBox(height: 5,),
SfCartesianChart(
series: <LineSeries<LiveData2, int>>[
LineSeries<LiveData2, int>(
onRendererCreated: (ChartSeriesController controller2) {
_chartSeriesController2 = controller2;
},
dataSource: chartData2,
color: Colors.yellow,
xValueMapper: (LiveData2 sales, _) => sales.time,
yValueMapper: (LiveData2 sales, _) => sales.speed,
dataLabelSettings: DataLabelSettings(isVisible: true)
)
],
primaryXAxis: NumericAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
//title: AxisTitle(text: 'puissance Active de pahse 1 (W)')
)
),]
),),
],
),
)
);
//);
}
else if (snapshot.hasError) {
//return const Text('Please wait');
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return Container(
height: 20,
width:20,
child: const CircularProgressIndicator());
},
)
);
return f1;
}
int time = 19;
void updateDataSource(String val) {
ChartSeriesController controller;
var snapshot;
chartData.add(LiveData(time++, double.parse(val)));
//math.Random().nextInt(60) + 30
chartData.removeAt(0);
_chartSeriesController?.updateDataSource(
addedDataIndex: chartData.length - 1, removedDataIndex: 0);
print('update chart');
}
List<LiveData> getChartData() {
return <LiveData>[
LiveData(0, 0),
LiveData(1, 0),
LiveData(2, 0),
LiveData(3, 0),
LiveData(4, 0),
LiveData(5, 0),
LiveData(6, 0),
LiveData(7, 0),
LiveData(8, 0),
LiveData(9, 0),
LiveData(10, 0),
LiveData(11, 0),
LiveData(12, 0),
LiveData(13, 0),
LiveData(14, 0),
LiveData(15, 0),
LiveData(16, 0),
LiveData(17, 0),
LiveData(18, 0)
];
}
//coube2
int time2 = 19;
void updateDataSource2(String val) {
ChartSeriesController controller2;
var snapshot;
chartData2.add(LiveData2(time2++, double.parse(val)));
chartData2.removeAt(0);
_chartSeriesController2?.updateDataSource(
addedDataIndex: chartData2.length - 1, removedDataIndex: 0);
print('update chart2');
}
List<LiveData2> getChartData2() {
return <LiveData2>[
LiveData2(0, 0),
LiveData2(1, 0),
LiveData2(2, 0),
LiveData2(3, 0),
LiveData2(4, 0),
LiveData2(5, 0),
LiveData2(6, 0),
LiveData2(7, 0),
LiveData2(8, 0),
LiveData2(9, 0),
LiveData2(10, 0),
LiveData2(11, 0),
LiveData2(12, 0),
LiveData2(13, 0),
LiveData2(14, 0),
LiveData2(15, 0),
LiveData2(16, 0),
LiveData2(17, 0),
LiveData2(18, 0)
];
}
}
class LiveData {
LiveData(this.time, this.speed);
final int time;
final num speed;
}
class LiveData2 {
LiveData2(this.time, this.speed);
final int time;
final num speed;
}
Any help is much appreciated

I tried to replicate your scenario with the provided code, but both the charts are not updating with your code. Instead of JSON data, we created data dynamically. And we found that, in the timer, you have called the setstate, and in the builder, you have called the updateDataSource method. You have processed setstate and updateDataSource simultaneously. If you comment on the updateDataaSource method, due to the setstate method you have called, the chart is getting updated. But for live update cases, we suggest using the updateDataSource method. In the timer, you can call the updateDataSource method, so that chart will get updated properly. And we are not sure why you have used both methods simultaneously. As of now, we have modified your code snippet by updating the random data at each time by calling the updateDataSource method and ensured that it updates the data properly and attached it below. You can modify the sample as per your requirement.
Code snippet:
import 'dart:math';
import 'dart:ui';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:syncfusion_flutter_charts/charts.dart';
import 'dart:convert';
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,
),
home: const Depart1(),
);
}
}
class Depart1 extends StatefulWidget {
const Depart1({Key? key}) : super(key: key);
#override
_Depart1State createState() => _Depart1State();
}
class _Depart1State extends State<Depart1> with SingleTickerProviderStateMixin {
late List<LiveData> chartData;
late List<LiveData2> chartData2;
ChartSeriesController? _chartSeriesController;
ChartSeriesController? _chartSeriesController2;
Timer? timer;
#override
void initState() {
chartData = getChartData();
chartData2 = getChartData2();
timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
updateDataSource('val');
updateDataSource2('val');
});
super.initState();
}
Future<String> getJsonData() async {
return await rootBundle.loadString('assets/data.json');
}
Future loadData() async {
String response = await getJsonData();
final dynamic responseData = json.decode(response);}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
FutureBuilder f1;
Center(
child: f1 = FutureBuilder(
future: getJsonData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
margin: const EdgeInsets.fromLTRB(20, 10, 20, 0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(children: [
Text('puissance Active de phase 1 (W)'),
SizedBox(
height: 5,
),
SfCartesianChart(
series: <LineSeries<LiveData, DateTime>>[
LineSeries<LiveData, DateTime>(
onRendererCreated:
(ChartSeriesController controller) {
_chartSeriesController = controller;
},
dataSource: chartData,
color: Colors.blue,
xValueMapper: (LiveData sales, _) => sales.time,
yValueMapper: (LiveData sales, _) => sales.speed,
dataLabelSettings:
DataLabelSettings(isVisible: true))
],
primaryXAxis: DateTimeAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
)),
]),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white),
margin: const EdgeInsets.fromLTRB(20, 10, 20, 0),
padding: EdgeInsets.fromLTRB(2, 7, 2, 7),
child: Column(children: [
Text('puissance Apparente de phase 1 (VA)'),
SizedBox(
height: 5,
),
SfCartesianChart(
series: <LineSeries<LiveData2, DateTime>>[
LineSeries<LiveData2, DateTime>(
onRendererCreated:
(ChartSeriesController controller2) {
_chartSeriesController2 = controller2;
},
dataSource: chartData2,
color: Colors.yellow,
xValueMapper: (LiveData2 sales, _) => sales.time,
yValueMapper: (LiveData2 sales, _) => sales.speed,
dataLabelSettings:
DataLabelSettings(isVisible: true))
],
primaryXAxis: DateTimeAxis(
majorGridLines: const MajorGridLines(width: 0),
edgeLabelPlacement: EdgeLabelPlacement.shift,
interval: 3,
title: AxisTitle(text: 'Temps (secondes)')),
primaryYAxis: NumericAxis(
axisLine: const AxisLine(width: 0),
majorTickLines: const MajorTickLines(size: 0),
)),
]),
),
],
),
));
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return Container(
height: 20, width: 20, child: const CircularProgressIndicator());
},
));
return Scaffold(appBar: AppBar(), body: f1);
}
int time = 19;
void updateDataSource(String val) {
ChartSeriesController controller;
var snapshot;
chartData.add(LiveData(chartData2.last.time.add(const Duration(days: 1)),
_getRandomInt(10, 100)));
chartData.removeAt(0);
_chartSeriesController?.updateDataSource(
addedDataIndex: chartData.length - 1, removedDataIndex: 0);
print('update chart');
}
List<LiveData> getChartData() {
return <LiveData>[
LiveData(DateTime(2022, 01, 00), 0),
LiveData(DateTime(2022, 01, 01), 0),
LiveData(DateTime(2022, 01, 02), 0),
LiveData(DateTime(2022, 01, 03), 0),
LiveData(DateTime(2022, 01, 04), 0),
LiveData(DateTime(2022, 01, 05), 0),
LiveData(DateTime(2022, 01, 06), 0),
LiveData(DateTime(2022, 01, 07), 0),
LiveData(DateTime(2022, 01, 08), 0),
LiveData(DateTime(2022, 01, 09), 0),
LiveData(DateTime(2022, 01, 10), 0),
LiveData(DateTime(2022, 01, 11), 0),
LiveData(DateTime(2022, 01, 12), 0),
LiveData(DateTime(2022, 01, 13), 0),
LiveData(DateTime(2022, 01, 14), 0),
LiveData(DateTime(2022, 01, 15), 0),
LiveData(DateTime(2022, 01, 16), 0),
LiveData(DateTime(2022, 01, 17), 0),
LiveData(DateTime(2022, 01, 18), 0)
];
}
int time2 = 19;
void updateDataSource2(String val) {
ChartSeriesController controller2;
var snapshot;
chartData2.add(LiveData2(chartData2.last.time.add(const Duration(days: 1)),
_getRandomInt(10, 100)));
chartData2.removeAt(0);
_chartSeriesController2?.updateDataSource(
addedDataIndex: chartData2.length - 1, removedDataIndex: 0);
}
List<LiveData2> getChartData2() {
return <LiveData2>[
LiveData2(DateTime(2022, 01, 31), 0),
LiveData2(DateTime(2022, 01, 01), 0),
LiveData2(DateTime(2022, 01, 02), 0),
LiveData2(DateTime(2022, 01, 03), 0),
LiveData2(DateTime(2022, 01, 04), 0),
LiveData2(DateTime(2022, 01, 05), 0),
LiveData2(DateTime(2022, 01, 06), 0),
LiveData2(DateTime(2022, 01, 07), 0),
LiveData2(DateTime(2022, 01, 08), 0),
LiveData2(DateTime(2022, 01, 09), 0),
LiveData2(DateTime(2022, 01, 10), 0),
LiveData2(DateTime(2022, 01, 11), 0),
LiveData2(DateTime(2022, 01, 12), 0),
LiveData2(DateTime(2022, 01, 13), 0),
LiveData2(DateTime(2022, 01, 14), 0),
LiveData2(DateTime(2022, 01, 15), 0),
LiveData2(DateTime(2022, 01, 16), 0),
LiveData2(DateTime(2022, 01, 17), 0),
LiveData2(DateTime(2022, 01, 18), 0)
];
}
int _getRandomInt(int min, int max) {
return Random().nextInt(max - min);
}
}
class LiveData {
LiveData(this.time, this.speed);
final DateTime time;
final num speed;
}
class LiveData2 {
LiveData2(this.time, this.speed);
final DateTime time;
final num speed;
}

Related

Is there time series chart on fl_chart or is there any way to make line_chart a time series one?

Using line charts i cannot display time series data correctly. Is there a way to make it happen using any packages other than syncfusion_flutter_charts and charts_flutter.
Here is the code I tried. But it results in incorrect graph.
Here is the expected output
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
class FlChartExample extends StatelessWidget {
FlChartExample({Key? key}) : super(key: key);
List<SalesDataPair> sales = [
SalesDataPair( DateTime(2017, 9, 20), 25),
SalesDataPair( DateTime(2017, 9, 24), 50),
SalesDataPair( DateTime(2017, 10, 3), 100),
SalesDataPair( DateTime(2017, 10, 11), 75),
];
#override
Widget build(BuildContext context) {
final List<FlSpot> dummyData1 = List.generate(4, (index) {
return FlSpot(sales[index].date.day.toDouble(), sales[index].amount);
});
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.all(20),
width: double.infinity,
child: LineChart(
LineChartData(
borderData: FlBorderData(show: false),
lineBarsData: [
LineChartBarData(
spots: dummyData1,
isCurved: false,
barWidth: 3,
color: Colors.red,
),
],
),
),
),
),
);
}
}
class SalesDataPair {
SalesDataPair(this.date, this.amount);
final DateTime date;
final double amount;
}
I understood why the graph is wrongly displayed.

Why me trackball always disappears when I move a mouse on a SfCartesianChart?

I have SfCartesianChart and trackball inside, when I try to click or move a mouse on the SfCartesianChart then nothing happens, my trackball doesn't appear.
It appears only one second then immediately disappears, I fond out it by clicking and moving a mouse on SfCartesianChart almost a minute...
How to fix that?
This is my SfCartesianChart, there is no trackball when I click or move on blue dots:
And this is my code:
class HomeWidget extends StatefulWidget {
const HomeWidget({super.key});
#override
State<HomeWidget> createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
late TabController _tabController;
void initState() {
_tabController = TabController(vsync: this, length: 3);
super.initState();
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 220.h,
child: TabBarView(
controller: _tabController,
children: [
DaysChart(
monitoringData: state.chartData,
precision: 1
),
DaysChart(
monitoringData: state.chartData,
precision: 2
),
DaysChart(
monitoringData: state.chartData,
precision: 3
),
],
)
);
}
}
Chartdata looks like this:
state.chartData = List<TimeSeriesValues> dayMonitoringData = [
TimeSeriesValues(DateTime(2007, 2, 1, 8, 40), 30),
TimeSeriesValues(DateTime(2007, 2, 1, 12, 40), 80),
TimeSeriesValues(DateTime(2007, 2, 1, 18, 40), 50),
];
And TimeSeriesValues class:
class TimeSeriesValues {
final DateTime time;
final int values;
TimeSeriesValues(this.time, this.values);
}
And this is DaysChart:
class DaysChart extends StatefulWidget {
const DaysChart({
Key? key,
required this.monitoringData,
required this.precision,
}) : super(key: key);
final List<TimeSeriesValues> monitoringData;
final int precision;
#override
State<DaysChart> createState() => _DaysChartState();
}
class _DaysChartState extends State<DaysChart> {
late TrackballBehavior _trackballBehavior;
#override
void initState() {
_trackballBehavior = TrackballBehavior(
enable: true,
shouldAlwaysShow: true,
lineColor: const Color(0xFF454545),
activationMode: ActivationMode.singleTap,
tooltipDisplayMode: TrackballDisplayMode.nearestPoint,
tooltipSettings: const InteractiveTooltip(
arrowLength: 0,
arrowWidth: 0,
canShowMarker: false,
color: Colors.transparent,
),
builder: (context, TrackballDetails trackballDetails) {
var tag = Localizations.maybeLocaleOf(context)?.toLanguageTag();
return SizedBox(
height: 50,
child: Column(
children: [
Text(
"${trackballDetails.point!.yValue.round().toString()}%",
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
)
),
Text(
DateFormat.MMMMd(tag).format(trackballDetails.point!.x),
style: TextStyle(
color: Colors.white,
fontSize: 10.sp,
),
)
],
)
);
}
);
super.initState();
}
#override
Widget build(BuildContext context) {
final List<double> stops = <double>[];
stops.add(0.1);
stops.add(1.0);
return SizedBox(
height: 190.h,
width: 320.w,
child: SfCartesianChart(
plotAreaBorderWidth: 0,
plotAreaBorderColor: Colors.white24,
trackballBehavior: _trackballBehavior,
primaryXAxis: DateTimeCategoryAxis(
majorTickLines: const MajorTickLines(width: 0),
axisLine: const AxisLine(
color: Colors.white24,
dashArray: <double>[5,5]
),
minimum: widget.monitoringData.first.time,
maximum: widget.monitoringData.last.time,
intervalType: widget.precision == 1
? DateTimeIntervalType.minutes
: widget.precision == 2
? DateTimeIntervalType.days
: DateTimeIntervalType.months,
dateFormat: widget.precision == 1
? DateFormat.Hm()
: widget.precision == 2
? DateFormat.E()
: DateFormat.MMMd(),
borderColor: Colors.transparent,
majorGridLines: const MajorGridLines(
width: 0.5,
color: Colors.transparent,
),
),
primaryYAxis: NumericAxis(
majorGridLines: const MajorGridLines(width: 0.5, color: Colors.white24, dashArray: <double>[5, 5]),
majorTickLines: const MajorTickLines(width: 0),
axisLine: const AxisLine(
width: 0
),
labelStyle: const TextStyle(
fontSize: 0
),
minimum: 0,
maximum: 100
),
series: <ChartSeries<TimeSeriesValues, DateTime>>[
AreaSeries<TimeSeriesValues, DateTime>(
borderWidth: 2,
animationDuration: 0,
borderColor: const Color(0xFF409CFF),
dataSource: widget.monitoringData,
markerSettings: const MarkerSettings(
isVisible: true,
color: Color(0xFF409CFF),
height: 11,
width: 11,
borderWidth: 3,
borderColor: Colors.transparent,
),
xValueMapper: (TimeSeriesValues sales, _) => sales.time,
yValueMapper: (TimeSeriesValues sales, _) => sales.values,
gradient: LinearGradient(
colors: const [Color(0xFF121212), Color(0xFF10273F)],
stops: stops,
begin: Alignment.bottomCenter,
end: Alignment.topCenter),
)
]
);
}

Flutter - How to flip a single card from swipe stack?

I am using swipe_stack
I need to flip the front card when I click on it. The problem currently I am facing is that the entire stacks get flipped.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:quiero_funku/widgets/appbar.dart';
import '../../utils/swipe_stack.dart';
class SwipeDeck extends StatefulWidget {
const SwipeDeck({Key? key}) : super(key: key);
#override
State<SwipeDeck> createState() => _SwipeDeckState();
}
class _SwipeDeckState extends State<SwipeDeck>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation _animation;
AnimationStatus _status = AnimationStatus.dismissed;
List<AnimationController> dataCtrl = <AnimationController>[];
// initialize _controller, _animation
#override
void initState() {
super.initState();
// add some AnimationController object before using any index
dataCtrl.add(AnimationController(vsync: this, duration: const Duration(seconds: 1)));
for (int i = 1; i < 10; i++) {
var "_controller$i" = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
"_animation$i" = Tween(end: 1.0, begin: 0.0).animate(_controller)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
_status = status;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppbar(
onBackPressed: () {},
title: '',
),
body: Container(
padding: const EdgeInsets.all(20),
height: 300,
width: double.infinity,
child: SwipeStack(
children: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int index) {
return SwiperItem(
builder: (SwiperPosition position, double progress) {
return Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.0015)
..rotateX(pi * _animation.value),
child: GestureDetector(
onTap: () {
print("tapped");
if (_status == AnimationStatus.dismissed) {
_controller.forward();
} else {
_controller.reverse();
}
},
child: Material(
elevation: 4,
borderRadius: const BorderRadius.all(Radius.circular(6)),
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(6)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Item $index",
style: const TextStyle(
color: Colors.black,
fontSize: 20,
),
),
Text(
"Progress $progress",
style: const TextStyle(
color: Colors.blue,
fontSize: 12,
),
),
],
),
),
),
),
);
});
}).toList(),
visibleCount: 3,
stackFrom: StackFrom.Right,
translationInterval: 6,
scaleInterval: 0.03,
onEnd: () => debugPrint("onEnd"),
onSwipe: (int index, SwiperPosition position) =>
debugPrint("onSwipe $index $position"),
onRewind: (int index, SwiperPosition position) =>
debugPrint("onRewind $index $position"),
),
),
);
}

Values are not displaying on Flutter Charts

I am fetching data from firebase's real-time database to show in flutter charts but after function calling the value returned is still null/0. When getDuration() is called the data is shown inside the function but it is not returning. As I am new to Flutter, I don't understand the problem occurring.
If I hard code the values, then it shows on the chart but if I call function then it doesn't display bars.
My database looks like this:
I am fetching duration under the date to show inside the flutter chart on the y-axis with weekdays as the x-axis.
My code for flutter chart:
void initState() {
getSeizureHistory();
//Charts
setState(() {
var data = [
addcharts(sevenDay.substring(0, 2), getDuration(seven),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(sixDay.substring(0, 2), getDuration(six),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(fiveDay.substring(0, 2), getDuration(five),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(fourDay.substring(0, 2), getDuration(four),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(threeDay.substring(0, 2), getDuration(three),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(twoDay.substring(0, 2), getDuration(two),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
addcharts(oneDay.substring(0, 2), getDuration(one.toString()),
charts.ColorUtil.fromDartColor(const Color(0xffe8e5af))),
];
var series = [
charts.Series(
domainFn: (addcharts addcharts, _) => addcharts.weeks,
measureFn: (addcharts addcharts, _) => addcharts.duration,
colorFn: (addcharts addcharts, _) => addcharts.barColor,
id: 'addcharts',
data: data,
),
];
chartdisplay = charts.BarChart(
series,
animationDuration: Duration(microseconds: 2000),
);
});
}
getDuration() function:
int _duration = 0;
int getDuration(String date) {
//print("Date $date");
//load the data from firebase and add to the list
fb.reference()
..child(cuser.uid)
.child('Seizure_history')
.child(date)
.once()
.then((DataSnapshot snapshot) {
var data = snapshot.value;
list = List();
if (data != null) {
data.forEach((key, value) {
EventList seizure_history = new EventList(
seiz_duration: value['duration'],
dateTime: value['datetime'],
key: key,
);
list.add(seizure_history);
setState(()
{
_duration = int.parse(list[0].seiz_duration);
});
print("Duration $_duration");
return _duration;
});
}
});
}
Inside widget:
Center(
child: Container(
height: size.width * 0.80,
width: size.width * 0.90,
padding: EdgeInsets.all(11.0),
child: Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Text(
"Seizure Duration",
style: TextStyle(
fontFamily: 'Montserrat',
fontSize: 12,
color: const Color(0xff232425),
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
Expanded(
child: chartdisplay,
)
],
),
),
),),
),
addcharts class:
class addcharts {
final String weeks;
final int duration;
final charts.Color barColor;
addcharts(this.weeks, this.duration, this.barColor);
}
On console, it is showing:
But on charts, it is still null:

How to implement API data in a line chart

I am trying to implement my API data in a chart using fl_chart dependencies in flutter. But I just cannot figure out how to implement it.
Here is how I implement my data:
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
itemCount: 1,
itemBuilder: (context, index){
// ignore: unused_local_variable
int number = index + 1;
return Container(
width: MediaQuery.of(context).size.width * 0.50,
child: LineChart(
LineChartData(
gridData: FlGridData(
show: true,
drawVerticalLine: true,
getDrawingHorizontalLine: (value) {
return FlLine(
color: const Color(0xff37434d),
strokeWidth: 1,
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: const Color(0xff37434d),
strokeWidth: 1,
);
},
),
titlesData: FlTitlesData(
show: true,
bottomTitles: SideTitles(
showTitles: true,
reservedSize: 22,
getTextStyles: (value) =>
const TextStyle(color: Color(0xff68737d), fontWeight: FontWeight.bold, fontSize: 16),
getTitles: (value) {
switch (value.toInt()) {
case 2:
return 'MAR';
case 5:
return 'JUN';
case 8:
return 'SEP';
}
return '';
},
margin: 8,
),
leftTitles: SideTitles(
showTitles: true,
getTextStyles: (value) => const TextStyle(
color: Color(0xff67727d),
fontWeight: FontWeight.bold,
fontSize: 15,
),
getTitles: (value) {
switch (value.toInt()) {
case 1:
return '10k';
case 3:
return '30k';
case 5:
return '50k';
}
return '';
},
reservedSize: 28,
margin: 12,
),
),
borderData:
FlBorderData(show: true, border: Border.all(color: const Color(0xff37434d), width: 1)),
minX: 0,
maxX: 11,
minY: 0,
maxY: 6,
lineBarsData: [
LineChartBarData(
spots: [
FlSpot(0 , pings[number.toString()][index].volume),
FlSpot(2.6, 2),
FlSpot(4.9, 5),
FlSpot(6.8, 3.1),
FlSpot(8, 4),
FlSpot(9.5, 3),
FlSpot(11, 4),
],
isCurved: true,
colors: gradientColors,
barWidth: 5,
isStrokeCapRound: true,
dotData: FlDotData(
show: true,
),
belowBarData: BarAreaData(
show: true,
colors: gradientColors.map((color) => color.withOpacity(0.3)).toList(),
),
),
],
)
And here is how i call my data:
Map<String, List<TankPing>> pings;
initState() {
Services.fetchPing().then((tankPings) => {
setState((){
pings = tankPings;
})
});
super.initState();
}
My API call is in another file. I call the API like below:
static Future<Map<String, List<TankPing>>> fetchPing() async {
String url3 = 'https://api.orbital.katsana.com/devices/graph-data';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final SharedPreferences prefs = await _prefs;
final token = prefs.getString('access_token');
final response3 = await http.get(url3, headers: {
'Authorization': 'Bearer $token'
});
if(response3.statusCode == 200) {
final tankPings = tankPingFromJson(response3.body);
return tankPings;
}else if(response3.statusCode == 400) {
print('Connection to server is bad');
}else if(response3.statusCode == 500){
print('No authorization');
}
}
I am trying to implement it inside of FlSPot() function. But then U receive this error:
The method '[]' was called on null.
Receiver: null
Tried calling: []("1")
Here is my model:
import 'dart:convert';
Map<String, List<TankPing>> tankPingFromJson(dynamic str) => Map.from(json.decode(str)).map((k, v) => MapEntry<String, List<TankPing>>(k, List<TankPing>.from(v.map((x) => TankPing.fromJson(x)))));
String tankPingToJson(Map<String, List<TankPing>> data) => json.encode(Map.from(data).map((k, v) => MapEntry<String, dynamic>(k, List<dynamic>.from(v.map((x) => x.toJson())))));
class TankPing {
TankPing({
this.trackedAt,
this.fuel,
this.level,
this.volume,
});
DateTime trackedAt;
double fuel;
double level;
double volume;
factory TankPing.fromJson(Map<String, dynamic> json) => TankPing(
trackedAt: DateTime.parse(json["tracked_at"]),
fuel: json["fuel"].toDouble(),
level: json["level"].toDouble(),
volume: json["volume"].toDouble(),
);
Map<String, dynamic> toJson() => {
"tracked_at": trackedAt.toString(),
"fuel": fuel,
"level": level,
"volume": volume,
};
}
Here is how the API look:
{
"1": [
{
"tracked_at": "2020-11-20T19:41:21.000000Z",
"fuel": 87.03,
"level": 3.0460554,
"volume": 50665.14
},
{
"tracked_at": "2020-11-22T00:19:41.000000Z",
"fuel": 85.75,
"level": 3.0012249,
"volume": 50051.86
},
{
"tracked_at": "2020-11-22T00:32:00.000000Z",
"fuel": 84.17,
"level": 2.9460489,
"volume": 49265.04
},
]
My API is very long and it looks like that. Any help would be appreciated.
I just post the code example in here. If you have any question, you can ask me and I will try to answer the question I can because this code is like almost 2 or 3 years old now and I did not work on this project anymore. Hope the code below helps you!
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:intl/intl.dart';
import 'custom_symbol_renderer.dart';
import 'package:orbital_app/Model/tank_ping.dart';
import 'package:orbital_app/Provider/api_provider.dart';
class TankChart extends StatefulWidget {
//This is my API class object to extract the data
TankChart({Key key}) : super(key: key);
#override
_TankChartState createState() => _TankChartState();
}
class _TankChartState extends State<TankChart> {
var ping;
var tankInfo;
// Since I am using a Provider in this code, I call the API here
getPingProvider(){
setState((){
ping = Provider.of<TankPingProvider>(context, listen: false);
ping.getTankPing(context);
});
}
getInfoProvider(){
setState((){
tankInfo = Provider.of<TankInfoProvider>(context, listen: false);
tankInfo.getTankInfo(context);
});
}
#override
initState() {
super.initState();
getPingProvider();
getInfoProvider();
}
#override
Widget build(BuildContext context) {
// Here I format the time to normal human time
final numericFormatter = charts.BasicNumericTickFormatterSpec.fromNumberFormat(
NumberFormat.compact()
);
final ping = Provider.of<TankPingProvider>(context);
return ListView.builder(
padding: EdgeInsets.zero,
// Here I want everything to be shrink and expand when the user needs it
shrinkWrap: true,
// Here is where I set whether the graph can be expand by user vertical
// scroll
physics: NeverScrollableScrollPhysics(),
//The data from the API is here
itemCount: ping.tankPing.length,
itemBuilder: (context, index){
if(ping.tankPing.length == null){
return CircularProgressIndicator();
} else if(ping.tankPing == null){
return CircularProgressIndicator();
} else{
int no = index + 1;
final size = MediaQuery.of(context).size;
// Here is the API dot or data dot on the graph
List<charts.Series<TankPing, DateTime>> series = [
charts.Series(
id: '${tankInfo.tankInfos.data[index].name}',
data: ping.tankPing[no.toString()],
colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
domainFn: (TankPing ping, _) => ping.trackedAt,
measureFn: (TankPing ping, _) => ping.volume
),
];
return Container(
height: 250,
child: Card(
child: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 5
),
child: charts.TimeSeriesChart(
series,
animate: false,
domainAxis: charts.DateTimeAxisSpec(
tickFormatterSpec: charts.AutoDateTimeTickFormatterSpec(
day: charts.TimeFormatterSpec(
format: 'dd',
transitionFormat: 'dd MMM',
),
),
),
primaryMeasureAxis: charts.NumericAxisSpec(
tickFormatterSpec: numericFormatter,
renderSpec: charts.GridlineRendererSpec(
// Tick and Label styling here.
labelStyle: charts.TextStyleSpec(
fontSize: 10, // size in Pts.
color: charts.MaterialPalette.black
),
)
),
defaultRenderer: charts.LineRendererConfig(
includeArea: true,
includeLine: true,
includePoints: true,
strokeWidthPx: 0.5,
radiusPx: 1.5
),
dateTimeFactory: const charts.LocalDateTimeFactory(),
behaviors: [
charts.SlidingViewport(),
charts.PanAndZoomBehavior(),
charts.SeriesLegend(
position: charts.BehaviorPosition.top,
horizontalFirst: false,
cellPadding: EdgeInsets.only(
left: MediaQuery.of(context).size.width * 0.27,
top: 15
),
),
charts.SelectNearest(
eventTrigger: charts.SelectionTrigger.tap
),
charts.LinePointHighlighter(
symbolRenderer: CustomCircleSymbolRenderer(size: size),
),
],
selectionModels: [
charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: (charts.SelectionModel model) {
if(model.hasDatumSelection) {
final tankVolumeValue = model.selectedSeries[0].measureFn(model.selectedDatum[0].index).round();
final dateValue = model.selectedSeries[0].domainFn(model.selectedDatum[0].index);
CustomCircleSymbolRenderer.value = '$dateValue \n $tankVolumeValue L';
}
})
]),
),
),
],
),
),
);
}
});
}
}
The answer is use the min and max value to determine how long the data will be. And then just use the flSpot to enter your data.