skip to Main Content

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


  1. Chosen as BEST ANSWER

    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:

    import 'dart:convert';
    import 'dart:io';
    import 'package:flutter/material.dart';
    import 'package:fl_chart/fl_chart.dart';
    import 'package:path_provider/path_provider.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 {
        // Get the documents directory
        final directory = await getApplicationDocumentsDirectory();
        final filePath = '${directory.path}/data.json';
    
        try {
          // Read the JSON file from the documents directory
          final jsonString = File(filePath).readAsStringSync();
          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.
          });
        } catch (e) {
          print('Error reading JSON file: $e');
        }
      }
    
      @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. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search