chart
Line, bar or donut, up to three series.
Shape, not precision. chart renders a line, a bar or a donut from up to three series of at most 24 points each, drawn with a CustomPainter rather than a charting dependency.
It answers "is this going up", "which month was busiest", "where does the money go". For a value the user has to read exactly, put it in a table.
Re-rendered in the browser from the SDK’s own scales. Not a screenshot.
Rendered natively by the Flutter SDK and the Web SDK, both since 0.1.0.
When not to use it
Do not use a chart when the user needs the numbers. A six-point line at thread width cannot be read to the decimal; table can.
Do not use one for a single value, which is stat_group, or for one value against a ceiling, which is progress.
Props
Every block also carries type and a required fallback_text of 1 to 300 characters.
| Prop | Type | Required | Constraints |
|---|---|---|---|
kind | line | bar | donut | Yes | A donut uses the first series only. |
title | string | No | Up to 120 characters. |
labels | string[] | Yes | 1 to 24 strings, up to 40 characters each. |
series | array | Yes | 1 to 3 entries. |
series[].name | string | No | Up to 60 characters. A legend appears on line and bar charts when there is more than one series and every one of them is named; a donut labels its slices in its own column instead. |
series[].data | number[] | Yes | 1 to 24 numbers. |
unit | string | No | Up to 12 characters. |
On the wire
{
"id": "blk_06",
"type": "chart",
"fallback_text": "Orders a month rose from 1 in March to 4 in August.",
"props": {
"kind": "bar",
"title": "Orders a month",
"labels": [
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug"
],
"series": [
{
"data": [
1,
0,
2,
1,
2,
4
]
}
]
}
}Interaction
None. chart has no interactive props: there is no tooltip, no legend toggle and no zoom.
States
| State | What the SDK draws |
|---|---|
| Loading | A plate holding a plot-shaped placeholder at the real plot height. |
| Empty | A chart whose series are all empty renders fallback_text inside the plate rather than an empty grid. |
| Flat data | When every value is identical the axis still spans a range, instead of dividing by zero. |
| One point | A line chart with a single point draws a dot at the centre rather than an invisible path. |
| Non-finite values | A NaN or an infinity is dropped from the series rather than poisoning every scale computed from it. A series left with nothing drops out of the chart. |
| Many slices | A donut lists at most six slices beside the ring and closes with a "+N more" line. The swatch and name only appear when every slice is labelled; otherwise the column degrades to percentages alone. |
Accessibility
A chart is the one block whose content is unreadable to a screen reader, so fallback_text is the chart: state the direction and the endpoints ("Orders a month rose from 1 in March to 4 in August"), not the fact that a chart exists. If fallback_text is missing the SDK generates a description from the data itself, which is a backstop rather than a substitute.
Axis labels and the legend are real text and stay readable; only the painting is replaced by the description.
How colour is assigned
A chart is a readout, so it carries none of your accent: the accent is reserved for what the reader can act on. A single-series bar chart is drawn in soft ink and full ink marks the peak, the bar worth reading first. Grouped bars and line charts tell their series apart by weight of ink rather than by hue.
A donut runs one ramp from full ink downward, so put the slice worth reading first at the head of the array and a twenty-slice donut never needs twenty invented colours. Percentages are computed by largest remainder, so the legend column adds to exactly 100.
Consecutive steps on that ramp sit about 1.64:1 apart, which is not enough to tell two neighbouring slices apart, and a monochrome ramp cannot answer that with more colour without inventing a palette you never chose. So the ring draws a hairline of the plate between every pair of slices instead: a structural boundary, which is also what WCAG 1.4.11 asks for between adjacent non-text areas. A slice keeps its full sweep until it is at least three times the gap, because losing a sliver entirely is worse than losing its separation.