I have a Flutter code who reads data from a Json file and with the help of the fl_chart package the data are displayed on a chart. On the screen there is also a button. The idea of the button is that when pressed the data are loaded again and the chart would need to rebuild. The problem that i have that pressing the button the chart does not rebuild. Follows the structure of the json:
[
{"day": "Day 1", "value": 10},
{"day": "Day 2", "value": 20},
{"day": "Day 2", "value": 25}
]
Follows the Flutter code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fl_chart/fl_chart.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: ChartPage(),
);
}
}
class ChartPage extends StatefulWidget {
const ChartPage({super.key});
@override
ChartPageState createState() => ChartPageState();
}
class ChartPageState extends State<ChartPage> {
List<DataPoint> dataPoints = [];
Key chartKey = UniqueKey(); // Create a unique key for the chart.
@override
void initState() {
super.initState();
loadChartData();
}
Future<void> loadChartData() async {
final jsonString = await rootBundle.loadString('assets/data.json');
final jsonData = json.decode(jsonString);
List<DataPoint> newDataPoints = [];
for (var item in jsonData) {
newDataPoints.add(DataPoint(
day: item['day'],
value: item['value'],
));
}
setState(() {
dataPoints = newDataPoints;
chartKey = UniqueKey(); // Update the key to rebuild the chart.
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Chart Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
loadChartData(); // Reload data when the button is pressed.
},
child: const Text('Reload Chart Data'),
),
Expanded(
child: dataPoints.isNotEmpty
? LineChart(
key: chartKey, // Assign the key to the chart.
LineChartData(
gridData: const FlGridData(show: false),
titlesData: const FlTitlesData(show: false),
borderData: FlBorderData(show: true),
minX: 0,
maxX: dataPoints.length.toDouble() - 1,
minY: 0,
maxY: getMaxYValue(dataPoints),
lineBarsData: [
LineChartBarData(
spots: getDataSpots(dataPoints),
isCurved: true,
dotData: const FlDotData(show: false),
),
],
),
)
: const Text('No data available. Press the button to load data.'),
),
],
),
),
);
}
List<FlSpot> getDataSpots(List<DataPoint> data) {
return data
.asMap()
.map((index, value) => MapEntry(index.toDouble(), FlSpot(index.toDouble(), value.value.toDouble())))
.values
.toList();
}
double getMaxYValue(List<DataPoint> data) {
double maxValue = 0;
for (var point in data) {
if (point.value.toDouble() > maxValue) {
maxValue = point.value.toDouble();
}
}
return maxValue + 10; // Add some padding for a better visualization.
}
}
class DataPoint {
final String day;
final int value;
DataPoint({required this.day, required this.value});
}
2
Answers
Instead putting my Json into the assets folder i put it to the local Documents folder. Like this it works and is ok for me.
Follows de code:
I did a few experiments with your provided code & I can conclude that you’ve been facing issues when you re-read the JSON value. It works on the very first time & it won’t be working afterwards. It’s happening because the root bundle cannot read the value if it changes at the run-time state. The root bundle won’t detect the new / updated / most recent value. It’ll stuck on the initial value until & unless you perform a hot restart or a hot reload.
So, the solution is to make a Map-type variable and store your data there instead of storing data in a JSON file. Load JSON file is asynchronous (it takes a little bit of time) & getting the value of the class variable is synchronous (won’t take time).
I can help you as a technical person if you don’t get what I am trying to convey.