Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display data value of an item on click inside flutter charts

I'm using charts_flutter library on one of my projects and I would like to display the data value on clicking on each Bar chart item. Is it possible?

var data = [
  new ClicksPerYear('2016', 12, Colors.red),
  new ClicksPerYear('2017', 42, Colors.blue),
  new ClicksPerYear('2018', counter, Colors.green),
];

var series = [
  new charts.Series(
    id: 'Clicks',
    data: data,
    domainFn: (ClicksPerYear clickData, _) => clickData.year,
    measureFn: (ClicksPerYear clickData, _) => clickData.clicks,
    colorFn: (ClicksPerYear clickData, _) => clickData.color,
  )
];
var barChart = new charts.BarChart(
      series,
      animate: true,
);
like image 225
krishnakumarcn Avatar asked Dec 07 '18 09:12

krishnakumarcn


4 Answers

Found a solution from Github issues

https://github.com/google/charts/issues/58

To get the value use SelectionModelConfig.changedListener.

Extend the CircleSymbolRenderer (for LinePointHighlighter.symbolRenderer) and overridden the paint method. Inside you can draw custom objects relative to the selected point.

Maybe in the future the authors will make this easier but for now you use this method to modify rendering process of the selected point in any way you need (using ChartCanvas methods)

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart';
import 'package:charts_flutter/src/text_element.dart';
import 'package:charts_flutter/src/text_style.dart' as style;

class Chart extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LineChart(
      _createSampleData(),
      behaviors: [
        LinePointHighlighter(
          symbolRenderer: CustomCircleSymbolRenderer()
        )
      ],
      selectionModels: [
        SelectionModelConfig(
          changedListener: (SelectionModel model) {
            if(model.hasDatumSelection)
              print(model.selectedSeries[0].measureFn(model.selectedDatum[0].index));
          }
        )
      ],
    );
  }

  List<Series<LinearSales, int>> _createSampleData() {
    final data = [
      new LinearSales(0, 5),
      new LinearSales(1, 25),
      new LinearSales(2, 100),
      new LinearSales(3, 75),
    ];
    return [
      new Series<LinearSales, int>(
        id: 'Sales',
        colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
        domainFn: (LinearSales sales, _) => sales.year,
        measureFn: (LinearSales sales, _) => sales.sales,
        data: data,
      )
    ];
  }
}

class CustomCircleSymbolRenderer extends CircleSymbolRenderer {
  @override
  void paint(ChartCanvas canvas, Rectangle<num> bounds, {List<int> dashPattern, Color fillColor, Color strokeColor, double strokeWidthPx}) {
    super.paint(canvas, bounds, dashPattern: dashPattern, fillColor: fillColor, strokeColor: strokeColor, strokeWidthPx: strokeWidthPx);
    canvas.drawRect(
      Rectangle(bounds.left - 5, bounds.top - 30, bounds.width + 10, bounds.height + 10),
      fill: Color.white
    );
    var textStyle = style.TextStyle();
    textStyle.color = Color.black;
    textStyle.fontSize = 15;
    canvas.drawText(
      TextElement("1", style: textStyle),
        (bounds.left).round(),
        (bounds.top - 28).round()
    );
  }
}
class LinearSales {
  final int year;
  final int sales;
  LinearSales(this.year, this.sales);
}
like image 171
krishnakumarcn Avatar answered Nov 20 '22 18:11

krishnakumarcn


There were some errors when using the ChartCanvas method above the modified corrected version of this code is:

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart';
// import 'package:charts_flutter/src/chart_canvas.dart' as eos;
import 'package:charts_flutter/src/text_element.dart' as TextElement;
import 'package:charts_flutter/src/text_style.dart' as style;

class Chart extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LineChart(
      _createSampleData(),
      behaviors: [
        LinePointHighlighter(
          symbolRenderer: CustomCircleSymbolRenderer()
        )
      ],
      selectionModels: [
        SelectionModelConfig(
          changedListener: (SelectionModel model) {
            if(model.hasDatumSelection)
              print(model.selectedSeries[0].measureFn(model.selectedDatum[0].index));
          }
        )
      ],
    );
  }

  List<Series<LinearSales, int>> _createSampleData() {
    final data = [
      new LinearSales(0, 5),
      new LinearSales(1, 25),
      new LinearSales(2, 100),
      new LinearSales(3, 75),
    ];
    return [
      new Series<LinearSales, int>(
        id: 'Sales',
        colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
        domainFn: (LinearSales sales, _) => sales.year,
        measureFn: (LinearSales sales, _) => sales.sales,
        data: data,
      )
    ];
  }
}

class CustomCircleSymbolRenderer extends CircleSymbolRenderer {

  @override
  void paint(ChartCanvas canvas, Rectangle<num> bounds, {List<int> dashPattern, Color fillColor, FillPatternType fillPattern, Color strokeColor, double strokeWidthPx}) {
    super.paint(canvas, bounds, dashPattern: dashPattern, fillColor: fillColor,fillPattern: fillPattern, strokeColor: strokeColor, strokeWidthPx: strokeWidthPx);
    canvas.drawRect(
      Rectangle(bounds.left - 5, bounds.top - 30, bounds.width + 10, bounds.height + 10),
      fill: Color.white
    );
    var textStyle = style.TextStyle();
    textStyle.color = Color.black;
    textStyle.fontSize = 15;
    canvas.drawText(

      TextElement.TextElement("1", style: textStyle),
        (bounds.left).round(),
        (bounds.top - 28).round()
    );
  }
}
class LinearSales {
  final int year;
  final int sales;
  LinearSales(this.year, this.sales);
}
like image 4
SYED FAISAL Avatar answered Nov 20 '22 19:11

SYED FAISAL


I think you are searching for selection model, here is a bit of code which make event happened when you touch in chart:

selectionModels: [
    new charts.SelectionModelConfig(
        changedListener: (SelectionModel model) {
            print( model.selectedSeries[0].measureFn(
                model.selectedDatum[0].index);
        }
    )
],
like image 2
Goutham Sivakumar Avatar answered Nov 20 '22 18:11

Goutham Sivakumar


A newer version of what the top answer recommends, with an example that works out of the box is here: https://github.com/google/charts/issues/58#issuecomment-1023006331

In a nutshell, you'll define the behaviour of the chart like this:

  Widget build(BuildContext context) {
    return new charts.LineChart(seriesList,
        animate: animate,
        defaultRenderer: new charts.LineRendererConfig(includePoints: true),
        behaviors: [
          charts.LinePointHighlighter(
            ////////////////////// notice ////////////////////////////
            symbolRenderer: TextSymbolRenderer(() => Random().nextInt(100).toString()),
            ////////////////////// notice ////////////////////////////
          ),
        ],
    );
  }

And define the TextSymbolRenderer like this:

import 'dart:math';

import 'package:charts_flutter/flutter.dart';
import 'package:charts_flutter/src/text_style.dart' as style;
import 'package:charts_flutter/src/text_element.dart' as element;
import 'package:flutter/material.dart';

typedef GetText = String Function();

class TextSymbolRenderer extends CircleSymbolRenderer {

  TextSymbolRenderer(this.getText, {this.marginBottom = 8, this.padding = const EdgeInsets.all(8)});

  final GetText getText;
  final double marginBottom;
  final EdgeInsets padding;


  @override
  void paint(ChartCanvas canvas, Rectangle<num> bounds, {List<int>? dashPattern, Color? fillColor, FillPatternType? fillPattern, Color? strokeColor, double? strokeWidthPx}) {
    super.paint(canvas, bounds, dashPattern: dashPattern, fillColor: fillColor, fillPattern: fillPattern, strokeColor: strokeColor, strokeWidthPx: strokeWidthPx);

    style.TextStyle textStyle = style.TextStyle();
    textStyle.color = Color.black;
    textStyle.fontSize = 15;

    element.TextElement textElement = element.TextElement(getText.call(), style: textStyle);
    double width = textElement.measurement.horizontalSliceWidth;
    double height = textElement.measurement.verticalSliceWidth;

    double centerX = bounds.left + bounds.width / 2;
    double centerY = bounds.top + bounds.height / 2 - marginBottom - (padding.top + padding.bottom);

    canvas.drawRRect(
        Rectangle(
            centerX - (width / 2) - padding.left,
            centerY - (height / 2) - padding.top,
            width + (padding.left + padding.right),
            height + (padding.top + padding.bottom),
        ),
        fill: Color.white,
        radius: 16,
        roundTopLeft: true,
        roundTopRight: true,
        roundBottomRight: true,
        roundBottomLeft: true,
    );
    canvas.drawText(
        textElement,
        (centerX - (width / 2)).round(),
        (centerY - (height / 2)).round(),
    );
  }
}



like image 2
Kaetherina Avatar answered Nov 20 '22 19:11

Kaetherina