DENIZ TOPRAK
DENIZ TOPRAK

Reputation: 3

Syncfusion real time line chart

Problem: I'm developing a Flutter application that receives EEG signals from a device and visualizes them using Syncfusion charts. However, the app starts to lag significantly when the visualization tab is open. The visualizations and all UI processes running on the UI isolate begin to slow down. Below is the relevant code for reference. I suspect the periodic updates and rendering might be causing the issue, but I'm not sure how to optimize it.

import 'dart:async';
import 'package:syncfusion_flutter_charts/charts.dart';

class Exg extends StatefulWidget {
   final ExploreDevice exploreDevice;
   late final ExgSubscriber subscriber;
   Exg({super.key, required this.exploreDevice}) {
      subscriber = exploreDevice.exgSubscriber;
   }

   @override
   State<Exg> createState() => _ExgState();
}

class _ExgState extends State<Exg> {
   final int channelCount = 8;
   List<List<ExgData>> chartData = [];
   final List<ChartSeriesController> _chartSeriesControllers = [];
   Timer? _chartUpdateTimer;

   @override
   void initState() {
       // Update chart data periodically
       _chartUpdateTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
           //normally widget has a subscriber field but it is removed to simplify question
       ExgSubscriber subscriber = widget.subscriber;

       // Skip corrupted packet
       if (subscriber.data.length > widget.exploreDevice.getChannelCount().value) {
          return;
       }
       //init chartData list if it is not
       for (int i = 0; i < subscriber.data.length; i++) {
           if (chartData.length <= i) {
              chartData.add(List.from(subscriber.data[i]));
              setState(() {});
              continue;
           }
           //find last how many new packets have arrived and add them to the list
           int indexLast = subscriber.data[i].length -
              subscriber.data[i].lastIndexWhere((element) => element.time == 
              chartData[i].last.time) - 1;
           chartData[i].addAll(subscriber.data[i].sublist(subscriber.data[i].length - 
              indexLast));

           //add new packets to the list. Remove old packets if array size exceeds it's limit
        if (chartData[i].length >= 400) {
      chartData[i].removeRange(0, indexLast);
      _chartSeriesControllers[i].updateDataSource(
        addedDataIndexes: List<int>.generate(
            indexLast, (index) => chartData[i].length - indexLast + index),
        removedDataIndexes: List<int>.generate(indexLast, (index) => index),
      );
    } else {
      _chartSeriesControllers[i].updateDataSource(
        addedDataIndexes: List<int>.generate(indexLast,
            (index) => chartData[i].length - indexLast + index),
      );
    }
  }
});

super.initState();
}

@override
void dispose() {
  _chartUpdateTimer?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
  body: SingleChildScrollView(
    child: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: _buildCharts(),
      ),
    ),
  ),
);
}

List<Widget> _buildCharts() {
List<Widget> chartsList = [];
for (int i = 0; i < widget.channelCount; i++) {
  chartsList.add(Card(
    child: SizedBox(
      child: SfCartesianChart(
        title: ChartTitle(
          text: "ch${i + 1}",
          alignment: ChartAlignment.center,
        ),
        series: <CartesianSeries<ExgData, String>>[
          LineSeries<ExgData, String>(
            onRendererCreated: (ChartSeriesController controller) {
              if (_chartSeriesControllers.length <= i) {
                _chartSeriesControllers.add(controller);
              } else {
                _chartSeriesControllers[i] = controller;
              }
            },
            dataSource: chartData.isNotEmpty ? chartData[i] : [],
            xValueMapper: (ExgData exg, _) => exg.time,
            yValueMapper: (ExgData exg, _) => exg.value,
          ),
        ],
      ),
    ),
  ));
}
return chartsList;
}
}

Details:

Issues:

Questions:

Note: I followed the best practice that they suggest on the syncfusion documentation. Documentation link

Upvotes: 0

Views: 96

Answers (0)

Related Questions