Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bar Chart performance problem #102

Closed
jaysonjh opened this issue Nov 10, 2019 · 7 comments
Closed

Bar Chart performance problem #102

jaysonjh opened this issue Nov 10, 2019 · 7 comments
Labels
question Further information is requested

Comments

@jaysonjh
Copy link

Bar Chart performance problem.
The bar data like this

[{dataTypeName: 温度, warningCount: 1095}, {dataTypeName: 湿度, warningCount: 1095}, {dataTypeName: 温度, warningCount: 1401}, {dataTypeName: 湿度, warningCount: 2333}, {dataTypeName: 温度, warningCount: 95}, {dataTypeName: 湿度, warningCount: 85}, {dataTypeName: 光照, warningCount: 2355}, {dataTypeName: 湿度, warningCount: 9489}, {dataTypeName: 温度, warningCount: 2019}, {dataTypeName: 光照, warningCount: 10549}, {dataTypeName: 降雨量, warningCount: 15131}]

When touch the bar, it is will change bar color and show tip very slowly

@jaysonjh
Copy link
Author

The BarChartData code

 BarChartData _mainBarData(List<WarningChartInfo> chartInfo) {
    var max = chartInfo.reduce((curr, next) =>
        curr.warningCount.toDouble() > next.warningCount.toDouble()
            ? curr
            : next);
    int axisInterval = max.warningCount ~/ 3;
    return BarChartData(
        maxY: (max.warningCount + 1).toDouble(),
        gridData: FlGridData(
            show: true,
            drawVerticalGrid: true,
            drawHorizontalGrid: true,
            checkToShowHorizontalGrid: (index) {
              if (axisInterval == 0) {
                return true;
              } else {
                if (index.toInt() % axisInterval == 0) {
                  return true;
                } else if (index.toInt() == max.warningCount) {
                  return true;
                }
              }
              return false;
            }),
        titlesData: FlTitlesData(
            show: true,
            bottomTitles: SideTitles(
                showTitles: true,
                margin: 16,
                getTitles: (index) {
                  return chartInfo[index.toInt()].dataTypeName;
                }),
            leftTitles: SideTitles(
                showTitles: true,
                getTitles: (index) {
                  if (axisInterval == 0) {
                    return index.toString();
                  } else {
                    if (index.toInt() % axisInterval == 0) {
                      return index.toString();
                    }
                  }
                  return null;
                }),
            rightTitles: SideTitles(
                showTitles: true,
                getTitles: (index) {
                  if (axisInterval == 0) {
                    return index.toString();
                  } else {
                    if (index.toInt() % axisInterval == 0) {
                      return index.toString();
                    }
                  }
                  return null;
                })),
        borderData: FlBorderData(
          show: false,
        ),
        barGroups: _mainGroupData(chartInfo),
        barTouchData: BarTouchData(
            touchTooltipData: BarTouchTooltipData(
                tooltipBgColor: Colors.deepOrange,
                getTooltipItem: (group, groupIndex, rod, rodIndex) {
                  var item = chartInfo[group.x.toInt()];
                  var text = '${item.dataTypeName}\n${item.warningCount}';

                  return BarTooltipItem(text, TextStyle(color: Colors.white));
                }),
            touchCallback: onTouchResponse));
  }

@imaNNeo
Copy link
Owner

imaNNeo commented Nov 10, 2019

Hi, please provide me a run-able code (with your data).
It's easier to reproduce if you provide me a complete code.
Thanks!

@jaysonjh
Copy link
Author

Hi, please provide me a run-able code (with your data).
It's easier to reproduce if you provide me a complete code.
Thanks!

The test code

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class ChartTestPage extends StatelessWidget {
  const ChartTestPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chart Test'),
      ),
      body: Container(
        color: Colors.black12,
        child: Column(
          children: <Widget>[
            WarningChartWidget(),
          ],
        ),
      ),
    );
  }
}

class WarningChartWidget extends StatefulWidget {
  WarningChartWidget({Key key}) : super(key: key);

  _WarningChartWidgetState createState() => _WarningChartWidgetState();
}

class _WarningChartWidgetState extends State<WarningChartWidget> {
  static Duration animDuration = Duration(milliseconds: 250);
  final Color barBackgroundColor = const Color(0xff72d8bf);
  int touchedIndex;
  List<TestChartInfo> data = [];
  @override
  void initState() {
    super.initState();
    data = [
      TestChartInfo(dataTypeName: 'TEST1', warningCount: 50),
      TestChartInfo(dataTypeName: 'TEST2', warningCount: 150),
      TestChartInfo(dataTypeName: 'TEST3', warningCount: 500),
      TestChartInfo(dataTypeName: 'TEST4', warningCount: 1200),
      TestChartInfo(dataTypeName: 'TEST5', warningCount: 12300),
      TestChartInfo(dataTypeName: 'TEST6', warningCount: 200),
      TestChartInfo(dataTypeName: 'TEST7', warningCount: 15),
    ];
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.white,
        width: double.infinity,
        height: 450.0,
        child: Column(
          children: <Widget>[
            Container(
              padding: EdgeInsets.only(right: 5.0, left: 5.0),
              decoration: BoxDecoration(
                  border: Border(
                      top: BorderSide(width: 0.5, color: Colors.black12))),
              child: Row(
                children: <Widget>[
                  Align(
                    alignment: Alignment.center,
                    child: Container(
                      padding: EdgeInsets.only(
                          left: 8.0, right: 8.0, top: 5.0, bottom: 5.0),
                      width: 5,
                      height: 24,
                      color: Colors.green[400],
                    ),
                  ),
                  Align(
                    alignment: Alignment.center,
                    child: Container(
                      padding: EdgeInsets.only(
                          left: 8.0, right: 8.0, top: 5.0, bottom: 5.0),
                      child: Text('Warning Chart'),
                    ),
                  ),
                ],
              ),
            ),
            Expanded(
                child: Row(
              children: <Widget>[
                Expanded(
                  child: Container(
                    margin: EdgeInsets.only(top: 5.0),
                    padding: EdgeInsets.only(right: 32.0, left: 32.0),
                    child: BarChart(
                      _mainBarData(data),
                      swapAnimationDuration: animDuration,
                    ),
                  ),
                )
              ],
            ))
          ],
        ));
  }

  BarChartData _mainBarData(List<TestChartInfo> chartInfo) {
    var max = chartInfo.reduce((curr, next) =>
        curr.warningCount.toDouble() > next.warningCount.toDouble()
            ? curr
            : next);
    int axisInterval = max.warningCount ~/ 3;
    return BarChartData(
        maxY: (max.warningCount + 1).toDouble(),
        gridData: FlGridData(
            show: true,
            drawVerticalGrid: true,
            drawHorizontalGrid: true,
            checkToShowHorizontalGrid: (index) {
              if (axisInterval == 0) {
                return true;
              } else {
                if (index.toInt() % axisInterval == 0) {
                  return true;
                } else if (index.toInt() == max.warningCount) {
                  return true;
                }
              }
              return false;
            }),
        titlesData: FlTitlesData(
            show: true,
            bottomTitles: SideTitles(
                showTitles: true,
                margin: 16,
                getTitles: (index) {
                  return chartInfo[index.toInt()].dataTypeName;
                }),
            leftTitles: SideTitles(
                showTitles: true,
                getTitles: (index) {
                  if (axisInterval == 0) {
                    return index.toString();
                  } else {
                    if (index.toInt() % axisInterval == 0) {
                      return index.toString();
                    }
                  }
                  return null;
                }),
            rightTitles: SideTitles(
                showTitles: true,
                getTitles: (index) {
                  if (axisInterval == 0) {
                    return index.toString();
                  } else {
                    if (index.toInt() % axisInterval == 0) {
                      return index.toString();
                    }
                  }
                  return null;
                })),
        borderData: FlBorderData(
          show: false,
        ),
        barGroups: _mainGroupData(chartInfo),
        barTouchData: BarTouchData(
            touchTooltipData: BarTouchTooltipData(
                tooltipBgColor: Colors.deepOrange,
                getTooltipItem: (group, groupIndex, rod, rodIndex) {
                  var item = chartInfo[group.x.toInt()];
                  var text = '${item.dataTypeName}\n${item.warningCount}';

                  return BarTooltipItem(text, TextStyle(color: Colors.white));
                }),
            touchCallback: onTouchResponse));
  }

  List<BarChartGroupData> _mainGroupData(List<TestChartInfo> chartInfo) {
    int index = 0;
    List<BarChartGroupData> data = [];
    chartInfo.forEach((item) {
      var itemData = makeGroupData(index, item.warningCount.toDouble(),
          isTouched: index == touchedIndex,
          barColor: Theme.of(context).accentColor ?? Colors.green[400]);
      data.add(itemData);
      index++;
    });
    return data;
  }

  BarChartGroupData makeGroupData(
    int x,
    double y, {
    bool isTouched = false,
    Color barColor = Colors.green,
    double width = 22,
  }) {
    return BarChartGroupData(x: x, barRods: [
      BarChartRodData(
        y: isTouched ? y + 1 : y,
        color: isTouched ? Colors.yellow : barColor,
        width: width,
        isRound: true,
      ),
    ]);
  }

  void onTouchResponse(BarTouchResponse barTouchResponse) {
    setState(() {
      if (barTouchResponse.spot != null &&
          barTouchResponse.touchInput is! FlPanEnd &&
          barTouchResponse.touchInput is! FlLongPressEnd) {
        touchedIndex = barTouchResponse.spot.touchedBarGroupIndex;
      } else {
        touchedIndex = -1;
      }
    });
  }
}

class TestChartInfo {
  String dataTypeName;
  int warningCount;

  TestChartInfo({this.dataTypeName, this.warningCount});

  TestChartInfo.fromJson(Map<String, dynamic> json) {
    dataTypeName = json['dataTypeName'];
    warningCount = json['warningCount'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['dataTypeName'] = this.dataTypeName;
    data['warningCount'] = this.warningCount;
    return data;
  }
}

@imaNNeo
Copy link
Owner

imaNNeo commented Nov 11, 2019

Got it.
By default, the titles and gridlines try to draw with 1 interval (it means it iterates from 0 to your maxY value),
it takes a long time if you have large values and is not performant.
you should update the 'interval' inside your leftTitles, rightTitles and also verticalInterval inside your FlGridData.

@imaNNeo
Copy link
Owner

imaNNeo commented Nov 11, 2019

Feel free to tell me any suggestion that you think about it,
I think it is not clear to people.

@imaNNeo imaNNeo added the question Further information is requested label Nov 11, 2019
@imaNNeo
Copy link
Owner

imaNNeo commented Nov 14, 2019

Helooo?

@jaysonjh
Copy link
Author

Got it.
By default, the titles and gridlines try to draw with 1 interval (it means it iterates from 0 to your maxY value),
it takes a long time if you have large values and is not performant.
you should update the 'interval' inside your leftTitles, rightTitles and also verticalInterval inside your FlGridData.

Thanks, it's worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants