skills/syncfusion-flutter-circular-charts/references/series-customization.md

7.6 KiB

Series Customization

Table of Contents


Animation

Series animates in on first render. Animation is on by default.

Control Duration

PieSeries<ChartData, String>(
  dataSource: chartData,
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  animationDuration: 1500,  // milliseconds; set to 0 to disable
)

Add Delay Before Animation Starts

PieSeries<ChartData, String>(
  dataSource: chartData,
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  animationDuration: 4500,
  animationDelay: 2000,  // waits 2s before animating
)

Dynamic Animation

When data updates trigger a setState, the chart re-animates. Set animationDuration: 0 to skip animation on updates.


Color Palette

Override the default 10-color palette at the chart level. Colors cycle through data points automatically:

SfCircularChart(
  palette: <Color>[
    Colors.amber,
    Colors.brown,
    Colors.green,
    Colors.redAccent,
    Colors.blueAccent,
    Colors.teal,
  ],
  series: <CircularSeries>[
    PieSeries<ChartData, String>(
      dataSource: chartData,
      xValueMapper: (d, _) => d.x,
      yValueMapper: (d, _) => d.y,
    ),
  ],
)

Per-Point Color Mapping

Map colors directly from the data source using pointColorMapper:

class ChartData {
  ChartData(this.x, this.y, this.color);
  final String x;
  final double y;
  final Color color;
}

PieSeries<ChartData, String>(
  dataSource: <ChartData>[
    ChartData('Rent',    1000, Colors.teal),
    ChartData('Food',    2500, Colors.lightBlue),
    ChartData('Savings',  760, Colors.brown),
    ChartData('Tax',     1897, Colors.grey),
    ChartData('Others',  2987, Colors.blueGrey),
  ],
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  pointColorMapper: (d, _) => d.color,
)

Gradient Fill

Use onCreateShader on SfCircularChart to apply a gradient across all data points. All points share a single shader.

Linear Gradient

final List<Color> colors = [
  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),
];
final List<double> stops = [0.2, 0.4, 0.6, 0.8, 1.0];

SfCircularChart(
  onCreateShader: (ChartShaderDetails details) {
    return Gradient.linear(
      details.outerRect.topRight,
      details.outerRect.centerLeft,
      colors,
      stops,
    );
  },
  series: <CircularSeries>[
    PieSeries<ChartData, String>(
      dataSource: chartData,
      xValueMapper: (d, _) => d.x,
      yValueMapper: (d, _) => d.y,
    ),
  ],
)

Sweep Gradient

SfCircularChart(
  onCreateShader: (ChartShaderDetails details) {
    return Gradient.sweep(
      details.outerRect.center,
      colors,
      stops,
      TileMode.clamp,
      _degreeToRadian(0),
      _degreeToRadian(360),
      _resolveTransform(details.outerRect, TextDirection.ltr),
    );
  },
  ...
)

double _degreeToRadian(int deg) => deg * (3.141592653589793 / 180);

Float64List _resolveTransform(Rect bounds, TextDirection dir) {
  final transform = GradientRotation(_degreeToRadian(-90));
  return transform.transform(bounds, textDirection: dir)!.storage;
}

Radial Gradient

SfCircularChart(
  onCreateShader: (ChartShaderDetails details) {
    return Gradient.radial(
      details.outerRect.center,
      details.outerRect.right - details.outerRect.center.dx,
      colors,
      stops,
    );
  },
  series: <CircularSeries>[
    DoughnutSeries<ChartData, String>(
      dataSource: chartData,
      xValueMapper: (d, _) => d.x,
      yValueMapper: (d, _) => d.y,
    ),
  ],
)

onCreateShader is called once at render time. It applies the same shader to the entire chart area — not per point.


Image Fill

Fill the chart with a tiled or mirrored image texture using ImageShader:

import 'dart:async';
import 'dart:ui' as ui;

ui.Image? _chartImage;

Future<void> _loadImage() async {
  final provider = const AssetImage('assets/texture.png');
  final completer = Completer<ImageInfo>();
  provider.resolve(const ImageConfiguration()).addListener(
    ImageStreamListener((info, _) => completer.complete(info)),
  );
  final info = await completer.future;
  setState(() => _chartImage = info.image);
}

// In build:
SfCircularChart(
  onCreateShader: (ChartShaderDetails details) {
    return ImageShader(
      _chartImage!,
      TileMode.mirror,
      TileMode.mirror,
      Matrix4.identity().scaled(0.4).storage,
    );
  },
  ...
)

Per-Point Shader Mapping

Assign a different gradient or image shader to each individual data point using pointShaderMapper. This overrides onCreateShader for individual points.

class ChartData {
  ChartData(this.x, this.y, this.shader);
  final String x;
  final double y;
  final Shader shader;
}

PieSeries<ChartData, String>(
  dataSource: chartData,
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  pointShaderMapper: (data, _, color, rect) => data.shader,
)

Build Shader objects (e.g., ui.ImageShader) and store them directly in the data model.


Point Render Mode

Controls whether data points are filled with solid colors or a sweep gradient. Only applies to PieSeries and DoughnutSeries (not RadialBarSeries). Has no effect when onCreateShader or pointShaderMapper is set.

PieSeries<ChartData, String>(
  dataSource: chartData,
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  pointRenderMode: PointRenderMode.gradient,  // sweep gradient from palette colors
)
  • PointRenderMode.segment — solid fill per point (default)
  • PointRenderMode.gradient — sweep gradient formed from palette/point colors

Empty / Null Data Points

Data points with a null y-value are treated as empty. Control how they appear:

PieSeries<ChartData, String>(
  dataSource: <ChartData>[
    ChartData('David', null),  // empty point
    ChartData('Steve', 38),
    ChartData('Jack', 34),
    ChartData('Others', 52),
  ],
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  dataLabelSettings: DataLabelSettings(isVisible: true),
  emptyPointSettings: EmptyPointSettings(
    mode: EmptyPointMode.average,  // replace null with average of neighbors
  ),
)

Available modes:

  • EmptyPointMode.gap — renders a gap (default)
  • EmptyPointMode.zero — treats null as 0
  • EmptyPointMode.drop — removes the point entirely
  • EmptyPointMode.average — fills with average of adjacent points

Customize Empty Point Appearance

emptyPointSettings: EmptyPointSettings(
  mode: EmptyPointMode.average,
  color: Colors.red,
  borderColor: Colors.black,
  borderWidth: 2,
),

Sorting

Sort data points before rendering using sortingOrder and sortFieldValueMapper:

PieSeries<ChartData, String>(
  dataSource: chartData,
  xValueMapper: (d, _) => d.x,
  yValueMapper: (d, _) => d.y,
  sortingOrder: SortingOrder.ascending,
  sortFieldValueMapper: (d, _) => d.x,  // sort alphabetically by label
)
  • SortingOrder.ascending — smallest/A-first
  • SortingOrder.descending — largest/Z-first
  • SortingOrder.none — render in data source order (default)

Sort by y-value (largest slice first):

sortingOrder: SortingOrder.descending,
sortFieldValueMapper: (d, _) => d.y,