flutter_circular_chart

A library for creating animated circular chart widgets with Flutter.

Create easily animated pie charts and radial charts by providing them with data objects they can plot into charts and animate between.

Step 1: Install it

Run this command:

With Flutter:

 $ flutter pub add flutter_circular_chart

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  flutter_circular_chart: ^0.1.0

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Step 2: Getting Started

Import the package:

import 'package:flutter_circular_chart/flutter_circular_chart.dart';

Create a GlobalKey to be able to access the chart and update its data:

final GlobalKey<AnimatedCircularChartState> _chartKey = new GlobalKey<AnimatedCircularChartState>();

Step 3: Create Charts

Create chart data entry objects:

List<CircularStackEntry> data = <CircularStackEntry>[
  new CircularStackEntry(
    <CircularSegmentEntry>[
      new CircularSegmentEntry(500.0, Colors.red[200], rankKey: 'Q1'),
      new CircularSegmentEntry(1000.0, Colors.green[200], rankKey: 'Q2'),
      new CircularSegmentEntry(2000.0, Colors.blue[200], rankKey: 'Q3'),
      new CircularSegmentEntry(1000.0, Colors.yellow[200], rankKey: 'Q4'),
    ],
    rankKey: 'Quarterly Profits',
  ),
];

Create an AnimatedCircularChart, passing it the _chartKey and initial data:

@override
Widget build(BuildContext context) {
  return new AnimatedCircularChart(
    key: _chartKey,
    size: const Size(300.0, 300.0),
    initialChartData: data,
    chartType: CircularChartType.Pie,
  );
}

Call updateData to animate the chart:

void _cycleSamples() {
  List<CircularStackEntry> nextData = <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(1500.0, Colors.red[200], rankKey: 'Q1'),
        new CircularSegmentEntry(750.0, Colors.green[200], rankKey: 'Q2'),
        new CircularSegmentEntry(2000.0, Colors.blue[200], rankKey: 'Q3'),
        new CircularSegmentEntry(1000.0, Colors.yellow[200], rankKey: 'Q4'),
      ],
      rankKey: 'Quarterly Profits',
    ),
  ];
  setState(() {
    _chartKey.currentState.updateData(nextData);
  });
}

Customization

Hole Label:

Property Default
holeLabel null
labelStyle Theme.of(context).textTheme.body2

Example:

new AnimatedCircularChart(
  key: _chartKey,
  size: _chartSize,
  initialChartData: <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(
          33.33,
          Colors.blue[400],
          rankKey: 'completed',
        ),
        new CircularSegmentEntry(
          66.67,
          Colors.blueGrey[600],
          rankKey: 'remaining',
        ),
      ],
      rankKey: 'progress',
    ),
  ],
  chartType: CircularChartType.Radial,
  percentageValues: true,
  holeLabel: '1/3',
  labelStyle: new TextStyle(
    color: Colors.blueGrey[600],
    fontWeight: FontWeight.bold,
    fontSize: 24.0,
  ),
)

hole label example screenshot


Segment Edge Style:

Property Default
edgeStyle SegmentEdgeStyle.flat
SegmentEdgeStyle Description
flat (default) Segments begin and end with a flat edge.
round Segments begin and end with a semi-circle.

Example:

new AnimatedCircularChart(
  key: _chartKey,
  size: _chartSize,
  initialChartData: <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(
          33.33,
          Colors.blue[400],
          rankKey: 'completed',
        ),
        new CircularSegmentEntry(
          66.67,
          Colors.blueGrey[600],
          rankKey: 'remaining',
        ),
      ],
      rankKey: 'progress',
    ),
  ],
  chartType: CircularChartType.Radial,
  edgeStyle: SegmentEdgeStyle.round,
  percentageValues: true,
)

round segment edge example screenshot

Chart data entries:

Charts expect a list of CircularStackEntry objects containing the data they need to be drawn.

Each CircularStackEntry corresponds to a complete circle in the chart. For radial charts that is one of the rings, for pie charts it is the whole pie.

Radial charts with multiple CircularStackEntrys will display them as concentric circles.

Each CircularStackEntry is composed of multiple CircularSegmentEntrys containing the value of a data point. In radial charts a segment corresponds to an arc segment of the current ring, for pie charts it is an individual slice.

Full Examples

Here are some examples of using this plugin to create circular charts. Let us start by creating a color pallete:

color_palette.dart

import 'dart:math';
import 'package:flutter/material.dart';
class ColorPalette {
  static final ColorPalette primary = new ColorPalette(<Color>[
    Colors.blue[400],
    Colors.blue[200],
    Colors.red[400],
    Colors.red[200],
    Colors.green[400],
    Colors.green[200],
    Colors.yellow[400],
    Colors.yellow[200],
    Colors.purple[400],
    Colors.purple[200],
    Colors.orange[400],
    Colors.orange[200],
    Colors.teal[400],
    Colors.teal[200],
    Colors.black,
  ]);
  ColorPalette(List<Color> colors) : _colors = colors {
    assert(colors.isNotEmpty);
  }
  final List<Color> _colors;
  Color operator [](int index) => _colors[index % length];
  int get length => _colors.length;
  Color random(Random random) => this;
}

1. Animated Pie Chart

Here is an example of animated piechart:

animated_pie_chart_example.dart.dart

import 'package:flutter/material.dart';
import 'package:flutter_circular_chart/flutter_circular_chart.dart';
void main() {
  runApp(new MaterialApp(
    home: new AnimatedPieChartExample(),
  ));
}
final List<List<CircularStackEntry>> _quarterlyProfitPieData = [
  <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(500.0, Colors.red[200], rankKey: 'Q1'),
        new CircularSegmentEntry(1000.0, Colors.green[200], rankKey: 'Q2'),
        new CircularSegmentEntry(2000.0, Colors.blue[200], rankKey: 'Q3'),
        new CircularSegmentEntry(1000.0, Colors.yellow[200], rankKey: 'Q4'),
      ],
      rankKey: 'Quarterly Profits',
    ),
  ],
  <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(1500.0, Colors.red[200], rankKey: 'Q1'),
        new CircularSegmentEntry(750.0, Colors.green[200], rankKey: 'Q2'),
        new CircularSegmentEntry(2000.0, Colors.blue[200], rankKey: 'Q3'),
        new CircularSegmentEntry(1000.0, Colors.yellow[200], rankKey: 'Q4'),
      ],
      rankKey: 'Quarterly Profits',
    ),
  ],
  <CircularStackEntry>[
    new CircularStackEntry(
      <CircularSegmentEntry>[
        new CircularSegmentEntry(1800.0, Colors.red[200], rankKey: 'Q1'),
        new CircularSegmentEntry(2900.0, Colors.green[200], rankKey: 'Q2'),
        new CircularSegmentEntry(4000.0, Colors.blue[200], rankKey: 'Q3'),
        new CircularSegmentEntry(7000.0, Colors.yellow[200], rankKey: 'Q4'),
      ],
      rankKey: 'Quarterly Profits',
    ),
  ],
];
class AnimatedPieChartExample extends StatefulWidget {
  @override
  _AnimatedPieChartExampleState createState() =>
      new _AnimatedPieChartExampleState();
}
class _AnimatedPieChartExampleState extends State<AnimatedPieChartExample> {
  final GlobalKey<AnimatedCircularChartState> _chartKey =
      new GlobalKey<AnimatedCircularChartState>();
  final _chartSize = const Size(300.0, 300.0);
  int sampleIndex = 0;
  void _cycleSamples() {
    setState(() {
      sampleIndex++;
      List<CircularStackEntry> data = _quarterlyProfitPieData[sampleIndex % 3];
      _chartKey.currentState.updateData(data);
    });
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Quarterly Profit'),
      ),
      body: new Center(
        child: new AnimatedCircularChart(
          key: _chartKey,
          size: _chartSize,
          initialChartData: _quarterlyProfitPieData[0],
          chartType: CircularChartType.Pie,
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        child: new Icon(Icons.refresh),
        onPressed: _cycleSamples,
      ),
    );
  }
}

2. Animated Radial Chart

animated_radial_chart_example.dart

import 'package:flutter/material.dart';
import 'package:flutter_circular_chart/flutter_circular_chart.dart';
void main() {
  runApp(new MaterialApp(
    home: new AnimatedRadialChartExample(),
  ));
}
class AnimatedRadialChartExample extends StatefulWidget {
  @override
  _AnimatedRadialChartExampleState createState() =>
      new _AnimatedRadialChartExampleState();
}
class _AnimatedRadialChartExampleState
    extends State<AnimatedRadialChartExample> {
  final GlobalKey<AnimatedCircularChartState> _chartKey =
      new GlobalKey<AnimatedCircularChartState>();
  final _chartSize = const Size(200.0, 200.0);
  double value = 50.0;
  Color labelColor = Colors.blue[200];
  void _increment() {
    setState(() {
      value += 10;
      List<CircularStackEntry> data = _generateChartData(value);
      _chartKey.currentState.updateData(data);
    });
  }
  void _decrement() {
    setState(() {
      value -= 10;
      List<CircularStackEntry> data = _generateChartData(value);
      _chartKey.currentState.updateData(data);
    });
  }
  List<CircularStackEntry> _generateChartData(double value) {
    Color dialColor = Colors.blue[200];
    if (value < 0) {
      dialColor = Colors.red[200];
    } else if (value < 50) {
      dialColor = Colors.yellow[200];
    }
    labelColor = dialColor;
    List<CircularStackEntry> data = <CircularStackEntry>[
      new CircularStackEntry(
        <CircularSegmentEntry>[
          new CircularSegmentEntry(
            value,
            dialColor,
            rankKey: 'percentage',
          )
        ],
        rankKey: 'percentage',
      ),
    ];
    if (value > 100) {
      labelColor = Colors.green[200];
      data.add(new CircularStackEntry(
        <CircularSegmentEntry>[
          new CircularSegmentEntry(
            value - 100,
            Colors.green[200],
            rankKey: 'percentage',
          ),
        ],
        rankKey: 'percentage2',
      ));
    }
    return data;
  }
  @override
  Widget build(BuildContext context) {
    TextStyle _labelStyle = Theme
        .of(context)
        .textTheme
        .title
        .merge(new TextStyle(color: labelColor));
    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Percentage Dial'),
      ),
      body: new Column(
        children: <Widget>[
          new Container(
            child: new AnimatedCircularChart(
              key: _chartKey,
              size: _chartSize,
              initialChartData: _generateChartData(value),
              chartType: CircularChartType.Radial,
              edgeStyle: SegmentEdgeStyle.round,
              percentageValues: true,
              holeLabel: '$value%',
              labelStyle: _labelStyle,
            ),
          ),
          new Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              new RaisedButton(
                onPressed: _decrement,
                child: const Icon(Icons.remove),
                shape: const CircleBorder(),
                color: Colors.red[200],
                textColor: Colors.white,
              ),
              new RaisedButton(
                onPressed: _increment,
                child: const Icon(Icons.add),
                shape: const CircleBorder(),
                color: Colors.blue[200],
                textColor: Colors.white,
              ),
            ],
          ),
        ],
      ),
    );
  }
}

3. Animated Random Radial Chart

Here is an example of Animated Random Radial Chart:

animated_random_radial_chart_example.dart

import 'package:flutter/material.dart';
import 'package:flutter_circular_chart/flutter_circular_chart.dart';
import 'dart:math' as Math;
import 'color_palette.dart';
void main() {
  runApp(new MaterialApp(
    home: new RandomizedRadialChartExample(),
  ));
}
class RandomizedRadialChartExample extends StatefulWidget {
  @override
  _RandomizedRadialChartExampleState createState() =>
      new _RandomizedRadialChartExampleState();
}
class _RandomizedRadialChartExampleState
    extends State<RandomizedRadialChartExample> {
  final GlobalKey<AnimatedCircularChartState> _chartKey =
      new GlobalKey<AnimatedCircularChartState>();
  final _chartSize = const Size(300.0, 300.0);
  final Math.Random random = new Math.Random();
  List<CircularStackEntry> data;
  @override
  void initState() {
    super.initState();
    data = _generateRandomData();
  }
  double value = 50.0;
  void _randomize() {
    setState(() {
      data = _generateRandomData();
      _chartKey.currentState.updateData(data);
    });
  }
  List<CircularStackEntry> _generateRandomData() {
    int stackCount = random.nextInt(10);
    List<CircularStackEntry> data = new List.generate(stackCount, (i) {
      int segCount = random.nextInt(10);
      List<CircularSegmentEntry> segments =  new List.generate(segCount, (j) {
        Color randomColor = ColorPalette.primary.random(random);
        return new CircularSegmentEntry(random.nextDouble(), randomColor);
      });
      return new CircularStackEntry(segments);
    });
    return data;
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Randomized radial data'),
      ),
      body: new Center(
        child: new AnimatedCircularChart(
          key: _chartKey,
          size: _chartSize,
          initialChartData: data,
          chartType: CircularChartType.Radial,
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _randomize,
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

Reference

Read more here.
Download code Here.
Follow code author here.