Series customization in Flutter Circular Charts (SfCircularChart)

22 Apr 202124 minutes to read

Animation

SfCircularChart provides animation support for the series. Series will be animated while rendering. Animation is enabled by default, you can also control the duration of the animation using animationDuration property. You can disable the animation by setting 0 value to that property.

  • dart
  • @override
        Widget build(BuildContext context) {
            return Scaffold(
                body: Center(
                    child: Container(
                        child: SfCircularChart(
                            series: <CircularSeries<ChartData,dynamic>>[
                                // Render pie chart
                                PieSeries<ChartData, String>(
                                    dataSource: chartData,
                                    pointColorMapper:(ChartData data,  _) => data.color,
                                    xValueMapper: (ChartData data, _) => data.x,
                                    yValueMapper: (ChartData data, _) => data.y,
                                    animationDuration: 1000
                                )
                            ]
                        )
                    )
                )
            );
        }

    Color Palette

    SfCircularChart provides support for color palette property called palette for the data points in the chart series. If the series color is not specified, then the series will be rendered with appropriate palette color. Ten colors are available by default.

  • dart
  • @override
        Widget build(BuildContext context) {
            return Scaffold(
                body: Center(
                    child: Container(
                        child: SfCircularChart(
                            palette: <Color>[Colors.amber, Colors.brown, Colors.green, Colors.redAccent, Colors.blueAccent, Colors.teal],
                            series: <CircularSeries<ChartData,dynamic>>[
                                // Render pie chart
                                PieSeries<ChartData, String>(
                                    dataSource: chartData,
                                    pointColorMapper:(ChartData data,  _) => data.color,
                                    xValueMapper: (ChartData data, _) => data.x,
                                    yValueMapper: (ChartData data, _) => data.y,
                                )
                            ]
                        )
                    )
                )
            );
        }

    Color Palette

    Color mapping for data points

    The pointColorMapper property is used to map the color field from the data source.

  • dart
  • @override
        Widget build(BuildContext context) {
            static dynamic chartData = <SalesData>[
                SalesData('Rent', 1000,Colors.teal),
                SalesData('Food', 2500,Colors.lightBlue),
                SalesData('Savings', 760,Colors.brown),
                SalesData('Tax', 1897,Colors.grey),
                SalesData('Others', 2987,Colors.blueGrey)
            ];
            return Scaffold(
                body: Center(
                    child: Container(
                        child: SfCircularChart(
                            primaryXAxis: CategoryAxis(),
                            series: <PieSeries<SalesData, String>>[
                                PieSeries<SalesData, String>(
                                    dataSource: chartData,
                  xValueMapper: (SalesData sales, _) => sales.year,
                  yValueMapper: (SalesData sales, _) => sales.sales,
                  //map Color for each dataPoint datasource.
                  pointColorMapper: (SalesData sales,_) => sales.color,
                                )
                            ]
                        )
                    )
                )
            );
        }

    mapcolor

    Gradient and image shader

    The onCreateShader callback is used to fill the circular chart series data points with gradient and image shader. This callback is called once while rendering
    the data points and legend.

    NOTE

    All the data points of the circular chart are considered together as a single segment and the shader is applied commonly.

    Gradient fill

    The data points of pie, doughnut and radial bar charts can be filled with three types of gradient such as linear, sweep and radial. All the data points in the circular chart are together considered as a single segment and the shader is applied commonly.

    Linear gradient

  • dart
  • List<Color> colors = <Color>[
          const Color.fromRGBO(75, 135, 185, 1),
          const Color.fromRGBO(192, 108, 132, 1),
          const Color.fromRGBO(246, 114, 128, 1),
          const Color.fromRGBO(248, 177, 149, 1),
          const Color.fromRGBO(116, 180, 155, 1)
        ];
    
        List<double> stops = <double>[
          0.2,
          0.4,
          0.6,
          0.8,
          1,
        ];
    
        @override
        Widget build(BuildContext context) {
            return Container(
                child: SfCircularChart(
                    onCreateShader: (ChartShaderDetails chartShaderDetails) {
                        return Gradient.linear(chartShaderDetails.outerRect.topRight,
                            chartShaderDetails.outerRect.centerLeft, colors, stops);
                    },
                    series: <CircularSeries<_SalesData, dynamic>>[
                        PieSeries<_SalesData, dynamic>(
                            dataSource: chartData,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                        )
                    ]
                ));
        }

    shader linear

    Sweep gradient

  • dart
  • @override
        Widget build(BuildContext context) {
            return Container(
                child: SfCircularChart(
                    onCreateShader: (ChartShaderDetails chartShaderDetails) {
                        return Gradient.sweep(
                            chartShaderDetails.outerRect.center,
                            colors,
                            stops,
                            TileMode.clamp,
                            _degreeToRadian(0),
                            _degreeToRadian(360),
                            _resolveTransform(chartShaderDetails.outerRect, TextDirection.ltr)
                        );
                    },
                    series: <CircularSeries<_SalesData, dynamic>>[
                        RadialBarSeries<_SalesData, dynamic>(
                            dataSource: chartData,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                        )
                    ]
                ));
        }
    
        // Rotate the sweep gradient according to the start angle 
        Float64List _resolveTransform(Rect bounds, TextDirection textDirection) {
            final GradientTransform transform = GradientRotation(_degreeToRadian(-90));
            return transform.transform(bounds, textDirection: textDirection)!.storage;
        }
    
        // Convert degree to radian
        double _degreeToRadian(int deg) => deg * (3.141592653589793 / 180);

    shader sweep

    Radial gradient

  • dart
  • @override
        Widget build(BuildContext context) {
            return Container(
                child: SfCircularChart(
                    onCreateShader: (ChartShaderDetails chartShaderDetails) {
                        return Gradient.radial(
                            chartShaderDetails.outerRect.center,
                            chartShaderDetails.outerRect.right - chartShaderDetails.outerRect.center.dx,
                            colors,
                            stops
                        );
                    },
                    series: <CircularSeries<_SalesData, dynamic>>[
                        DoughnutSeries<_SalesData, dynamic>(
                            dataSource: chartData,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                        )
                    ]
                ));
        }

    shader radial

    Image fill

    The data points of pie, doughnut and radial bar charts can also be filled with image by returning ImageShader with required parameters.

  • dart
  • /// Package import
        import 'dart:async';
        import 'dart:ui' as ui;
    
        ui.Image? image;
    
        Future<void> getImage() async {
            const ImageProvider imageProvider = AssetImage('assets/apple.png');
            final Completer<ImageInfo> completer = Completer<ImageInfo>();
            imageProvider.resolve(const ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info, bool _) {
                completer.complete(info);
            }));
            final ImageInfo imageInfo = await completer.future;
            image = imageInfo.image;
            isLoadedImage = true;
        }
    
        @override
        Widget build(BuildContext context) {
            getImage();
            return Container(
                child: SfCircularChart(
                    onCreateShader: (ChartShaderDetails chartShaderDetails) {
                        return ImageShader(
                                    ui.image!,
                                    TileMode.mirror,
                                    TileMode.mirror,
                                    Matrix4.identity().scaled(0.4).storage
                        );
                    },
                    series: <CircularSeries<_SalesData, dynamic>>[
                        PieSeries<_SalesData, dynamic>(
                            dataSource: chartData,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                        )
                    ]
                ));
        }

    Image shader

    Shader mapping for data points

    The pointShaderMapper property is used to map the shader field from the chart data source. You can map different gradient types and image shader for individual data points using this mapper callback.

  • dart
  • /// Package import
        import 'dart:async';
        import 'dart:ui' as ui;
    
        ui.Image? image1;
        ui.Image? image2;
        ui.Image? image3;
        ui.Image? image4;
    
        // To get the images from asset folder
        void getImage() async {
            final Completer<ImageInfo> completer = Completer();
            final ImageProvider imageProvider = AssetImage('images/apple.png');
            imageProvider.resolve(const ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info, bool _) async {
            completer.complete(info);
                final ImageInfo imageInfo = await completer.future;
    
                image1 = imageInfo.image;
            }));
    
            final Completer<ImageInfo> completer1 = Completer();
            final ImageProvider imageProvider1 = AssetImage('images/orange.png');
            imageProvider1.resolve(const ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info, bool _) async {
                completer1.complete(info);
                final ImageInfo imageInfo1 = await completer1.future;
                image2 = imageInfo1.image;
            }));
    
            final Completer<ImageInfo> completer2 = Completer();
            final ImageProvider imageProvider2 = AssetImage('images/pears.png');
            imageProvider2.resolve(const ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info, bool _) async {
                completer2.complete(info);
                final ImageInfo imageInfo2 = await completer2.future;
    
                image3 = imageInfo2.image;
            }));
    
            final Completer<ImageInfo> completer3 = Completer();
            final ImageProvider imageProvider3 = AssetImage('images/other_fruits.png');
            imageProvider3.resolve(const ImageConfiguration()).addListener(ImageStreamListener((ImageInfo info, bool _) async {
                completer3.complete(info);
                final ImageInfo imageInfo4 = await completer3.future;
                image4 = imageInfo4.image;
                if (mounted) {
                    setState(() {});
                }
            }));
        }
    
        Widget? renderWidget;
    
        @override
        Widget build(BuildContext context) {
            getImage();
            if (image1 != null && image2 != null && image3 != null && image4 != null) {
                renderWidget = SfCircularChart(
                    title: ChartTitle(text: 'Sales comparison of fruits in a shop'),
                    series: <PieSeries<_ChartShaderData, String>>[
                        PieSeries<_ChartShaderData, String>(
                            dataSource: <_ChartShaderData>[
                                _ChartShaderData(
                                    'Apple',
                                    25,
                                    '25%',
                                    ui.ImageShader(
                                        image1!,
                                        TileMode.repeated,
                                        TileMode.repeated,
                                        Matrix4.identity().scaled(0.5).storage,
                                    ),
                                ),
                                _ChartShaderData(
                                    'Orange',
                                    35,
                                    '35%',
                                    ui.ImageShader(
                                        image2!,
                                        TileMode.repeated,
                                        TileMode.repeated,
                                        Matrix4.identity().scaled(0.6).storage,
                                    ),
                                ),
                                _ChartShaderData(
                                    'Pears',
                                    22,
                                    '22%',
                                    ui.ImageShader(
                                        image3!,
                                        TileMode.repeated,
                                        TileMode.repeated,
                                        Matrix4.identity().scaled(0.6).storage,
                                    ),
                                ),
                                _ChartShaderData(
                                    'Others',
                                    18,
                                    '18%',
                                    ui.ImageShader(
                                        image4!,
                                        TileMode.repeated,
                                        TileMode.repeated,
                                        Matrix4.identity().scaled(0.5).storage,
                                    ),
                                ),
                            ],
                            strokeColor: Colors.black.withOpacity(0.5),
                            strokeWidth: 1.5,
                            explodeAll: true,
                            explodeOffset: '3%',
                            explode: true,
                            xValueMapper: (_ChartShaderData data, _) => data.x,
                            yValueMapper: (_ChartShaderData data, _) => data.y,
                            dataLabelMapper: (_ChartShaderData data, _) => data.text,
                            // mapped the shader data from the chart's data source
                            pointShaderMapper: (_ChartShaderData data, _, Color color, Rect rect) => data.shader,
                            radius: '83%',
                        ),
                    ],
                );
            } else {
                getImage();
                renderWidget = Center(child: CircularProgressIndicator());
            }
            return Scaffold(
                body: renderWidget!
            );
        }
    
        class _ChartShaderData {
            _ChartShaderData(this.x, this.y, this.text, this.shader);
    
            final String x;
    
            final num y;
    
            final String text;
    
            final Shader shader;
        }

    Image pointshadermapper

    Point render mode

    The pointRenderMode property is used to define the painting mode for the data points. The data points in the pie and doughnut chart can be filled either with solid colors or with sweep gradient by using this property. This property is not applicable for RadialBarSeries.

    • If PointRenderMode.segment is specified, the data points are filled with solid colors from the palette or with the colors mentioned in pointColorMapper property.

    • If PointRenderMode.gradient is specified, a sweep gradient is formed with the solid colors and fills the data points.

    NOTE

    This property is applicable only if the onCreateShader and pointShaderMapper are null.

  • dart
  • @override
        Widget build(BuildContext context) {
            getImage();
            return Container(
                child: SfCircularChart(
                    series: <CircularSeries<_SalesData, dynamic>>[
                        PieSeries<_SalesData, dynamic>(
                            dataSource: chartData,
                            // Sweep gradient will be formed with default palette colors.
                            pointRenderMode: PointRenderMode.gradient,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                            xValueMapper: (_SalesData sales, _) => sales.year,
                        )
                    ]
                )
            );
        }

    Image pointshadermapper

    Dynamic animation

    SfCircularChart also provide the dynamic animation support for the series. The series can be dynamically added to the charts, it will animated by setting the timer value. when you set the animationDuration value to 0, the series won’t be animated.

    Empty points

    The data points that has null value are considered as empty points. Empty data points are ignored and not plotted in the chart. By using emptyPointSettings property in series, you can decide the action taken for empty points. Available modes are gap, zero, drop and average. Default mode of the empty point is gap.

  • dart
  • @override
        Widget build(BuildContext context) {
            
             final List<ChartData> chartData = [
                ChartData('David', null),
                ChartData('Steve', 38),
                ChartData('Jack', 34),
                ChartData('Others', 52)
            ];
            return Scaffold(
                body: Center(
                    child: SfCircularChart(
                        series: <CircularSeries<ChartData,dynamic>>[
                            // Render pie chart
                            PieSeries<ChartData, String>(
                                dataSource: chartData,
                                dataLabelSettings: DataLabelSettings(isVisible:true),
                                emptyPointSettings:
                          EmptyPointSettings(mode: EmptyPointMode.average),
                                pointColorMapper:(ChartData data,  _) => data.color,
                                xValueMapper: (ChartData data, _) => data.x,
                                yValueMapper: (ChartData data, _) => data.y,)
                        ]
                    )
                )
            );
        }

    Empty points

    Empty point customization

    Specific color for empty point can be set by color property in emptyPointSettings. The borderWidth property is used to change the stroke width of the empty point and borderColor is used to change the stroke color of the empty point.

  • dart
  • @override
        Widget build(BuildContext context) {
            final List<ChartData> chartData = [
            ChartData('David', null),
            ChartData('Steve', 38),
            ChartData('Jack', 34),
            ChartData('Others', 52)
            ];
            return Scaffold(
                body: Center(
                    child: Container(
                        child: SfCircularChart(
                            series: <CircularSeries<ChartData, dynamic>>[
                                PieSeries<ChartData, String>(
                                dataSource: chartData,
                                dataLabelSettings: DataLabelSettings(isVisible: true),
                                emptyPointSettings: EmptyPointSettings(
                                    mode: EmptyPointMode.average,
                                    color: Colors.red,
                                    borderColor: Colors.black,
                                    borderWidth: 2),
                                xValueMapper: (ChartData data, _) => data.x,
                                yValueMapper: (ChartData data, _) => data.y)
                            ]
                        )
                    )
                )
            );
        }

    Empty points customization

    Sorting

    The chart’s data source can be sorted using the sortingOrder and sortFieldValueMapper properties of series. The sortingOrder property specifies the data points in the series can be sorted in ascending or descending order. The data points will be rendered in the specified order if sortingOrder is set to none. The sortFieldValueMapper specifies the field in the data source, which is considered for sorting the data points.

  • dart
  • @override
        Widget build(BuildContext context) {
            
            final List<ChartData> chartData = [
                ChartData('David', 25),
                ChartData('Steve', 38),
                ChartData('Jack', 34),
                ChartData('Others', 52)
            ];
    
            return Scaffold(
                body: Center(
                    child: Container(
                        child: SfCircularChart(
                          series: <CircularSeries<ChartData,dynamic>>[
                            // Render pie chart
                            PieSeries<ChartData, String>(
                                dataSource: chartData,
                                dataLabelSettings: DataLabelSettings(isVisible:true),
                                sortingOrder: SortingOrder.ascending,
                                sortFieldValueMapper: (ChartData data, _) => data.x,
                                pointColorMapper:(ChartData data,  _) => data.color,
                                xValueMapper: (ChartData data, _) => data.x,
                                yValueMapper: (ChartData data, _) => data.y,
                                animationDuration: 1000
                            )]))));
        }

    Sorting

    See Also