Skip to content
HDCharts

Naming and File Structure

Every chart is one public composable plus the internals behind it. These rules name both, so a reader can tell what a file holds from its name alone. Follow them when adding a chart, adding a public composable to a chart module, or moving code between layers.

The rules exist because the naming drifted. Five charts once declared an internal composable with the same name as their public one, and two of those needed an import alias or a shadowing import to call it. Neither was visible at the call site.

Public Composables

One public composable per file. The file is named after it and holds nothing else public.

FileComposableModule
LineChart.ktLineChartcharts-line
LiveLineChart.ktLiveLineChartcharts-line
BarChart.ktBarChartcharts-bar
HistogramChart.ktHistogramChartcharts-histogram
PieChart.ktPieChartcharts-pie
RadarChart.ktRadarChartcharts-radar
StackedBarChart.ktStackedBarChartcharts-stacked-bar
StackedAreaChart.ktStackedAreaChartcharts-stacked-area

A second public composable in the same module gets its own file. charts-line has two, because live line and the regular line chart are different charts that share internals.

A style file is named after the chart it styles: LineChartStyle.kt holds LineChartStyle and its blocks. See Style Defaults for how the styles are built.

Internal Composables

An internal composable never repeats the name of the public composable it implements. The public name belongs to the public composable.

The suffix says what the composable does. There are four roles, and a composable that needs two of them should be split.

RoleSuffixWhat it holdsExample
CanvasContentA canvas and the drawing calls on itBarChartContent
State and interactionImplDensity mode, zoom, animation values, the header; delegates drawingBarChartImpl
Layout shellFrameHeader, plot, and legend slots only, with no chart stateLineChartFrame
Input preparationEntryValidates, clamps, and converts before handing over to the contentLineChartEntry

A suffix outside this table is the signal that a composable is doing two jobs.

A boundary that a second chart calls is named as a distinct noun rather than with a suffix. BarChartInternalPlot is shared by bar and histogram and is neither bar's entry nor bar's canvas.

The chains

Every chain ends at a Content, which owns the canvas.

ChartChain
LineLineChart → LineChartEntry → LineChartImpl → LineChartContent → LineChartFrame
Live lineLiveLineChart → LineChartEntry → LiveLineChartImpl → LineChartContent
BarBarChart → BarChartInternalPlot → BarChartImpl → BarChartContent
HistogramHistogramChart → BarChartInternalPlot → BarChartImpl → BarChartContent
PiePieChart → PieChartFrame → PieChartContent
RadarRadarChart → RadarChartContent
Stacked barStackedBarChart → StackedBarChartImpl → StackedBarChartContent
Stacked areaStackedAreaChart → StackedAreaChartImpl → StackedAreaChartContent

Radar and pie lay their plot out with ChartSquarePlotLayout from charts-core, which supplies the square plot, the header, and the legend.

Two Public Composables in One Module

Line is the only module with two, and the split is at the input boundary, not the draw boundary. LineChartImpl and LiveLineChartImpl are separate, because live line has no selection and a window that shifts. They share LineChartContent, because both draw the same way.

When a module gets a second public composable, copy that split: each gets its own input handling, and they share the content. State which stage the split falls at in the file's KDoc.

Files

FileHolds
<Name>Chart.ktThe public composable, and the private helpers only it uses
<Name>ChartStyle.ktThe chart's style and its blocks
<Name>ChartPreviews.ktPreviews for one public composable
internal/<Name>ChartEntry.ktInput preparation for one public composable
internal/<Name>ChartImpl.ktState and interaction for one public composable
internal/<Name>ChartContent.ktThe canvas and its drawing
internal/<Name>ChartFrame.ktA layout shell, when more than one composable calls it
internal/<Name>ChartHelpers.ktPure functions for one chart
internal/<Name>ChartDrawing.ktDrawing helpers kept apart from the canvas composable

Previews follow the public composable they preview, so a second public composable gets its own Previews file. LineChartPreviews.kt currently previews both line composables and should be split.

A file with one narrow concern names that concern: StackedBarDensity.kt, StackedBarInteraction.kt, StackedAreaDensity.kt. A file of pure functions for a chart uses <Name>ChartHelpers.kt, which line, bar, pie, and radar all do.

A Frame gets its own file when more than one composable calls it, which is why LineChartFrame.kt exists. A frame with one caller stays in the file that owns it, which is why PieChartFrame sits inside PieChart.kt.

Test files

Test source sets hold two kinds of file with no @Test, named differently.

  • Shared fixtures take the chart family prefix: LineTestFixtures.kt in charts-line, StackedBarTestFixtures.kt in charts-stacked-bar. One file per module, holding the data, colours, and builders that module's tests share. Never name one *Test.kt — nothing treats that suffix as a suite unless the file holds tests.
  • A test helper is named for what it does, like any other file: PixelCapture.kt holds setCapturedContent.

Module Structure

charts-core holds everything a chart shares, and a chart module holds only what is its own. The shared code is organised by concern, in internal/ sub-packages: axis, bezier, composable, density, drawing, interaction, layout, model, palette, and theme, plus the flat files DataValidation.kt, StyleClamping.kt, Constants.kt, and AnimationSpec.kt.

A chart module's own internal/ package stays flat. Sub-package it by stage only once it holds more than about ten files, and match the core naming when you do.

Everything that is not public API should carry @InternalChartsApi, a @RequiresOptIn at error level declared in InternalChartsApi.kt. charts-core applies it across its own internal/ packages, and a chart module applies it to anything another chart module calls.

Five composables in internal/composable/ do not carry it yet, so they are unrestricted public API of the published charts-core artifact: ChartErrors, Legend, LegendItems, rememberShowState, and rememberAnimationState. Their siblings in the same directory are annotated. rememberAnimationState has no production caller at all and should be deleted rather than annotated.

Keep one boundary composable per shared plot, and have every chart that needs that plot call the boundary rather than the other chart's internals. Histogram calls BarChartInternalPlot for this reason, and never reaches into charts-bar any further. A boundary is named as a distinct noun, not with a role suffix, because it is none of the four roles on its own.

Adding a Chart or a Public Composable

  • Name the public composable after the file, and give it its own file.
  • Give each internal composable the suffix for its role, and give it a file to match.
  • Never repeat the public name on an internal composable, and never alias or shadow an import to work around a repeat.
  • Give the content composable the canvas, and keep state out of it.
  • If a second chart will use part of this, put that part behind an @InternalChartsApi boundary with a distinct name.
  • Follow the style rules in Style Defaults and the clamping rules in Style Clamping for anything the user can set.

Tests

BehaviorTests
An internal composable never repeats a public namegrep -rn "^internal fun [A-Z][A-Za-z]*Chart(" charts-*/src/commonMain returns nothing
Every chart draws through its content composableThe screenshot suite, one directory per test file under sample/androidApp
Style blocks clampStyleClampingTest in charts-core/src/commonTest, plus a *_withInvalidNumericStyleValues_drawsClampedChart test per chart
Non-public internals are opted ingrep -rn "@InternalChartsApi" charts-*/src/commonMain

Naming rules are checked by reading, not by a test. The grep in the first row is the closest thing to one, and it is worth running before opening a pull request.

Known Issues

Known issues and limits are listed in the Known Issues section.