flutter_variety_chart 0.5.24
flutter_variety_chart: ^0.5.24 copied to clipboard
A pure-Dart, dependency-free chart library for Flutter offering cartesian and circular visualizations such as line, spline, area, column, bar, scatter, pie and doughnut charts.
0.5.24 #
What the options promised, the canvas now does #
Nine things a chart accepted and then ignored. Every one of them was a value the caller set, the documentation described, and the drawing code never asked for — the failure mode this library has had to hunt most often, because it is silent: nothing throws, nothing is missing, the picture just does not change.
Fixed
- A delayed series no longer starts early.
animationDelaywas subtracted from the eased progress instead of from the raw timeline, so a series waiting 500 ms had already risen a quarter of the way when 300 ms had passed. The delay is now taken out before the curve is applied. - A series can stretch the entrance. A series declaring a longer
animationDurationthan the chart kept the chart's timeline, because the controller's duration was read once ininitStateand never again. - A pinned end stays where it was pinned.
VarietyRangeControllerwindows are remembered as a normalised position, so a low end pinned at 4 drifted to 4.9 as soon as a point was appended past the old maximum. A pinned end is re-applied every frame; a free end still follows the data. - Selection reports itself.
selectionBehavior.onSelectionChangedwas called when a selection was seeded and never when one was tapped, so a series-level behaviour could not observe anything. - A bar draws its corner radius and outline.
VarietyBarSeriesshares the column builder, and only columns were asked forcornerRadius,borderColorandborderWidth. - A line honours
fillOpacity. The value was read only to place the top of a stacked run, so a plain line asking for a fill drew the same stroke as one without it. labelOffsetis included in the room reserved for axis labels. The inset was a hard coded 12, which cropped every caption as soon as the offset was raised above the default.animate: falseis per series. It used to mean only "the chart animates unless every series opts out", so one series asking to stand still was ignored while the others rose.- Category captions over a long run stay unique. The caption pattern was
chosen from the span the axis covers, which for two thousand readings a
second apart stops at minutes: thirty-four captions for two thousand slots.
When the span's ladder cannot separate two instants the search drops to the
gap between neighbours, and a microsecond token (
SSSSSS) is available for the last resort.
0.5.23 #
A chart that can be driven #
Three things a chart could not do before this: show a window somebody else chose, take a point while it is on screen, and let a caption or a marker be answered with. All three are the same gap seen from different sides — the chart was something an application built and then watched, never something it could talk to while it ran.
Added
VarietyRangeController, andVarietyAxis.rangeControllerto hang one on an axis. Pinningstartandendreplaces the range the axis would have derived from its own points, and the chart records every window a gesture moves to back onto the same controller, so reading it always answers with what is on screen even if nothing was ever set on it.dateTimeStartanddateTimeEndread and write the ends as instants for an axis of dates.- Sharing one controller between two charts ties their windows together, which is how a price chart and the volume chart beneath it stay on the same sessions. Pinning one end leaves the other following the axis, so a series that grows can still stretch a window nobody pinned.
VarietyCartesianChartControllerandVarietyCartesianChart.controller, and itsupdateDataSource. Points are added, inserted, replaced or removed on the list the series was built with — the caller's own list, so there is never a second copy to fall out of step — and the chart repaints without replaying its entrance animation and without losing whatever window the reader has zoomed or panned to. This is what a chart fed one reading a second has wanted: it used to be rebuilt, so the line redrew itself from nothing on every sample.VarietyAnimationType.realtime, which keeps a chart from replaying its entrance animation when its series are replaced.loadandlinearare honoured now too —loadeases out andlinearadvances evenly, where before the choice was declared and never read.VarietyCartesianChart.onMarkerRender, asked once per marker that stands for a point. Answer with a colour, a size or a shape to draw that point differently from its neighbours, ornullto draw no marker at all. The mean, the outliers and the inner points of a box plot are left alone: they answer to their own options and stand for no reading to be asked about.VarietyCartesianChart.onDataLabelTapped, which answers with the point the caption names. A caption often sits clear of the point it belongs to, so a chart that only offered the point itself left the caption dead.
Changed
VarietyRangeControllerclamps a pinned end to what the data reaches rather than refusing it: a window has to be a window onto something.
Fixed
- A tooltip card was measured with the height of the chart it floats over, so on any chart shorter than the card the card's own column was squeezed and reported a RenderFlex overflow: the striped band along the bottom, and content that could not be read. A card is an overlay and is measured at its natural size now, with only its width bounded. One that still cannot fit is pushed against the edge of the chart and cut off there rather than being crushed into a layout that cannot hold it.
Covered by test/widgets/variety_range_controller_test.dart, test/widgets/variety_update_data_source_test.dart, test/render/variety_marker_and_label_callbacks_test.dart and test/widgets/variety_tooltip_anchor_test.dart. The example app grew a "Driving a chart" page showing two charts on one window, a reading a second, a marker that picks itself out and a caption that answers a tap.
0.5.22 #
A category key is the instant, and the label families answer together #
Fixed
- A date category axis told its categories apart by a rendering of the instant, so it could only tell apart instants a millisecond apart and folded two readings inside the same millisecond onto one slot: a column drawn on top of another one, and a lookup that landed a point on its neighbour. The key is the instant itself now, and it is finer than the text it replaces. It is also a fraction of the cost — the text was rebuilt several times over per point per frame and nothing ever read it.
- A pie and a funnel ignored
VarietySeries.dataLabelMapper, so a chart that named its own captions was captioned with the numbers instead. Neither consultedshowZeroValue, so both printed the zero the settings had asked to hide. Every family that draws a data label resolves its caption through one place now, which is what stops a mapper, abuilderand the zero switch from meaning different things on a column, a slice and a funnel segment. - A pie and a doughnut ignored the theme for those captions:
VarietyChartTheme.dataLabelTextStylewas never read, so a caption stayed at eleven pixels whatever the theme asked for, anduseSeriesColorchanged nothing — a slice was always captioned in white. A slice is filled with its own colour, so white is still the last resort, but it is no longer the only answer.
Changed
- A pie slice that names no caption of its own is captioned with its value the
way every other family captions one — a whole number reads
10where it used to read10.0, and a large one carries the sameK/M/Bsuffix a column would. A slice that carries a label still shows that label. A funnel segment that captions nothing no longer draws an empty label.
Performance
- A point of a stacked series added up every series underneath it each time it was asked where it sat, so placing a stack was quadratic in its height: four series of a thousand points took 2.4 ms to lay out, eight took 12.8 ms and sixteen took 67.8 ms. The totals of a value axis are summed once now and each point reads its running total off that.
VarietyCartesianGeometry.plotRectis worked out once per chart rather than once per point placed, which spent a rectangle per point per frame on a value that cannot change.- The highlight pass behind the trackball walked every highlighted point to draw a marker for each, even when the settings asked for no markers. It returns as soon as it knows they are off.
0.5.21 #
Captions resolved once, and the markers the settings never reached #
Fixed
- Every caption on a date category axis was worked out on its own, and working one out means formatting every value the axis carries. Building the captions was therefore quadratic in the number of categories: a recording of two thousand instants cost millions of date formats to label. The pattern is resolved once per axis now and each caption only formats its own value. The captions themselves are unchanged.
- The cache of laid-out text runs was emptied completely once it filled up, so
every caption after that point paid for a fresh layout — and a chart whose
captions each carry a colour of their own can fill it up by itself. The oldest
runs are dropped one at a time instead, which keeps the runs the rest of the
frame is about to ask for.
VarietyElementRenderer.cachedRunCountreports how many the cache is holding. - A step area and a hi lo series wrote the glyph, the diameter and the colour of
their markers into the geometry themselves, so
markerSettingswas ignored twice over: it neither turned the markers on nor named what to draw. Both ask the same way every other family asks now. - A spline area, a step area, a range area and a spline range area each declare a marker diameter of five, and each fell through to the generic six.
- The builders that place markers were passed a diameter none of them read; the parameter is gone, so one place decides that now.
0.5.20 #
The readings an indicator feeds itself, and the options nothing read #
Fixed
- An indicator reads a closing price when the point carries one and its own
value otherwise, but the list it produces is a set of derived readings rather
than bars, and it used to keep the closing price of the point it was computed
from. A second pass therefore averaged the original prices again: the
triangular average collapsed back into a simple average, the MACD signal line
was an average of closes instead of the MACD line, and the stochastic
%Dcame out at price level while%Ksat between 0 and 100. A derived point no longer carries the bar it came from. - A window holding a point with no reading now yields a point with no reading, rather than counting the gap as a zero and tilting the start of the line towards the origin.
- The track behind a radial bar was told apart from the reading in front of it by asking the point whether it carried a value, but the track reuses the reading's own point, so the answer was always no and every track was outlined as though it were a reading. The flag the geometry sets for a track decides now, which is what hit testing already used.
VarietySeries.onPointTapis called. The per-series double tap and long press handlers were connected and this one, declared and documented beside them, was not, so a chart listening for a tap on a series never heard anything. All three go through one path now.VarietySeries.dataLabelMapperdecides the caption for a point. It is asked beforeVarietyDataLabelSettings.builder, and the chart-wide label resolver still sees the result. Setting it used to change nothing.VarietySeries.pointColorMappercolours the point it is asked about, ahead of the point's own colour and the series colour. Returningnullkeeps whatever the point would have been drawn in.VarietySeries.legendIconShapenames the glyph the legend draws, ahead oflegendIconType, because a marker shape is the more specific way to describe one.VarietyMarkerShape.noneasks for a caption with no glyph. The answer is available asVarietyLegend.iconFor, so an entry built by hand draws the same glyph the default one would.VarietyLegendSettings.spacingandrunSpacingare read. The legend widget kept private copies of both with 16 and 8 written into them, so a chart that set them saw no change.VarietyChartTheme.majorTickLineColoris read when neither the axis'majorTickLines.colornor itsaxisLineColoris set. Its getter falls back to the axis line colour, so a theme that never names it draws as it did.VarietyDataLabelSettings.marginis read. It is the room left around a caption inside its card, which used to be a fixed 4 by 2; the default is that same 4 by 2, so an untouched chart draws identically.VarietyMultiLevelLabels.overlapis read. Neighbouring brackets meet exactly by default, and asking for an overlap bleeds each side by half of it.VarietyAnnotationRegion, declared with no reader at all, now decides how far a rule annotation reaches: the plot area by default, or the whole chart withVarietyAnnotationRegion.chart, which lets a line run through the axis gutters.
Changed
- The room kept below a horizontal axis is now worked out from the same style
the caption block is drawn with. The axis title is measured instead of being
assumed to be 22 pixels tall, a row turned by
rotate45orrotate90is reserved at the angle it is painted at, the second row ofmultipleRowsis allowed for, a secondary value axis measures its captions through the themed label style, and the title of an extra value axis is painted in the themed title style. A larger themed style used to be honoured where the text was painted and ignored where the room for it was decided. VarietySeries.legendIconShapeis optional now rather than defaulting to a circle. That is what lets a series say nothing about its glyph and follow the legend, the way its documentation already described.
0.5.19 #
A rounded doughnut slice draws as a ring again #
Fixed
- A doughnut slice that asked for a
cornerRadiuswas outlined by dropping from the outer arc straight across the band onto the inner one, so the slice filled the middle of the chart and read as a spike instead of a ring. The inner arc's sweep was also negated twice, which turned it back the way it came. The outline is built from the four turns a band really has — outer arc, right edge, inner arc, left edge — with the corner radius turning each join. A pie, which has no inner arc, and a radius wider than half the band keep the plain outline. - Both arcs of a slice now give up the same angle to their corners, taken from the radius halfway across the band. Measuring each arc at its own radius gave the inner and outer gaps different angles, so a join read as a wedge, and the narrow slices tore open outright. The angle is still capped at a quarter of the sweep, so the two ends of one narrow arc can never meet and swallow it. A four percent slice now draws as cleanly as a fifty percent one.
- The slot handed to a
centerwidget was a fixedradius * 1.1, which ignores bothradiusFactorandinnerRadiusFactor. On the demo's doughnut that made the slot 4% wider than the hole it was meant to sit in, so a centre widget carrying its own background would have overlapped the ring — the demo's two short lines were small enough to hide it. The slot is measured from the hole now, taking the smallest hole when several doughnuts share the plot, and falls back to the old behaviour when no series leaves a hole at all.
Changed
- A corner radius leaves a gap at every join, because a corner can only turn in the arc it gives up. That is what the option is for, but it is not what the doughnut with a centre widget is there to show, so that chart no longer asks for one and its ring reads whole. The radial bar chart still asks for a radius and shows the corners.
0.5.18 #
Tapping a legend entry now takes the series off the plot #
Fixed
VarietyLegendSettings.toggleSeriesVisibilitypromised that tapping a legend entry hides the series and tapping it again brings it back. The callback reported the new state correctly the whole time, but the set the plot is actually filtered by was never changed: a tap on a visible series tried to remove it from a set it was not in, and a tap on a hidden one tried to add it back to the same set. Both directions were inverted, so tapping never hid anything. The set is toggled the way the callback already described, and whatonLegendTappedreports is unchanged.
Added
- A hidden legend entry is struck through and greyed out when the legend draws
the entry itself, so the state is readable from the legend alone. An entry
built by
legendBuilderoritemBuilderis dimmed instead, because nothing in the legend can restyle a widget it did not build. The cue is applied whether or not a tap handler is attached, so a legend driven from outside the chart still shows which series are off. VarietyColumnSeries.spacingandVarietyBarSeries.spacingopen a gap between the rectangles of a group. The gap is taken out of each rectangle's own width —spacing: 0.3leaves a little under a third of it empty — so a group still fits the slot it was given, and the default of0draws exactly what it always did.VarietyCandleSeries.enableSolidCandlesleaves rising candles hollow while falling ones stay solid. It is the convention on some markets and it keeps a dense series readable, since only half the candles carry a block of colour. It defaults totrue, which is what every version before this one drew.VarietyCandleSeries.showIndicationForSameValuesdraws a short horizontal mark for a session whose open, high, low and close are all equal. Such a candle has no body and so drew nothing at all, which left a flat session missing from the chart.
Changed
- The line and area demo pages gain a two value axes chart and a grouped column chart with a gap, and the financial page shows hollow rising candles, so each of the options above can be seen rather than only described.
0.5.17 #
Four more places where a frame did the same work twice #
Nothing here changes what a chart looks like either.
Performance
- A percentage stack worked out its total once per series per point, and working one out walks every series, so the layout was quadratic in the series count: four series of a thousand points took 2.8 ms, eight took 13.3 ms and sixteen took 68.5 ms. The totals are summed once per value axis now and read from there: 1.5 ms, 3.3 ms and 10.2 ms for the same three charts, which scales with the data again instead of with its square.
- The horizontal captions were laid out before the label cap and the overlap thinning threw most of them away, so a chart of two thousand categories laid out two thousand of them on every frame in order to draw about twenty. The cap and the duplicate-caption pass decide from the index and the text alone, so they run first now and only the survivors are measured: the frame of a three series, two thousand category column chart went from 86 ms to 16 ms, and the single series case from 78 ms to 7.3 ms. Painting a two thousand point column series went from 75 ms to 5.5 ms for the same reason.
- Text runs are laid out once and handed out from a bounded cache, so drawing a caption on the next frame is a lookup rather than a fresh layout. Measuring a caption for the axis inset and then drawing it also cost two layouts for the same run; the widget now measures through the painter's cache, and reserving room for an upright label row measures one sample instead of every caption, because a single line is as tall as its style whatever it says.
- Of the three or four geometries a frame builds, only the painted one builds elements. The base geometry is read for its four limits and the range probe for its two, so the other two skip the element pass, which is about three quarters of a frame's layout work on a large data set.
Added
VarietyElementRenderer.runForandVarietyElementRenderer.measure, the cached layout the painter draws from and the widget measures with.
Notes
- Runs are keyed by the text and by the style's own values, so a theme change
produces a different style and a different run. An application that swaps its
default font while leaving the same
TextStylein place would be handed the runs measured before the swap, until the map is dropped atVarietyElementRenderer.runCacheLimit.
394 tests before, 398 after.
0.5.16 #
A cheaper frame, and one list that stopped growing #
Nothing here changes what a chart looks like. It changes how much work each frame costs, and it fixes a list that never stopped growing.
Fixed
- The axis label rectangles the painter hands back were appended to on every
frame rather than describing the current one. A chart that repainted for a
second collected hundreds of stale rectangles: memory that grew without
bound, and a hit test that could match positions no longer on screen. Both
hit lists are emptied at the start of a paint now. The regression test in
test/render/variety_render_hot_paths_test.dartreports twelve entries where four are expected on the previous revision.
Performance
- A category chart looked a point's slot up by scanning the whole category list, once per point and again for every element built, which made the layout quadratic in the number of categories. Three series of two thousand points took 19.6 ms to lay out and now take 3.6 ms. The lookup goes through an index built alongside the keys, so what it resolves is unchanged.
- The inset probe that is built on every frame purely to measure the axis furniture no longer builds the series elements, which nothing on that path reads: 0.9 ms against 3.6 ms for a full build of the same data.
- Markers no longer build a path and two paints apiece. The glyph is built once per shape and size, the circle and the square go straight to the canvas, and the paints are reused with their colour reassigned. A five thousand point scatter went from 14.4 ms a frame to 4.6 ms, and a two thousand point line with markers from 7.6 ms to 2.1 ms.
- The dashes of a dashed path are collected into a single path and drawn once, instead of a path being cut out and drawn for each dash: a two thousand point dashed line went from 5.9 ms a frame to 3.7 ms.
Added
VarietyCartesianGeometry.buildElements. Passingfalseskips the element pass for callers that only need the ranges, ticks and captions;elementsis then empty, so nothing that draws should be handed a geometry built that way.
Notes
- A series dense enough to draw several points per pixel still costs
proportionally more to dash, because there are proportionally more dashes.
That is what
VarietyFastLineSeriesand itsdecimationFactorare for.
0.5.15 #
The rest of the dead configuration, and the first export hook #
An option that is declared, documented and then never read is worse than no option at all: the chart silently ignores what the caller asked for. The sweep that has been clearing that list now comes back empty — every field it found is read by the renderer — and the options the API was missing are added with them.
Added
VarietyAxis.minorTickLinesis drawn at last, withsize,widthandcolor, on both axes and on every extra axis. Minor ticks followminorTicksPerIntervalexactly as before; only their styling was missing.VarietyAxis.maximumLabelscaps the captions at that many per 100 logical pixels, which is where the default of three comes from. Captions are dropped on a regular stride, and the two ends are always kept.VarietyAxis.labelAlignmentplaces a caption by its leading edge, its centre or its trailing edge.VarietyMultiLevelLabels.mergefolds neighbouring groups that share a caption and touch into one bracket, androwHeightsets how far a nested row sits below the axis.VarietyPlotBand.associatedAxisStart/associatedAxisEndbound a band on the opposite axis, turning a full-height stripe into a rectangle;VarietyPlotBand.gradientreplaces the flat fill;dashArrayoutlines the band.VarietyErrorBarSeries.directiondraws a one-sided whisker, andverticalErrorValue/horizontalErrorValueplus the four per-side magnitudes give each direction its own reach (the per-side ones apply toVarietyErrorBarType.custom).VarietyAxis.initialZoomFactor/initialZoomPositionopen the chart on a window rather than the whole range, still pan and zoom-able from there.VarietyCartesianChart.paletteoverrides the colour cycle for one chart.VarietyCartesianChart.onChartTouchInteractionDown/Move/Upreport every raw pointer position, whether or not it landed on a series.VarietyCartesianChart.onPlotAreaSwipefires when a pan runs out of data at one end of the axis, andloadMoreIndicatorBuildershows a widget over the bottom of the plot while the reader is sitting at that end — the pair an infinite scroll needs.toImage({double pixelRatio})onVarietyCartesianChartState,VarietyCircularChartStateandVarietyFunnelChartState, reached through aGlobalKeyof the state type. Every chart now owns aRepaintBoundary, so the export is the chart and nothing behind it.VarietyZoomPanBehavior.onZoomingreports the window on every frame of a gesture, andonZoomResetreports a return to the full range.VarietyTooltipBehavior.tooltipPositionpins the card to the point or lets it follow the pointer, andheadercaptions it.VarietyLegendPosition.autopicks a side from the shape of the chart: taller than wide puts the legend underneath, wider puts it at the right.VarietyMarkerShape.nonedraws no glyph at all, which is how one series in a chart disables markers for itself.VarietyLegendIconType.pentagon/verticalLine/horizontalLine, and a series' ownlegendIconTypenow wins over the chart wide setting.VarietyLabelIntersectAction.trimshortens a caption that would collide instead of dropping it.VarietyAxisBorderType.withoutTopAndBottomjoins the two value axes with upright rules and leaves the top and bottom edges out.VarietyTrackballLineType.nonedraws no guide, andVarietyActivationMode.doubleTaplets a trackball or crosshair claim the double tap when zoom is not using it.VarietyDataLabelSettings.connectorLineSettings(aVarietyConnectorLineSettingswithlength,width,colorandtype) draws a line from a caption back to its point,opacityfades the caption and its card, andshowCumulativeTotalcaptions a stack with its running total.VarietySeries.selectionColorcolours the highlight of a selected point;VarietySeries.legendIconType,initialSelectedDataIndexesandanimationDelayare all consumed now, so a series can pick its own legend glyph, open the chart already selected, and be staggered behind the series before it.VarietyEmptyPointSettings.showMarkerdecides whether a point whose value was substituted for an empty one gets a marker. It defaults tofalse, so an interpolated reading no longer passes itself off as a measurement.VarietyTooltipBehavior.showDurationwaits that long before a hovered card appears, so a pointer crossing the plot no longer flashes one at every point it passes.VarietyZoomPanBehavior.enableDeferredZooming: falsekeeps the zoom state moving during a pinch but holds the repaint back until the fingers are off, which is what makes pinching a very large data set cheap.- New value types:
VarietyLabelAlignment,VarietyErrorBarDirection,VarietyTooltipPosition,VarietySwipeDirection,VarietyChartTouchArgs,VarietyConnectorLineSettings.
Changed
- Breaking.
VarietyAxis.labelAlignmentis now aVarietyLabelAlignment(start,center,end), the axis counterpart of the caption alignment the data labels use. It used to be aVarietyLabelPosition, which described a different idea. The default iscenter, so nothing moves for a chart that does not set it. - Breaking.
VarietyTrackballVisibilityMode.alwaysis nowvisible, the spelling used everywhere else in the API. - Breaking.
VarietyMultiLevelBorderType.curlyBracketandbraceare nowcurlyBraceandsquareBrace, andwithoutTopAndBottomwas added.
Fixed
- A plain pan reported nothing, so
onZoomEndnever fired after panning. It does now, and a pan that runs out of data reports the end throughonPlotAreaSwipe. - The room reserved for a multi-level label band was hard coded to 22 pixels a
row while the painter drew the rows at
rowHeight. Both now read the same value, which is the same half-wired mistake the tick label style used to have.
0.5.14 #
Multiple horizontal axes #
VarietySeries.xAxisName was declared, mirrored in every series constructor and
read by nothing. The vertical family already had its counterpart working
(secondaryYAxes + yAxisName); the horizontal family never went plural. It
does now.
Added
VarietyCartesianChart.secondaryXAxes, bound from a series throughVarietySeries.xAxisNameexactly as secondary Y axes are bound throughyAxisName. Every extra axis resolves its own range, its own ticks and its own captions, prints them in a row of its own under the plot area, and gives the columns on it a slot width of their own. A name that matches no axis falls back to the primary one.VarietyCartesianGeometrygained the per-axis horizontal state its vertical counterpart already had:xAxes,seriesXAxis,xAxisIndexOf,xAxisFor,axisXTypes,axisXMinimums/axisXMaximums/axisXIntervals/axisXTickOrigins,axisCategories,axisCategoryValues,axisSlotCenters,axisSlotWidths,axisDateTimeTicks,pixelXOn,xTickPositionsOn,xNumericTicksOn,xMinorTickPositionsOn,xTickLabelOnanddateTimeTickLabelOn. The old singular members (categories,slotCenters,slotWidth,xMinimum,xMaximum,xInterval,xTickOrigin,dateTimeTicks,xNumericTicks,xAxisType,pixelXFor,categoryIndexOf) still read and behave exactly as before, now as views onto axis 0, except thatpixelXFortakes a series index instead of a series andcategoryIndexOftakes an optional axis index.
Changed
- Two horizontal axes can now share one plot area, so a series can leave the primary axis' scale. That is the point of the feature, but it means a series that names a second horizontal axis no longer widens the primary axis' range, and its points are drawn against the other axis' pixels. Charts that name no axis are unaffected down to the pixel.
- Column grouping now looks only at the series standing on the same slot axis. Previously the group width was divided by every column-like series on the chart and the offset was taken from the series' position in the whole list, so a column chart with a line series interleaved between two column series gave the later column the wrong offset and a width divided by the wrong count.
- The trackball snaps each series on its own horizontal axis: a category axis to the nearest slot, a value axis to the nearest point by pixel. On a single-axis chart this is the old behaviour; on a multi-axis chart it is what keeps a series on a second axis reachable.
- Grid lines and plot bands stay with the primary horizontal axis, and so does the zoom window, matching how secondary Y axes have always been treated. A window expressed in the primary axis' units means nothing on another scale, so a second axis always shows its whole range.
Fixed
VarietyChartTheme.axisLabelTextStylenow reaches every tick label it claims to. The primary captions honoured it, but the secondary value axes, the extra horizontal axes and the room the widget reserves for all of them fell back to a hard-coded 11 px style instead, so a chart that asked for larger labels got them on the primary axis only and clipped them everywhere else. The three places that have to agree — the caption, the space reserved for it and whatever is stacked underneath it — now share one chain (axis.labelStyle, then the theme, then the built-in default), and the multi-level labels measure the primary row with it too.
Covered by test/render/variety_multi_x_axis_test.dart and test/widgets/variety_axis_label_style_test.dart, and the example app's Advanced page grew a two-axis dyno chart.
0.5.13 #
Auto scrolling and the anchored value range #
Two more axis fields were declared, documented and never read.
Added
autoScrollingDeltaandautoScrollingModenow keep a window of the axis in view. The delta is a span in axis units, so on a category axis it is the number of points: a delta of 20 keeps the last twenty categories visible and leaves the rest reachable by panning.VarietyAutoScrollingMode.startkeeps the oldest points instead of the newest, fewer points than the delta shows all of them, and an appended point slides the window along. A window the reader has moved by hand is left alone, so the delta never fights a gesture.anchorRangeToVisiblePointsnow decides whether a value axis fits itself to the points inside the visible window. It is on by default, which is what makes panning into a long series rescale the value axis to what is on screen.
Changed
- Zooming one axis can now rescale another. With the default
anchorRangeToVisiblePoints: true, narrowing the x window re-fits the value axis to the points left in view, so the reading under the pointer is no longer fixed on the y axis while x zooms. SetanchorRangeToVisiblePoints: falseon the value axis for the previous behaviour, where its range covers every point.
Covered by test/widgets/variety_auto_scrolling_test.dart and the
anchorRangeToVisiblePoints pair in test/widgets/variety_pinch_zoom_test.dart.
0.5.12 #
The axes can cross where the data says #
VarietyAxis.crossesAt was declared and never read, so there was no way to run
the x axis through zero -- the line, its marks and its captions all stayed on
the bottom edge. A chart of signed data had to keep a baseline it did not mean.
crossesAt on an axis now moves the opposite axis line to that reading: the
line lands on the value, the marks point away from it and the x captions sit on
whichever side of it they have room on, so a line through the middle of the
plot does not print its labels over the series. Readings outside the visible
range clamp to the edge.
An axis that sets no crossesAt draws exactly what it drew before, including
opposedPosition handling.
Covered by test/render/variety_axis_crossing_test.dart.
0.5.11 #
Tick marks were half wired and half missing #
Each axis carried majorTickLines and majorGridLines style objects that
nothing read, and tickPosition that nothing read either. Worse, the marks
that did get drawn were nested inside the caption loop, so showLabels: false
silently took the tick marks with it, and the x axis never drew marks at all
however showTicks was set.
Fixed
majorTickLines(size, width, colour) andmajorGridLines(width, colour, dash array) now reach the renderer on both axes.majorTickLineswins over the plaintickLength/ axis colour fields, the same relationshipminorGridLinesalready had with the grid fields.tickPositionnow decides the side a mark is drawn on, on both axes and on extra axes, andVarietyTickPosition.insideflips it into the plot area.- Tick marks no longer depend on
showLabels: the two are separate switches, which is what the fields always implied. - The x axis draws tick marks, which it never did.
The guides read their lineType #
VarietyTrackballBehavior.lineType and VarietyCrosshairBehavior.lineType
were both declared and never read: the trackball always drew a vertical guide
and the crosshair always fell back to its two booleans. Both now resolve
through the same switch, and the crosshair's showVerticalLine /
showHorizontalLine pair only decides when no lineType was given.
The trackball reads its visibilityMode #
VarietyTrackballBehavior.visibilityMode was declared and never read. always
keeps the trackball past its hide delay, hidden refuses to show it at all and
auto is the behaviour that was already there. shouldAlwaysShow is kept as
the older spelling of always.
Covered by test/render/variety_axis_tick_lines_test.dart and test/widgets/variety_trackball_visibility_test.dart.
0.5.10 #
splineType now picks the interpolation #
VarietyLineSeries.splineType (and the four other line-like series that carry
one) was declared, documented and then never read: every curved series was
drawn with the same cardinal spline, so natural, clamped and monotonic
all produced the identical curve.
A curve is a cubic Hermite run, so the four kinds differ only in the tangent carried at each point, and each axis is interpolated as its own scalar sequence. That makes the curve a parametric spline rather than a function of x, which is what the existing shape already assumed.
cardinal-- Catmull-Rom, the previous behaviour and still the default, so charts that never set the field are unchanged.natural-- solves the tridiagonal system with a zero second derivative at each end.clamped-- the same system with a flat tangent pinned at each end.monotonic-- Fritsch-Carlson, which pulls the tangents back inside the monotone cone so the curve cannot overshoot the points it passes through.
VarietySeries gains a splineType getter defaulting to cardinal, so the
renderer can ask any series for one; the non-line series ignore it.
Covered by test/render/variety_spline_type_test.dart, which pins the overshoot
behaviour of cardinal against monotonic and would have passed trivially
before, since every kind drew the same path.
0.5.9 #
Tooltip styling reaches the circular and funnel charts #
VarietyCircularChart and VarietyFunnelChart only had an enableTooltip
flag and a body builder, so the card they showed was always the default one --
its fill, border, opacity and marker dot could not be changed at all, and
neither could the way a value was captioned.
Added
tooltipBehavioronVarietyCircularChartandVarietyFunnelChart, the sameVarietyTooltipBehaviorthe cartesian chart takes. All four chart widgets now style and format their tooltips the same way, and the card is built from one shared chrome.enableTooltipremains the on/off switch and is now combined withVarietyTooltipBehavior.enabled, so either one hides the card.
0.5.8 #
Value boxes on the axes #
While a crosshair or trackball is up the reader has to look down at the axis to learn which x the guide sits on, and across to it for the y. The guide carried no such reading, so the axis had to be traced by eye.
Added
VarietyAxisTooltipSettings, and anaxisTooltipfield on bothVarietyCrosshairBehaviorandVarietyTrackballBehavior. The x box rides on the x axis under the guide, the y box on the y axis level with the point.- The caption comes from the axis, so
labelFormatter,numberFormatanddateFormatshape the text; the settings cover the fill, text colour, outline, corner radius and padding.VarietyAxisTooltipSettings.hiddenturns the boxes off. axisTooltipBackgroundColor,axisTooltipTextColorandaxisTooltipBorderColoronVarietyChartTheme, all falling back to the tooltip colours.varietyFormatTooltipValue, the value formatter the tooltip cards used, is now shared rather than private to the card.
0.5.7 #
A chart theme you can actually reach #
VarietyChartTheme existed but only carried the six colours the painter
happened to need, and there was no way to hand a chart your own. Everything
else -- the minor grid, tick marks, axis titles, chart title, legend, plot
area, crosshair, rubber-band selection, data labels and the series palette --
was either derived from the ambient ThemeData or hardcoded.
Added
- Around twenty more fields on
VarietyChartTheme, covering grid lines (major and minor), tick marks, axis titles, chart title and its fill, legend text, title and fill, plot area fill and border, data labels, crosshair, the rubber-band selection rectangle, the shared tooltip's separator, and seven text styles. VarietyChartTheme.copyWith, so one override never disturbs the rest. Every field left out falls back to the six base colours, which in turn come from the surrounding theme, so nothing needs configuring for a default look.VarietyChartThemeScope, which applies a theme to every chart below it.VarietyChartTheme.ofreads the nearest scope first and derives from the ambientThemeDatawhen there is none.palette, which replaces the built-in series colours for the whole chart.
Fixed
VarietyZoomPanBehavior.selectionRectColorandselectionRectBorderColorwere declared but never read: the rubber-band rectangle was painted with a hardcoded blue. It now honours the behaviour, then the theme.
0.5.6 #
Rounds out the option surface #
A pass over the reference library turned up a set of options that were either missing or declared and then never read. Everything here is wired through to the renderer and covered by tests.
Added
VarietyRangePaddinggainsnormal,additionalStart,additionalEnd,roundStartandroundEnd, so an axis can be padded at one end only.additionalnow buys half an interval past the rounded ends, which it needs to be visible at all.VarietyMarkerShapegainspentagon,verticalLineandhorizontalLine.VarietySelectionType.clusterselects one point per series at the tapped slot.VarietyDataLabelSettingsgainsoffset,useSeriesColor,backgroundColor,borderColor,borderWidth,borderRadius,angleandshowZeroValue.VarietyTooltipBehaviorgainsformat,decimalPlaces,canShowMarker,borderColor,borderWidth,opacityandelevation.formatwraps the value with{value}, so'{value} kg'prints12 kg. Both tooltip cards -- the single point one and the shared trackball one -- now read the same behaviour, so a fill, border or opacity applies to every tooltip the chart shows. AtextStylecolour also drives the card's heading and caption.VarietyCartesianChartgainsplotAreaBackgroundColor,plotAreaBorderColor,plotAreaBorderWidth,borderColorandborderWidth.
Fixed
VarietySelectionBehavior.selectedColorandunselectedOpacitywere declared but never applied, so a selection changed nothing on screen. Selected points now take the configured colour and border, and series that hold nothing selected fade back byunselectedOpacity.
0.5.5 #
A numeric x axis honours interval, and keeps it through a zoom #
VarietyAxis.interval was read into the geometry and then never used: a numeric
x axis split [minimum, maximum] into desiredIntervals equal parts instead. A
caller whose x values are whole numbers — a point index, a slot number, anything
counted — therefore got grid lines and captions between its points, and
rounding a caption to the nearest point only hid part of the mismatch.
Zooming made it worse. A gesture overwrites xMinimum/xMaximum with the
visible window (_resolvePrimaryRange), and the ticks were then recomputed from
that window, whose bounds are never whole. Measured on a 1400px plot, every grid
line sat 22px away from the point it was meant to mark.
The interval is now honoured the way the secondary axis has always honoured its
own in yTicks.
Fixed
- A numeric x axis lays its ticks on a fixed grid,
xTickOrigin + k * interval, and clips that grid to the visible window. The origin is the axis minimum before the window is applied, so zooming and panning leave every tick on the same value instead of recomputing it from the window. - Grid lines, captions and the measured label band all come from that one list
(
VarietyCartesianGeometry.xNumericTicks), so a caption can no longer describe a different value than the line it sits on — or than the band it reserved space for. - When the visible window is narrower than a step and a half the whole grid
would vanish, so the step subdivides — halving, or dropping to
1— which keeps every tick a whole multiple of the caller's interval. - Without an interval a numeric axis still splits the visible span into
desiredIntervalsequal parts, unchanged.
0.5.4 #
Category axes no longer fold a day of readings into one slot #
A category axis keyed each slot on the caption it would print, and a date time caption defaults to a date only pattern. Every point of a day therefore landed on the same slot: three readings a few seconds apart were drawn on top of each other, and the line had nothing to spread across, so the points did not line up with the times on the axis.
Fixed
- Categories key on the instant a point carries, not on its caption, so two readings a second apart stay in buckets of their own even when their captions happen to match.
- Captions are derived separately, and a pattern is chosen fine enough to give
every category a caption of its own, so a minute of readings is labelled
11:30:01,11:30:11,11:30:31rather than one repeated15 Sep. - Slots and captions are now kept apart, so a caller who pins a coarse
dateFormatgets the repeated captions they asked for while every point still keeps its own slot.
0.5.3 #
Trackball guide follows the point you tapped #
The guide used to be anchored on the first series' nearest point, whatever the touch was actually near. Series carry their own x values -- time stamped data almost always does -- so a tap on a point of the second series could put the guide more than a hundred pixels away, and every marker in the shared tooltip sat beside it rather than on it.
Fixed
- The guide is anchored on the point closest to the touch across all series, so the crosshair lands on the point the reader actually aimed at.
VarietyTrackballBehavior.displayModeis now honoured. It was declared but never read, so every mode behaved likegroupAllPoints:groupAllPointspulls every series onto the guide, so the markers line up with the guide and the tooltip.floatAllPointsleaves each point on its own x.nearestPointreports the single closest point.nonereports nothing.
0.5.2 #
Date time axis captions stay unique #
A date time axis could print the same caption more than once, which made a
chart driven by very little data look like it had duplicate points. The
caption pattern used to be derived from the span alone, so it could be coarser
than the tick interval: milliseconds across half a minute were all captioned
10:28:01, ten days carved into hours repeated each dd MMM twenty-four
times, and a range crossing midnight repeated 10:00.
Fixed
- The caption pattern is now resolved against the ticks that were actually generated, walking from the shortest pattern that reads naturally to one that keeps every caption distinct.
- Two ticks can no longer be captioned the same way, even when the axis pins a
dateFormator alabelFormatterthat cannot tell them apart: the painter keeps the first caption and drops the rest. - Automatic intervals are no longer taken at face value. The step is sized so the axis lands near eight ticks, which also stops a ten day range from being carved into two hundred and forty hourly ones. Ranges under two seconds now get millisecond ticks instead of a single label.
0.5.1 #
Gesture zoom and pan reworked around a normalised window #
Pinch, wheel, double-tap, pan and selection zooming now use a normalised
(zoomFactor, zoomPosition) window model instead of raw data-space
minimum/maximum pairs. Each
axis keeps the visible fraction of the full range plus where that window
starts, so a gesture only ever touches two numbers and the focal point stays
pinned while it is applied.
Fixed
- The mouse wheel could zoom in but never back out: the target window was clamped against the already-zoomed window instead of the full range.
- A pinch anchored the vertical axis at the mirrored position, because the pixel-to-value helper treated the top of the plot as the axis minimum.
- Dragging vertically panned the plot against the finger. Both axes now move with the content.
Changed
- Selection zooming is driven by a long press: press, drag out a region,
release.
VarietyZoomMode.bothnow really does enable pinch, pan and selection at the same time, andVarietyZoomMode.selectionkeeps drag-to-pan. VarietyZoomMode.nonenow disables every zoom and pan gesture. It was previously ignored, somode: nonestill zoomed.- While selection zooming is active a long press draws the zoom region rather than activating a long-press trackball.
Removed a redundant null assertion in the trackball hit path that tripped
unnecessary_non_null_assertion.
0.5.0 #
CartLineSeries full parity (the last 4) #
Closed every remaining gap in the series parameter surface.
VarietyLineSeries now exposes all 37 parameters the chart API offers.
onCreateRendererlets you supply a custom element renderer per series. The painter keepsMap<int, VarietyElementRenderer>so each series is drawn by its own renderer instance.onCreateShaderis consulted for every path the series emits (fill and stroke); a non-null shader overridesgradientandborderGradient.onRendererCreatedfires once per series right after the renderer is built, which is the hook a renderer-scoped controller attaches to.selectionBehavioris now per-series. When set on a series it overrides the chart-wide one.
Implementation changes
- Added
VarietyRendererFactoryandVarietyShaderFactorytypedefs next toVarietyElementRenderer. VarietyElementgains an optionalseriesIndexso the painter can route every drawable to the right series. All geometry builders were updated to populate the index.VarietyElementRenderer.paintWith(canvas, element, series)lets the painter pass the owning series through.VarietySelectionBehavioris the per-series override and threads through the chart widget viaVarietyChartWidgetState._seriesSelection(int).
Behaviour parity
- Per-series selection takes precedence over chart-wide selection on tap.
- The shared layout-time renderer construction is wrapped in
try / catchso a broken user callback cannot bring down the chart.
0.4.0 #
Line series parameter parity #
- Added series-scoped parameters, shared by
VarietyLineSeriesand every other cartesian series:markerSettings,gradient,borderGradient,enableTrackball,initialIsVisible,initialSelectedDataIndexes,isVisibleInLegend,legendItemText,legendIconType,pointColorMapper,dataLabelMapper,onPointDoubleTap,onPointLongPress,animationDuration,sortFieldValueMapperandborderWidth. VarietyMarkerSettingsis now exported and shared by every series.dashArrayis accepted as an alias fordashPattern.- Per-series
animationDurationoverrides the chart-level one when it is longer. The controller is rebuilt when the series list changes. - Honour
initialIsVisibleat paint time andenableTrackballat hit-test time so hidden series do not appear in the trackball picker.
Behaviour parity #
- The double-tap gesture now dispatches the series-scoped
onPointDoubleTapcallback before the zoom-pan logic kicks in. - Long-pressing a point dispatches the series-scoped
onPointLongPress. - The legend widget now honours
isVisibleInLegend(defaulting the existing filter) and renderslegendItemTextinstead ofnamewhen supplied.
Fixed #
- Series-scoped
onPointDoubleTapwas declared but never wired. - A blank
geometry.datalookup made the double-tap callback crash when the click fell on the chart edge.
0.3.0 #
Spark charts #
- Added four dedicated widgets:
VarietySparkLineChart,VarietySparkAreaChart,VarietySparkBarChartandVarietySparkWinLossChart. Each one carries the full option set:plotBand,color,isInversed,axisCrossesAt,axisLineColor,axisLineWidth,axisLineDashArray,highPointColor,lowPointColor,negativePointColor,firstPointColor,lastPointColor,labelDisplayMode,labelStyleandtrackball. - Line charts add
strokeWidthanddashArray; area charts addborderWidthandborderColor; bar charts addborderWidthandborderColor; win/loss charts addtiePointColoras well. - Added
VarietySparkMarkerwithnone,all,high,low,firstandlastdisplay modes, five shapes, a fill colour, an outline and a size. - Added
VarietySparkLabelDisplayModewith the same six modes for data labels. - Added
VarietySparkPlotBandto shade a value range with an optional border. - Added
VarietySparkTrackballwith tap, double tap and long press activation, a line colour, width and dash pattern, a customisable tooltip card, atooltipFormattercallback,shouldAlwaysShowandhideDelay. VarietySparklineis now a thin adapter over the four widgets, so the original API keeps working.
The four widgets, VarietySparkMarker, VarietySparkPlotBand,
VarietySparkTrackball and all four enumerations were verified parameter by
parameter and have full coverage.
Behaviour parity
- The drawing area has no padding, so the first and last points sit on the edges and the highest value touches the top.
- The value scale follows the data only.
axisCrossesAtmoves the axis line and is clamped into the drawing area; it never stretches the scale. - A flat series, or a series with a single point, is drawn on the top edge, and a single point is centred horizontally.
isInversedmirrors the series horizontally, which is what the reference widget does, rather than flipping the value axis.- Marker colour resolution follows the reference priority: first, last, high, low, then negative.
- A data label is placed above a positive value and below a negative one, is pushed clear of the marker, and is clamped inside the drawing area.
- The widget sizes itself when its constraints are unbounded: a 16:9 box, or 480 x 270 when both axes are unbounded.
Multiple axes #
- Added
VarietyCartesianChart.secondaryYAxesso a chart can plot series against more than one value axis. - Added
VarietySeries.yAxisNameandVarietySeries.xAxisNameto bind a series to a named axis. - The layout resolves an independent range, tick interval and label format per axis, and the painter stacks the extra axes to the right of the plot area.
Fixed #
hideDelayon a spark chart trackball now starts counting from the gesture, not only from a pointer exit.
0.2.0 #
Expanded the library from a core skeleton to a broad feature set.
Series #
- Added
VarietySplineAreaSeries,VarietyStepAreaSeries,VarietySplineRangeAreaSeriesandVarietyFastLineSeries. - Added
VarietyBoxAndWhiskerSerieswith exclusive, inclusive and normal quartile modes, outliers, mean markers and inner point overlays. - Added
VarietyErrorBarSerieswith fixed, percentage, standard deviation, standard error and custom magnitudes, usable standalone or attached to any cartesian series throughVarietySeries.errorBar. - Added
VarietySplineTypeso splines can be cardinal, natural, clamped or monotonic. - Added
VarietySortingOrderto every series.
Empty points #
- Added
VarietyEmptyPointSettingswith thegap,zero,averageanddropmodes.
Axes #
- Added
VarietyLabelIntersectActionwithnone,hide,rotate45,rotate90,wrapandmultipleRows. - Added
VarietyRangePadding,VarietyEdgeLabelPlacement,VarietyLabelPlacement,VarietyTickPosition,VarietyAxisBorderType. - Added
isInversed,plotOffset,plotOffsetStart,plotOffsetEnd,maximumLabels,numberFormatand axis names. - Added minor ticks and minor grid lines through
minorTicksPerInterval,VarietyMinorTickLines,VarietyMinorGridLines,VarietyMajorTickLinesandVarietyMajorGridLines. - Added
VarietyMultiLevelBorderTypeso group brackets can be rectangles, braces or curly brackets.
Interactions #
- Added a full callback surface:
onLegendTapped,onTooltipRender,onDataLabelRender,onAxisLabelTapped,onActualRangeChanged,onZoomStartandonZoomEnd. - Added
VarietySelectionController,enableMultiSelectionandtoggleSelection. - Added
VarietyZoomAxisModeso a gesture can zoom the primary axis, the secondary axis or both. - Added trackball
lineType,displayMode,visibilityMode,markerSettings,hideDelayandshouldAlwaysShow. - Added
VarietyRenderingModewith an optional loading placeholder.
Legend #
- Added
VarietyLegendSettings: icon type, icon size, item orientation, overflow mode, alignment, title, per-item padding, responsiveness andtoggleSeriesVisibility. Icons are now painted per series kind.
Analysis #
- Added the accumulation / distribution indicator (
VarietyAdIndicator).
Tooling #
- Split the test suite into 14 files covering geometry, indicators, regression, formatting and widgets; 163 cases in total.
0.1.0 #
Initial release.
- Added
VarietyCartesianChartsupporting line, area, column, bar and scatter series, numeric and category axes, grid lines, data labels, legend and tooltip. - Added
VarietyCircularChartsupporting pie and doughnut series with slice exploding and centre widgets. - Added an entrance animation, hover/tap hit testing and custom tooltip builders.
- Added a full multi-platform example application.