Pantrix UI

Line Chart

A line chart of rows that are given once: a line for each of the values read from them, along the places x reads from them.

import com.pantrixui.components.chart.line.PantrixLineChart

Examples

Lines from the same rows

At its plainest: the rows, the x that gives a row's place, and a ChartValue for each series. A series takes its key and its name; the block at the end reads its value from a row.

Three series take the theme's first three chart colours. Press a name in the legend: its series goes, and the ones that stay keep their colours.

PantrixLineChart(
    data = TRAFFIC,
    x = { it.day },
    values = listOf(
        ChartValue("search", "Search") { it.search },
        ChartValue("direct", "Direct") { it.direct },
        ChartValue("social", "Social") { it.social }
    ),
    description = "Three sources of visits by day of the week",
    tagKey = "plain"
)

private data class Traffic(
    val day: String,
    val search: Int,
    val direct: Int,
    val social: Int
)

private val TRAFFIC = listOf(
    Traffic("Mon", search = 420, direct = 240, social = 130),
    Traffic("Tue", search = 480, direct = 260, social = 120),
    Traffic("Wed", search = 455, direct = 300, social = 165),
    Traffic("Thu", search = 530, direct = 280, social = 190),
    Traffic("Fri", search = 510, direct = 330, social = 240),
    Traffic("Sat", search = 360, direct = 210, social = 310),
    Traffic("Sun", search = 330, direct = 180, social = 285)
)

Numeric axis

The height of a hiking trail, measured at uneven points along the way: the places are numbers, in kilometres. With xAxis = ChartAxis(numeric = true) the points stand as far apart as their numbers: 0.8 km and 1.2 km close together, 7.5 km and 11 km far apart; the axis is marked at round kilometres, written as format says. The summit's line does not stand at a measurement but at 9.6 km.

Turn the switch off: the numbers become names, set out at even steps, and the trail's steepness reads wrong.

var numeric by remember { mutableStateOf(true) }
val strings = PantrixTheme.strings
PantrixSwitchButton(
    checked = numeric,
    onCheckedChange = { numeric = it },
    label = "Numeric axis",
    tagKey = "line_numeric"
)
PantrixLineChart(
    data = TRAIL,
    x = { it.first },
    values = listOf(ChartValue("height", "Height (m)") { it.second }),
    curve = ChartCurve.Smooth,
    xAxis = ChartAxis(numeric = numeric, format = { "${strings.decimal(it)} km" }),
    marks = listOf(ChartReferenceLine(x = 9.6, label = "Summit")),
    description = "The height of a hiking trail, measured along the way",
    tagKey = "numeric"
)

private val TRAIL = listOf(
    0.0 to 420,
    0.8 to 455,
    1.2 to 470,
    2.5 to 560,
    4.0 to 610,
    7.5 to 820,
    9.6 to 1_040,
    11.0 to 930,
    12.4 to 780
)

How the line goes

curve says how the line goes from one point to the next. Linear goes straight. Smooth goes round through the points but stays between them: it makes up no peak or dip. The stepped ones carry a value level from one point to the next: StepAfter holds until the value changes, like a stock or a price; StepBefore goes to the next value ahead of it; Step steps halfway between the two.

var curve by remember { mutableStateOf(ChartCurve.Smooth) }
PantrixSegmentButton(
    options = CURVES,
    selected = curve,
    onSelect = { curve = it },
    fullWidth = false,
    tagKey = "curve"
)
PantrixLineChart(
    data = TRAFFIC,
    x = { it.day },
    values = listOf(ChartValue("search", "Search", marker = ChartMarker.Circle) { it.search }),
    curve = curve,
    description = "Visits from search by day of the week",
    tagKey = "curves"
)

private data class Traffic(
    val day: String,
    val search: Int,
    val direct: Int,
    val social: Int
)

private val TRAFFIC = listOf(
    Traffic("Mon", search = 420, direct = 240, social = 130),
    Traffic("Tue", search = 480, direct = 260, social = 120),
    Traffic("Wed", search = 455, direct = 300, social = 165),
    Traffic("Thu", search = 530, direct = 280, social = 190),
    Traffic("Fri", search = 510, direct = 330, social = 240),
    Traffic("Sat", search = 360, direct = 210, social = 310),
    Traffic("Sun", search = 330, direct = 180, social = 285)
)

private val CURVES = listOf(
    SegmentOption(value = ChartCurve.Linear, label = "Straight", tagKey = "linear"),
    SegmentOption(value = ChartCurve.Smooth, label = "Smooth", tagKey = "smooth"),
    SegmentOption(value = ChartCurve.Step, label = "Step", tagKey = "step"),
    SegmentOption(value = ChartCurve.StepBefore, label = "Before", tagKey = "before"),
    SegmentOption(value = ChartCurve.StepAfter, label = "After", tagKey = "after")
)

Series told apart by more than colour

Colour alone is not enough for every reader: for an eye that does not tell red from green, for a black and white printout, for a chart of many series. Here each series is told apart by more than its colour too: one is a solid line with circles, one a dashed line with squares, one a dotted line with triangles.

The same sample stands in the legend and in the tooltip: a series is known there too by its line and its shape.

PantrixLineChart(
    data = TRAFFIC,
    x = { it.day },
    values = listOf(
        ChartValue("search", "Search", marker = ChartMarker.Circle) { it.search },
        ChartValue("direct", "Direct", dash = ChartDash.Dashed, marker = ChartMarker.Square) { it.direct },
        ChartValue("social", "Social", dash = ChartDash.Dotted, marker = ChartMarker.Triangle) { it.social }
    ),
    description = "Three sources of visits by day of the week",
    tagKey = "told_apart"
)

private data class Traffic(
    val day: String,
    val search: Int,
    val direct: Int,
    val social: Int
)

private val TRAFFIC = listOf(
    Traffic("Mon", search = 420, direct = 240, social = 130),
    Traffic("Tue", search = 480, direct = 260, social = 120),
    Traffic("Wed", search = 455, direct = 300, social = 165),
    Traffic("Thu", search = 530, direct = 280, social = 190),
    Traffic("Fri", search = 510, direct = 330, social = 240),
    Traffic("Sat", search = 360, direct = 210, social = 310),
    Traffic("Sun", search = 330, direct = 180, social = 285)
)

A small line with no axes

A small line in the corner of a card: it has no axes and no legend (null), and is sixty dp tall. Its values are still in place: the value of the day pointed at with a pointer or a finger is read in the tooltip, and a screen reader says the days one by one.

A chart with no axis tells its tooltip how its values are written (ChartTooltip.format): here what the number counts is added after the number.

val strings = PantrixTheme.strings
PantrixLineChart(
    data = TRAFFIC,
    x = { it.day },
    values = listOf(ChartValue("search", "Search") { it.search }),
    modifier = Modifier.height(60.dp),
    curve = ChartCurve.Smooth,
    xAxis = null,
    yAxis = null,
    tooltip = ChartTooltip(format = { "${strings.decimal(it)} visits" }),
    description = "Visits from search by day of the week",
    tagKey = "bare"
)

private data class Traffic(
    val day: String,
    val search: Int,
    val direct: Int,
    val social: Int
)

private val TRAFFIC = listOf(
    Traffic("Mon", search = 420, direct = 240, social = 130),
    Traffic("Tue", search = 480, direct = 260, social = 120),
    Traffic("Wed", search = 455, direct = 300, social = 165),
    Traffic("Thu", search = 530, direct = 280, social = 190),
    Traffic("Fri", search = 510, direct = 330, social = 240),
    Traffic("Sat", search = 360, direct = 210, social = 310),
    Traffic("Sun", search = 330, direct = 180, social = 285)
)

API

data

A line chart of rows that are given once: a line for each of the values read from them, along the places x reads from them.

ParameterTypeDefaultWhat it is
dataList<T>the rows, in the order the lines go through them.
x(T) -> Anywhere a row stands along the chart: a name, a day, anything that is equal to itself.
valuesList<ChartValue<T>>the series: what each reads from a row, and how it is called and drawn.
modifierModifierModifierthe chart's modifier. A height here is the chart's, the legend's with it.
curveChartCurveChartCurve.Linearhow each line goes from a point to the next.
missingChartMissingChartMissing.Gapwhat a line does at a row that has no value.
labelsChartDataLabels?nullthe values written over the points of every line, where they have room. null writes none.
xAxisChartAxis?ChartAxis()the axis of the places, under the chart. null draws nothing of it.
yAxisChartAxis?ChartAxis()the axis of the values, beside the chart. null draws nothing of it.
secondYAxisChartAxis?ChartAxis()the second axis of the values, at the chart's other side: drawn where a value is read by it (ChartValue.axis), and not otherwise. null draws nothing of it.
marksList<ChartMark>emptyList()the lines and the bands drawn in the plot besides the series: a target, a limit, a stretch of places.
legendChartLegend?ChartLegend()where the names of the series stand. null shows none.
tooltipChartTooltip?ChartTooltip()what is shown for the place that is pointed at. null shows none.
zoomChartZoom?nullwhat lets the reader look closer at a part of the places and move along the rest. null, which it is unless the chart is given one, shows all the places and no more.
decimationChartDecimation?ChartDecimation()how a line with more points in view than the plot has room for is drawn through fewer of them. null draws every point, as many as there are.
exportChartExport?nullwhat lets the reader take the chart out of the app as a file: a picture, or a drawing that is sharp at any size. null, which it is unless the chart is given one, and the chart has no such button.
empty(@Composable () -> Unit)?nullwhat the chart shows in its plot while it has no value to draw, in the place of the theme's words for it ("No data"), which a screen reader is then not told either. null writes the words. A file the chart is exported as says them.
stateChartStaterememberChartState()what the reader has hidden and what they point at: the chart's own where none is given.
descriptionString?nullwhat the chart shows, in a few words, for a screen reader.
onSelect((ChartPoint) -> Unit)?nullcalled with the value that is pressed.
styleChartStyle?nullthis chart's own geometry.
colorsChartColors?nullthis chart's own colours, where they are not the theme's.
tagKeyString?nullthe chart's automation key. The chart is tagged as the PantrixChart it is, Chart_<tagKey>, and its parts and events are that chart's.

ChartStyle

A chart's geometry: how tall it is where nothing says, how thick its lines and how large its marks are, and the room between its parts; and whether its fills have textures besides their colours.

ParameterTypeDefaultWhat it is
heightDpDp.UnspecifiedHow tall a chart's plot is, with its axes, where the chart's modifier gives it no height. A legend above or under the plot adds its own.
lineWidthDpDp.UnspecifiedThe width of a series' line.
markerSizeDpDp.UnspecifiedThe width and height of the mark on a point.
selectedMarkerSizeDpDp.UnspecifiedThe same of the mark on the point that is pointed at.
gridWidthDpDp.UnspecifiedThe width of the grid's lines, of the lines along the axes and of the line that marks the place pointed at. Zero draws none.
labelGapDpDp.UnspecifiedBetween the plot and what is written beside it and under it, and between that and an axis' title.
tickSpacingDpDp.UnspecifiedThe least room between two values marked along the axis of values: what decides how many are marked where the axis does not say.
legendGapDpDp.UnspecifiedBetween the legend and the chart it stands beside.
legendItemGapDpDp.UnspecifiedBetween one entry of the legend and the next.
swatchSizeDpDp.UnspecifiedHow wide the sample of a series is in the legend and in the tooltip: a short run of its line, or a patch of its fill.
swatchGapDpDp.UnspecifiedBetween that sample and the name after it.
tooltipShapeShapeSpecShapeSpec.UnspecifiedThe tooltip's corners: a step of the brand's shape scale, or a fixed shape.
tooltipHorizontalPaddingDpDp.UnspecifiedBetween the tooltip's sides and what it holds.
tooltipVerticalPaddingDpDp.UnspecifiedBetween its top and bottom and what it holds, and between its heading and rows.
tooltipGapDpDp.UnspecifiedBetween the place pointed at and the tooltip beside it.
tooltipElevationDpDp.UnspecifiedThe tooltip's shadow.
barGapDpDp.UnspecifiedBetween two bars that stand side by side at a place. Bars too thin for it stand closer.
barMaxWidthDpDp.UnspecifiedThe widest a bar is, however much room its place has: a chart of three places is not three slabs.
barRadiusDpDp.UnspecifiedHow round the corners of a bar's end are, the end away from nought.
sliceGapDpDp.UnspecifiedBetween two slices of a pie that lie side by side, the same from the pie's middle to its rim.
selectedSliceOffsetDpDp.UnspecifiedHow far the slice of a pie that is pointed at stands out past the pie's rim. The pie keeps this much room about itself, so that the slice has where to stand.
gaugeWidthDpDp.UnspecifiedHow thick the arc of a gauge is. An arc is no thicker than half its radius.
meterHeightDpDp.UnspecifiedHow tall the bar of a meter group is.
trackerHeightDpDp.UnspecifiedHow tall the blocks of a status tracker are.
bubbleMinSizeDpDp.UnspecifiedHow far across the smallest bubble of a scatter chart is: a point whose size is nothing, or too little to be seen as an area, is still a point.
bubbleMaxSizeDpDp.UnspecifiedHow far across its largest bubble is: the one whose size is the most of the chart's.
navigatorHeightDpDp.UnspecifiedHow tall the strip under a chart's plot is, on which the part of the chart that is in view is marked and moved, in a chart whose zoom has one: how wide, beside a plot whose places go down its side.
barBandShareFloatFloat.NaNHow much of a place's band its bars take, more than nought and one at the most: the rest is the room between one place's bars and the next's. Bars are no wider than barMaxWidth all the same.
markLineWidthDpDp.UnspecifiedThe width of a mark's line: a target, a limit, a day. Zero draws none, and the mark's words are written all the same.
markBandAlphaFloatFloat.NaNHow much of its colour a mark's band is drawn in, from nought to one: a band lies behind the series, and is a tint.
patternsBoolean?nullWhether the fills of a chart's series, slices and tiles are drawn with textures besides their colours, and its lines broken in turn (ChartPattern): what tells them apart for a reader who cannot tell the colours apart. Left out, as the system is set: on where it is set to tell things apart without colour, iOS's "Differentiate Without Color" and Android's colour correction, and off otherwise.
nodeWidthDpDp.UnspecifiedHow wide a node of a sankey is.
nodeGapDpDp.UnspecifiedBetween two nodes of a column of a sankey.

ChartColors

What a chart is painted with, for a chart that does not take the theme's colours: a colour that is set is used, and one that is left out stays the theme's.

ParameterTypeDefaultWhat it is
seriesList<Color>emptyList()The colours the series are drawn in, in the order they are handed out, for the series that name none of their own. Empty, they are the theme's chart colours.
gridColorColor.UnspecifiedThe lines drawn across the chart at the marks of an axis. The theme's divider.
axisColorColor.UnspecifiedThe line along an axis. The theme's border.
labelColorColor.UnspecifiedWhat is written beside the axes: the values, the places and the axes' titles, and the names in the legend. The theme's textSecondary.
crosshairColorColor.UnspecifiedThe line that marks the place pointed at. The theme's borderHover: stronger than the grid it crosses, and quieter than the series it stands behind.
tooltipContainerColorColor.UnspecifiedThe tooltip's fill. The theme's surface.
tooltipBorderColorColor.UnspecifiedThe tooltip's edge. The theme's border.
tooltipTextColorColor.UnspecifiedThe tooltip's heading and its values. The theme's textPrimary.
tooltipLabelColorColor.UnspecifiedThe names of the series in the tooltip. The theme's textSecondary.
risingColorColor.UnspecifiedWhat a value that goes up is drawn in: a step of a waterfall that adds. The theme's success.
fallingColorColor.UnspecifiedWhat a value that goes down is drawn in: a step of a waterfall that takes away. The theme's danger.