hand_drawn_analytics library

A bridge between analytics_toolkit (a rendering-agnostic query engine) and hand_drawn_toolkit (sketchy chart and table widgets).

Configure a hand_drawn_toolkit chart as a styling template (with data: null), hand it plus a typed analytics query to one of the HandDrawnAnalytics* widgets, and the bridge runs the query, maps the result into the toolkit's data class, and renders it — defaulting only the values you left unset, never reinterpreting your query.

Classes

AnalyticsScalarCard
A self-contained card that runs a single scalar query and shows the result as a big number inside CardChrome.
AnalyticsScope
Carries shared bridge configuration down the widget tree for the common one-config-per-page case.
AnalyticsScopeData
The configuration an AnalyticsScope publishes to its descendants.
AnalyticsWidgetBuilders
Consumer-supplied widget builders keyed by the bridge's mapped data class.
AsyncData<T>
The load completed successfully with value.
AsyncError<T>
The load failed with error.
AsyncLoading<T>
The load is in progress; no data or error yet.
AsyncValue<T>
The tri-state of an in-flight asynchronous load: loading, data, or error.
BridgeAnalyticsError
Wraps an upstream AnalyticsError (validation or execution failure) so the bridge can carry it through the same Err channel as its own errors.
BridgeError
The bridge's vocabulary for failure and async state.
BridgeFormatters
The single source of truth for turning typed analytics values into display strings and chart-ready numbers.
BridgeIncompatibleValues
The result's shape fits the widget, but its values cannot be plotted on a numeric axis. The specific cause is carried in reason — a non-numeric measure type, several series with incompatible units sharing one axis, or epoch spacing requested over non-temporal keys.
BridgeMissingScope
A required piece of configuration (palette, formatters, page range, …) was neither passed to the widget nor available from an enclosing AnalyticsScope. missingField names what was absent.
BridgePalette
An ordered color palette plus the bridge's coloring resolution order.
BridgeResult
The successful payload of a query run.
BridgeShapeMismatch
The result a mapper was handed does not match the shape the chosen widget can draw — e.g. a multi-series result handed to a single-series bar mapper.
CardChrome
Hand-drawn framing around a dashboard widget: a HandDrawnContainer with optional title and an optional edit affordance.
ExecuteQueryRequest
A plain, transport-friendly bundle of everything the executor needs.
HandDrawnAnalyticsBarChart
A bar chart backed by an analytics query and styled by a hand_drawn_toolkit template.
HandDrawnAnalyticsBigNumber
A prominent single-value display backed by an analytics scalar result.
HandDrawnAnalyticsCard
A spec-driven dashboard card: decodes an AnalyticsWidgetSpec's three JSON payloads, runs the query, and renders the result by its display type inside CardChrome.
HandDrawnAnalyticsLineChart
A line chart backed by an analytics query and styled by a hand_drawn_toolkit template.
HandDrawnAnalyticsScatterPlot
A scatter plot of two analytics series aligned by bucket key, styled by a hand_drawn_toolkit template.
HandDrawnAnalyticsStreakLeaderboard
A streak leaderboard backed by a StreakMeasure query, styled by a HandDrawnTable template.
HandDrawnAnalyticsTable
A table backed by an analytics query, styled by a hand_drawn_toolkit HandDrawnTable template.
HandDrawnAnalyticsWidget
Renders an already-computed BridgeResult according to a free-form displayType, without running a query itself.
HandDrawnTableView
Renders an already-mapped TableMapping into a HandDrawnTable template, optionally with resizable columns.
PairedResult
The result of a paired query: an independently-computed x and y series, aligned downstream by BucketKey equality.
ScatterMapping
The result of pairedToScatter: the chart data plus how many buckets were dropped during alignment, so a widget can optionally surface the data loss (mirroring the table mapper's truncated-row count).
SeriesView
A uniform read-only view over the two "named series" shapes the analytics layer produces:
SingleResult
The result of a single (non-paired) query.
StreakTableOptions
Options controlling how a streak TableResult is laid out as a leaderboard.
TableMapping
The bridge's stringified table projection of any analytics result.
UnknownDisplayType
Returned by HandDrawnVisualizationKind.resolve when a displayType string matches neither a canonical name nor a configured alias.
WidgetQueryRunner
The one place that knows the toolkit's execution choreography: list sources → validate → project the date range → fetch records → execute. Its seams (listSources, fetchRecords, executeQuery) are injectable so the same orchestration works in tests, in-process, or across an isolate.

Enums

BarMode
How a multi-dimension result is laid out as bars.
HandDrawnVisualizationKind
The closed set of visualizations the bridge knows how to render.
IncompatibleValuesReason
Why a result's values cannot be drawn on a numeric chart axis.
TemporalSpacing
How points are positioned along a line chart's X axis.

Functions

assembleLineChartData({required List<LineSeriesData> series, required List<BucketKey> keys, required List<double> positions, required List<List<double?>> numericGrid, required BridgeFormatters formatters, required TemporalSpacing temporalSpacing, required bool integerValued, required FieldType unitType, required List<LegendEntry> legend}) → LineChartData
Builds LineChartData with computed Y/X ranges, the zero-crossing axis default, categorical labels (under uniform spacing), and a unit-suffixed Y formatter.
bridgeShapeOf(BridgeResult result) → ResultShape
The ResultShape of a BridgeResult. Used when building a BridgeShapeMismatch so the error reports the shape that was actually produced rather than a fixed placeholder.
defaultAxisDisplayFor(NiceRange? range) → AxisDisplay
The default AxisDisplay for a value axis spanning range.
defaultShouldReload(AnalyticsChange change, Set<String> mySourceIds) bool
The default reload policy (matches the locked change semantics):
epochSpacingGuard(List<BucketKey> keys, TemporalSpacing spacing) BridgeError?
Guards epoch-millisecond spacing against non-temporal keys.
isChartableFieldType(FieldType type) bool
Whether type can be projected onto a numeric chart axis.
isIntegerMeasure(Measure measure, {FieldType? outputType}) bool
Whether measure inherently produces integer-valued output, so a chart axis should default to integer ticks.
mixedUnitGuard(List<SeriesView> views) BridgeError?
Guards against plotting series whose values carry incompatible units on a single shared axis.
multiMeasureToBar(MultiMeasureSeriesResult result, {required BarMode mode, required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, bool integerValued = false, double? fillAlpha}) → Result<BarChartData, BridgeError>
Maps a MultiMeasureSeriesResult to grouped or stacked BarChartData.
multiMeasureToLine(MultiMeasureSeriesResult result, {required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, TemporalSpacing temporalSpacing = TemporalSpacing.uniform, bool integerValued = false}) → Result<LineChartData, BridgeError>
Maps a MultiMeasureSeriesResult to a multi-line LineChartData — one line per measure. Rejects mixed-unit measures.
multiSeriesToBar(MultiSeriesResult result, {required BarMode mode, required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, bool integerValued = false, double? fillAlpha}) → Result<BarChartData, BridgeError>
Maps a MultiSeriesResult to grouped or stacked BarChartData.
multiSeriesToLine(MultiSeriesResult result, {required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, TemporalSpacing temporalSpacing = TemporalSpacing.uniform, bool integerValued = false}) → Result<LineChartData, BridgeError>
Maps a MultiSeriesResult to a multi-line LineChartData — one line per secondary-group series.
nicePaddedRange(Iterable<double?> values) NiceRange?
A padded range for scatter plots: unlike niceYRange this never floors at zero — it pads both ends so points don't sit on the axis. Returns null when there is nothing to plot.
niceUpperBound(double value, {bool integerValued = false}) double
Rounds value up to a visually tidy bound (1/2/2.5/5 × 10ⁿ). For integerValued data the result is at least the ceiling of value and is itself a whole number.
niceYRange(Iterable<double?> values, {bool integerValued = false}) NiceRange?
Computes a tidy Y range for bar/line charts from the plottable values.
outputTypeOf(AnalyticsResult result) → FieldType?
The measure output FieldType a chart axis should format against, or null when there is no single such type.
pairedToScatter(SeriesResult x, SeriesResult y, {required BridgeFormatters formatters, String? xAxisLabel, String? yAxisLabel}) → Result<ScatterMapping, BridgeError>
Aligns two series by BucketKey and maps the matched (x, y) pairs to a ScatterMapping.
projectMultiSeries(List<SeriesView> views, {required BridgeFormatters formatters}) List<List<double?>>
Projects the views into a dense series × position grid of plottable double?s, using formatters to map each typed value to a chart number.
resolveIntegerValued({required bool? explicit, required List<Measure> measures, FieldType? outputType}) bool
Resolves the effective integer-valued axis flag for a chart widget.
resultShapeOf(AnalyticsResult result) → ResultShape
The ResultShape corresponding to a concrete AnalyticsResult. Used when building a BridgeShapeMismatch so the error reports the actual shape.
resultToTable(AnalyticsResult result, {required BridgeFormatters formatters}) → Result<TableMapping, BridgeError>
Universal intake: maps any AnalyticsResult to a TableMapping.
scalarToBigNumber(AnalyticsResult result, {required BridgeFormatters formatters}) → Result<({Color? color, String displayValue, String? label}), BridgeError>
The big-number projection of a scalar result: a display string, the measure label, and an optional color (left null — the widget decides).
seriesToBar(SeriesResult result, {required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, bool integerValued = false, double? fillAlpha}) → Result<BarChartData, BridgeError>
Maps a single SeriesResult to BarChartData: one bar per bucket.
seriesToLine(SeriesResult result, {required BridgePalette palette, required BridgeFormatters formatters, SemanticColorResolver? resolver, TemporalSpacing temporalSpacing = TemporalSpacing.uniform, bool integerValued = false}) → Result<LineChartData, BridgeError>
Maps a single SeriesResult to a one-line LineChartData.
streakToTable(TableResult table, {required BridgeFormatters formatters, StreakTableOptions options = const StreakTableOptions()}) TableMapping?
Maps a StreakMeasure TableResult to a readable leaderboard TableMapping (raw entity id hidden by default, wide name column, narrow streak counts).
unchartableValueError(FieldType type) BridgeError
The error returned when a measure's output type cannot be plotted on a numeric axis. Suggests a table, which can render any value.
unitAxisFormatter(BridgeFormatters formatters, FieldType fieldType) → AxisValueFormatter?
A Y-axis tick formatter that appends the measure's unit (only durations carry one), or null when no unit annotation applies.

Typedefs

AnalyticsCardReloadPredicate = bool Function(AnalyticsChange change, Set<String> mySourceIds)
A predicate deciding whether a widget should reload in response to an AnalyticsChange. mySourceIds is the set of source ids the widget reads.
ExecuteQueryFn = FutureOr<Result<AnalyticsResult, AnalyticsError>> Function(ExecuteQueryRequest request)
Executes a fully-prepared request and returns the typed result, synchronously or asynchronously. The default runs AnalyticsExecutor.execute in-process; an async function lets a host move execution off the main isolate (e.g. by handing the request to compute with a top-level function that calls AnalyticsExecutor.execute).
FetchRecordsFn = Future<List<SourceRecord>> Function(String sourceId, {(DateTime, DateTime)? dateBound})
Fetches the normalized records for sourceId, optionally bounded to a resolved [start, end) date range. The host typically composes SourceSnapshotCache.getOrFetch into this seam; the runner does not own a cache itself.
ListSourcesFn = List<SourceDef> Function()
Supplies the source catalog to validate and project against.
NiceRange = ({double max, double min})
A computed [min, max] axis range.
SemanticColorResolver = Color? Function(String? semanticTag, BucketKey? key)
A consumer-supplied hook for assigning a color to a series, bucket, or segment from its semantic identity rather than its position.