7.6 KiB
Series Customization
Table of Contents
- Animation
- Color Palette
- Per-Point Color Mapping
- Gradient Fill
- Image Fill
- Per-Point Shader Mapping
- Point Render Mode
- Empty / Null Data Points
- Sorting
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,
),
],
)
onCreateShaderis 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 0EmptyPointMode.drop— removes the point entirelyEmptyPointMode.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-firstSortingOrder.descending— largest/Z-firstSortingOrder.none— render in data source order (default)
Sort by y-value (largest slice first):
sortingOrder: SortingOrder.descending,
sortFieldValueMapper: (d, _) => d.y,