any_sparklines 3.0.1
any_sparklines: ^3.0.1 copied to clipboard
Feature-rich and highly optimized sparklines for Flutter
Sparklines #
Feature-rich, highly optimized sparklines for Flutter. Line, scatter, bar, pie, and between-line charts with shared layouts, animation, and flexible styling.

You might also like my other packages: any_timeago, any_borders
Core concepts #
Layouts #
- AbsoluteLayout — Data coordinates map 1:1 to pixels; origin bottom-left, Y up.
- RelativeLayout — Data is scaled to explicit bounds. Use
RelativeLayout.normalized()(0–1),RelativeLayout.signed()(-1–1), orRelativeLayout.full()(auto from data). SetminX/maxX/minY/maxYtodouble.infinityordouble.negativeInfinityto derive from chart data. Identical layout instances are resolved once and shared across all charts using them.
Layouts transform plotted coordinates only. Visual dimensions choose their own units independently.
Visual lengths #
Thicknesses, marker radii, borders, and corner radii use ILengthValue:
- Px(2) — Flutter logical pixels.
- Vw(1) / Vh(1) — CSS-style viewport percentages;
1means 1%. - Vmin(1) / Vmax(1) — Percentage of the shorter/longer logical viewport dimension.
- Dx(0.1) / Dy(0.1) — X/Y data-space intervals converted through the chart transform.
LineData(
layout: const RelativeLayout.normalized(),
line: points,
thickness: const ThicknessData(size: Px(2)),
pointStyle: const CircleDataPointStyle(radius: Vw(1), color: Colors.blue),
)
Custom units implement ILengthValue.resolve(ILengthContext):
final class Vmean implements ILengthValue {
final double percentage;
const Vmean(this.percentage);
@override
double resolve(ILengthContext context) {
final side = (context.viewportWidth + context.viewportHeight) / 2;
return side * percentage / 100;
}
}
Unit changes animate smoothly because both endpoints are resolved in the current viewport before interpolation.
Rotation, flip, and origin #
- ChartRotation —
d0,d90,d180,d270(clockwise). Ford90/d270, logical width/height are swapped so the chart fills the widget. - ChartFlip —
none,vertically,horizontally,both. Flip charts around axes; applied after rotation. - origin —
Offsetapplied before rotation; use to position charts.
Crop and visibility #
- crop — When
true, rendering is clipped to chart bounds. Chart-levelcropoverrides the widget default. - visible — Per-chart; when
false, the chart is skipped.
Chart insets and point fitting #
ChartInsets reserves screen-space room inside the chart viewport without changing data points or layout bounds. Each side accepts any ILengthValue and omitted sides have no inset.
Use ChartInsets.all(), ChartInsets.horizontal(), or ChartInsets.vertical() for equal sides. Set global insets on SparklinesChart.padding or additional local insets on any built-in chart data object.
SparklinesChart(
padding: const ChartInsets(
left: Px(8),
top: Px(4),
right: Px(8),
bottom: Px(4),
),
charts: [
LineData.scatter(
padding: const ChartInsets.horizontal(Px(2)),
points: const [
DataPoint(
x: 0,
y: 0.5,
dy: 0,
data: {
IDataPointExtent: ChartInsets.all(Px(6)),
},
),
],
),
],
)
ChartInsets also implements IDataPointExtent, so it can describe a visual centered on a point's (x, fy) anchor. Point fitting is automatic: only the part of an extent that would cross a viewport edge is added. Global padding, local padding, and point overflow are combined, while charts sharing a layout use the largest requirement on each side so they remain aligned.
Px, Vw, Vh, Dx, and Dy values are resolved once with the preliminary chart transform, then insetsMatrix × layoutMatrix is used for rendering. Negative or non-finite resolved values are treated as zero. If opposing insets consume the viewport, every point collapses to a weighted position on that axis. For example, left 10 and right 40 in a width of 10 collapse x at 2.
DataPoint #
- x — X coordinate.
- key — Optional object used to select point transformations such as keyed
scatterZ()styles. UseDataPointKey(id: ..., key: ..., label: ...)when a structured key is useful. - y — Base Y (e.g. stacked base).
- dy — Delta from base; fy = y + dy is the value used for drawing.
- z — Third dimension for weights and other visual mappings; defaults to
0and does not affect layout bounds. - data — Extensible
Map<Type, IDataPointData?>for per-point metadata. Keys are type tokens; values implementIDataPointData(supportslerpTofor animation). Usepoint.of<M>()for type-safe access.
DataPoint.key accepts any object. DataPointKey is a convenience value object when a point needs a numeric ID, string key, label, or any combination of them:
const stockPrice = DataPoint(
x: 0,
dy: 187.44,
key: DataPointKey(id: 42, key: 'AAPL', label: 'Apple Inc.'),
);
final instrumentKey = stockPrice.key as DataPointKey;
DataPointKey has value equality, so equivalent keys can be used in sets passed to keyed pipeline operations such as scatterZ(keys: ...). During point interpolation, the source key is retained at t <= 0; the destination key is used afterward.
Common data entries (via extension getters or of<M>()):
- style —
IDataPointStyle?(e.g.CircleDataPointStyle) for point markers. - thickness —
IThicknessOverride?(size, color, gradient, align) to override chart thickness for this point. - extent —
IDataPointExtent?; useChartInsetsto automatically fit a visual centered on(x, fy)inside the viewport. - pieOffset —
IPieOffset?for pie slice offset.
Every ISparklinesData is iterable over its source points. Line, scatter, bar, and pie data iterate their corresponding point list. BetweenLineData iterates its from points followed by its to points, but reports supportsPointExtents == false, so metadata on those child points does not affect fitting.
Thickness (global and per-point) #
- ThicknessData —
size(ILengthValue),color, optionalgradient(overrides color),align:ThicknessData.alignInside(-1),alignCenter(0),alignOutside(1). - ThicknessOverride on
DataPoint— Same fields; overrides chart thickness for that point.
Border and border radius #
- IChartBorder —
border(ThicknessData?),borderRadius(ILengthValue?). Used by BarData and PieData.
Area fill (line charts) #
- areaColor / areaGradient — Fill below the line (from line down to base Y). Gradient takes precedence over color.
- areaFillType — Optional
PathFillTypefor the fill.
Line charts #
LineData — line, thickness, areaColor/areaGradient, areaFillType, lineType, pointStyle.
LineData.scatter — Marker-only x/y data without connected lines or area fills. It uses const ScatterLineData() internally. Scatter points are stored in line; use y for the vertical coordinate and set dy to zero so fy = y.
LineData.scatter(
points: const [
DataPoint(x: 0.0, y: 0.25, dy: 0.0),
DataPoint(x: 0.5, y: 0.75, dy: 0.0),
DataPoint(x: 1.0, y: 0.5, dy: 0.0),
],
pointStyle: const CircleDataPointStyle(radius: Px(4), color: Colors.blue),
)
Use z with DataPointPipeline.scatterZ() to generate per-point styles and fitting extents:
const smallStyle = CircleDataPointStyle(radius: Vmin(0.5), color: Colors.blue);
const largeStyle = CircleDataPointStyle(radius: Vmax(2), color: Colors.red);
final weightedPoints = DataPointPipeline()
.rescaleZ()
.scatterZ(
style: (
min: smallStyle,
max: largeStyle,
),
extent: (
min: smallStyle.extent,
max: largeStyle.extent,
),
)
.build(points);
final weightedScatter = LineData.scatter(points: weightedPoints);
Use keys or predicate to style only selected points. Supplying both uses AND matching.
Seat maps #
SeatsBuilder records seat-layout actions and creates ordinary DataPoint objects only when build() is called.
Each point uses a SeatKey directly as its key. A key may include a database/display label, but identity remains based on area, row, and column; labels and stable characteristic tags do not affect equality.
enum AircraftSeatTag { extraLegroom }
final labels = ListLabels([
for (var row = 1; row <= 10; row++)
for (final column in ['A', 'B', 'C', 'D', 'E', 'F']) '$row$column',
]);
final rawSeats = SeatsBuilder(
area: 'economy',
seatDx: 1.0,
rowDy: 1.0,
gapDx: 0.2,
gapDy: 0.4,
labelBuilder: labels,
)
.seats(3, tags: {AircraftSeatTag.extraLegroom})
.skip(2)
.seats(3, tags: {AircraftSeatTag.extraLegroom})
.nextRow()
.record()
.seats(3)
.skip(2)
.seats(3)
.nextRow()
.saveRecord(key: 'standardRow')
.repeat(9, key: 'standardRow')
.build();
final points = DataPointPipeline()
.seats(style: availableStyle, extent: seatExtent)
.seats(
selector: SeatSelector(tags: {{AircraftSeatTag.extraLegroom}}),
style: extraLegroomStyle,
)
.seats(keys: bookedSeats, style: bookedStyle)
.build(rawSeats);
final seatMap = LineData.scatter(points: points);
seatDx and rowDy are the base slot distances.
gapDx and gapDy add persistent space between future columns and rows without moving the cursor immediately; skip() instead consumes complete horizontal slots and their column numbers.
nextRow() resets the horizontal cursor and column while retaining area, tags, data, and gap defaults.
record() starts a repeatable action block and repeat(count) closes it; the count is the total number of block executions.
Use saveRecord(key: 'name') instead of repeat(count) to close and save a block without applying it. Replay it later with repeat(count, key: 'name') from the current cursor and defaults.
Saved-record keys must be unique, records must be saved before they can be replayed, and saved replays can be composed inside other recording blocks.
Blocks may be nested. Labels default to null through const NullLabels(); provide an ISeatLabelBuilder to the constructor or an individual seats() action, or override one seat with seat(label: ...).
ListLabels assigns its strings sequentially to emitted seats, ignores skipped slots, and validates that every supplied label was consumed. Custom builders implement next(row, column, area) and validate(); validation runs at the end of build().
saveState() saves the current gaps, area, tags, and data by default; set a flag to false to exclude that state group. saveOnlyState() starts with every flag disabled so individual groups can be selected, and an empty snapshot is a valid no-op.
Anonymous snapshots are restored once in LIFO order with restoreState(). Named snapshots use saveState(key: ...) or saveOnlyState(key: ...), are overwritten by later saves with that key, and can be restored repeatedly with restoreState(key).
State restoration never rewinds the seat cursor, row, column, labels, or emitted points.
area(), tags(), and data() replace defaults for subsequent actions.
Per-call data overlays the default DataPointDataMap, with call entries winning by metadata type.
Inputs are snapshotted when their actions are recorded, and each build() independently interprets the complete action program.
Every SeatSelector field is a set. Values within labels, rows, columns, and areas use OR semantics, while different fields use AND semantics; areas may contain null.
tags is an OR-set of all-of groups, so {{window, extraLegroom}, {accessible}} means (window AND extraLegroom) OR accessible.
All supplied pipeline filters are combined with AND semantics, and later seats() decorators retain normal last-writer-wins styling behavior.
Line types #
- LinearLineData — Straight segments; optional
isStrokeCapRound,isStrokeJoinRound. - SteppedLineData — Step at fraction between points:
stepJumpAt0→prev, 1→next; constructors.start(),.middle(),.end(). - CurvedLineData — Smooth curve;
smoothness0.0–1.0 (default 0.35). - ScatterLineData — Marker-only points;
drawLineis false andminPointsis 1.
Custom ILineTypeData implementations define drawLine and minPoints. Line and area geometry is rendered only when drawLine is true and the series contains at least minPoints; point markers render independently.
Between-line charts #
BetweenLineData — Fills the area between two lines. from, to (both LineData), areaColor, areaGradient, areaFillType. Uses same layout; both lines share the same coordinate system.
Bar charts #
BarData — bars (List<DataPoint>; fy = top, y = base), thickness, border, borderRadius, pointStyle. Bars are drawn from y to fy; use DataPointPipeline for stacking.
Pie charts #
PieData — Each DataPoint is one arc: x = radius, y = start angle, dy = sweep (end = y + dy). Angles in radians. thickness, padAngle (gap between slices), pieOffset, border, borderRadius, pointStyle. Bounds are computed from slice geometry.
DataPoint pipeline #
DataPointPipeline — Build transformed lists for stacking/normalization; reuse one pipeline for multiple series so shared state (e.g. stacking) is consistent.
- stack({ offset?, spacing?, groupingStep? }) — Stack points by x; each point’s
ybecomes the running sum at that x,dystays the value.offsetsets the initial base for each x (default 0.0), andspacingadds a gap between stacked segments. By default, x values must match exactly. WhengroupingStepis provided, x values are grouped by rounding them to buckets of that size, which is useful for coordinates affected by floating-point arithmetic. - normalize({ total, threshold?, spacing?, trailingSpacing?, thresholdPoint? }) — Scale
dyso sum ofabs(dy)equalstotal(default 1.0).thresholdrepeatedly drops smallest segment until none below threshold;thresholdPointreceives accumulated dy of removed points.spacingreserves gap between segments;trailingSpacingadds one more spacing (useful for full pies). - normalize2pi({ total, threshold?, spacing?, spacingDeg?, trailingSpacing?, thresholdPoint? }) — Same as
normalizewith defaulttotal2π for angles.spacingDegis spacing in degrees (converted to radians);trailingSpacingdefaults to true whentotal >= 2ortotal <= -2. - rescale({ currentMin?, currentMax?, targetMin, targetMax }) — Linearly rescale intervals
[DataPoint.y..DataPoint.fy]from[currentMin..currentMax]to[targetMin..targetMax](default 0–1). Bothyandfyare transformed;dyis recalculated asfy - y. IfcurrentMinorcurrentMaxare not finite, they are computed from input interval bounds. - rescaleZ({ currentMin?, currentMax?, targetMin, targetMax, clamp }) — Linearly rescale
DataPoint.zinto a target range (default 0–1). Automatic bounds are shared across every input registered with the pipeline. Values clamp to the source range by default, and an equal source range maps to the target midpoint. - scatterZ({ predicate?, keys?, style, extent? }) — Interpolate
IDataPointStyleand optionalIDataPointExtentintervals using z clamped to 0–1, then store them in each matching point's metadata.styleis a requiredStylesIntervalrecord andextentis an optionalExtentsIntervalrecord. When bothpredicateandkeysare supplied, both must match. - seats({ selector?, predicate?, keys?, style, extent? }) — Apply a fixed style and optional extent to matching points whose key is a
SeatKey. All supplied filters must match; without filters, every seat point is decorated. - sort({ x?, y?, fy?, z? }) — Sort input by x, y, fy, and/or z. Each:
true= ascending,false= descending. If all null, sorts by x ascending. - aggregate({ function, window? }) — Aggregate
dyover a window ending at each point.function:DataAggregation.sum,.avg,.min,.max,.median,.std(defaultsum).window: null = cumulative from start, N = last N elements. Updatesdyandfyper point.
IThresholdPoints / ThresholdPoints — When normalize removes below-threshold points and uses thresholdPoint, the aggregate point’s data contains ThresholdPoints(removed) so you can access the original points via point.of<IThresholdPoints>()?.thresholdPoints.
final pipeline = DataPointPipeline().stack().normalize(total: 1.0);
final seriesA = pipeline.build(rawPointsA);
final seriesB = pipeline.build(rawPointsB);
For example, stack(groupingStep: 1e-9) groups 0.1 + 0.2 and 0.3 at the same x position.
Widget options #
SparklinesChart
- charts — List of
ISparklinesData(e.g.LineData,BarData,PieData,BetweenLineData). - layout — Default
IChartLayout(e.g.AbsoluteLayout(),RelativeLayout.full()). - crop — Default clip-to-bounds.
- padding — Global chart insets; defaults to
const ChartInsets()(no insets). - width / height — Fixed size; one can be null and filled by layout.
- aspectRatio — Used when both width and height are null.
- animate — Enable data-driven animation (default
true). - animationDuration — Default 300 ms.
- animationCurve — Default
Curves.easeInOut.
Charts implement ILerpTo for smooth transitions when data changes.
Extending #
- IDataPointStyle + IDataPointRenderer — Custom point markers.
- IChartRenderer — Custom chart types.
- ILineTypeData + ILineTypeRenderer — Custom line path and stroke.
- IChartLayout — Custom coordinate systems; implement
resolve()andtransform(). - ILengthValue — Custom visual units; implement
resolve(ILengthContext).
Custom ISparklinesData implementations must expose ChartInsets get padding, implement Iterable<DataPoint>, and report supportsPointExtents. Use IterableMixin<DataPoint> to implement iteration from an existing point list. Return const ChartInsets() and false to opt out of local insets and automatic point-extent fitting.
