3.0.0 Migration
Upgrade note: If upgrading from 2.4.0 or earlier, read these migration notes.
Bar and Histogram v3 migration
Use ChartData with one Double series for both BarChart and
HistogramChart. Convert older dataset values before creating the chart data.
Before
BarChart(
dataSet = listOf(18f, 32f, 26f).toChartDataSet(
title = "Daily sales",
labels = listOf("Mon", "Tue", "Wed"),
),
selectedBarIndex = 1,
)After
val selection = rememberChartSelection(initialIndex = 1)
BarChart(
data = listOf(18.0, 32.0, 26.0).toChartData(
categories = listOf("Mon", "Tue", "Wed"),
seriesName = "Daily sales",
),
title = "Daily sales",
selection = selection,
)The equivalent HistogramChart migration is:
// Before
HistogramChart(
dataSet = listOf(3f, 6f, 11f, 16f, 14f, 9f, 5f).toChartDataSet(
title = "Request Duration Distribution",
labels = listOf("0-50ms", "50-100ms", "100-150ms", "150-200ms", "200-250ms", "250-300ms", "300ms+"),
),
)
// After
HistogramChart(
data = listOf(3.0, 6.0, 11.0, 16.0, 14.0, 9.0, 5.0).toChartData(
categories = listOf("0-50ms", "50-100ms", "100-150ms", "150-200ms", "200-250ms", "250-300ms", "300ms+"),
seriesName = "Request Duration Distribution",
),
title = "Request Duration Distribution",
)Both charts accept one series. Histogram values must be nonnegative. The old
dataSet overloads and selectedBarIndex parameter are removed; use the
top-level selection parameter instead.
Styles
Styles are grouped. Migrate customizations to the corresponding blocks on
BarChartStyle or HistogramChartStyle, such as bars, range, grid,
axis, and selectionLine. Create them with BarChartDefaults or
HistogramChartDefaults rather than the removed flat style parameters.
Behavior
titleis separate fromseriesNameand category labels.- An empty category list hides X-axis labels; supplied categories must match the value count.
valueFormatterformats selected values andaxisValueFormatterformats Y-axis ticks independently.- Selection identifies a source bar or bin. Replacing the data clears it; resizing and density changes preserve it.
- Set
interactionEnabled = falseto disable user controls while retaining programmatic selection.
Kotlin/JS to Kotlin/Wasm migration
Use Kotlin/Wasm (wasmJs) for web applications that consume HDCharts.
Before
js {
browser()
}After
Replace the Kotlin/JS web target with wasmJs:
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}JVM, Android, and iOS targets are unchanged.
Line v3 migration
Use one LineChart for both single- and multi-series line charts. The chart
accepts shared ChartData with aligned Double values.
Before
@Composable
private fun ShowMultiLine() {
val items = listOf(
"Web Store" to listOf(420f, 510f, 480f, 530f, 560f, 590f),
"Mobile App" to listOf(360f, 420f, 410f, 460f, 500f, 540f),
"Partner Sales" to listOf(280f, 320f, 340f, 360f, 390f, 420f),
)
val dataSet = items.toMultiChartDataSet(
title = "Weekly Revenue by Channel",
prefix = "$",
categories = listOf("Week 1", "Week 2", "Week 3", "Week 4", "Week 5", "Week 6"),
)
LineChart(
dataSet = dataSet,
selectedPointIndex = 1,
)
}After
val selection = rememberChartSelection(initialIndex = 1)
LineChart(
data = listOf(
"Web Store" to listOf(420.0, 510.0, 480.0, 530.0, 560.0, 590.0),
"Mobile App" to listOf(360.0, 420.0, 410.0, 460.0, 500.0, 540.0),
"Partner Sales" to listOf(280.0, 320.0, 340.0, 360.0, 390.0, 420.0),
).toChartData(
categories = listOf("Week 1", "Week 2", "Week 3", "Week 4", "Week 5", "Week 6"),
),
title = "Weekly Revenue by Channel",
selection = selection,
valueFormatter = ChartValueFormatters.prefix("$"),
)The old ChartDataSet and MultiChartDataSet entry points are removed. There
is no separate multi-line composable. Use chartDataOf when explicit
ChartSeries construction is more convenient.
Styles and selection
Move customizations to the grouped LineChartStyle sections such as line,
points, selection, and axis. Use selection instead of
selectedPointIndex; it identifies one source X index shared by all series.
Use staticChartSelection(index) for a preset preview or screenshot.
Categories are explicit labels. An empty list hides X-axis labels, and supplied
categories must match every series. valueFormatter and axisValueFormatter
are independent and both receive Double values.
Maven namespace migration
HDCharts has moved to the organization-owned Maven group
io.github.hdcharts. The personal identity io.github.dautovicharis no
longer receives new artifacts.
Coordinates
| Before (2.x) | After (3.0.0) | |
|---|---|---|
| Umbrella | io.github.dautovicharis:charts | io.github.hdcharts:charts |
| Modules | io.github.dautovicharis:charts-line | io.github.hdcharts:line |
| BOM | io.github.dautovicharis:charts-bom | io.github.hdcharts:bom |
The Kotlin package also moves with the group:
io.github.dautovicharis.charts.*->io.github.hdcharts.charts.*
Update both the dependency coordinates and the import statements in your
project.
Before
import io.github.dautovicharis.charts.LineChart
import io.github.dautovicharis.charts.model.toChartData
commonMain.dependencies {
implementation("io.github.dautovicharis:charts-line:2.4.0")
}After
import io.github.hdcharts.charts.LineChart
import io.github.hdcharts.charts.model.toChartData
commonMain.dependencies {
implementation("io.github.hdcharts:line:3.0.0")
}Upgrade path from 2.x
Consumers staying on io.github.dautovicharis:charts-line:2.x do not need to
change anything until they upgrade to 3.0.0. When a consumer that pins
io.github.dautovicharis:charts-line:3.0.0 resolves the new version, Maven
Central follows the relocation published under the old group and downloads the
artifact from io.github.hdcharts:line:3.0.0 automatically. No manual
intervention is required during the upgrade.
The relocation applies once at the 3.0.0 release. From 3.0.1 onward, the
project only publishes under io.github.hdcharts. Pin the new coordinates
directly when starting a new project.
Pie chart v3 migration
Use finite, nonnegative Double values when constructing PieSlice instances.
Before
PieChart(
dataSet = listOf(80f, 20f).toChartDataSet(
title = "Progress",
labels = listOf("Completed", "Remaining"),
),
selectedSliceIndex = 0,
)After
val selection = rememberChartSelection(initialIndex = 0)
PieChart(
data = listOf(
PieSlice(label = "Completed", value = 80.0),
PieSlice(label = "Remaining", value = 20.0),
),
modifier = Modifier.fillMaxWidth(),
title = "Progress",
style = PieChartDefaults.style(
donut = PieChartDefaults.donut(holePercentage = 50f),
),
selection = selection,
)Convert source values at the application boundary:
val slices = sourceSlices.map { slice ->
PieSlice(label = slice.label, value = slice.value.toDouble())
}The v3 API replaces the old ChartDataSet input with a list of PieSlice
values. PieSlice.value uses Double; invalid or negative values are rejected
by the chart.
Selection and interaction
Pass a ChartSelection directly to PieChart when selection must be
controlled by the application:
val selection = rememberChartSelection()
PieChart(data = slices, selection = selection)Programmatic selection remains visible when interactionEnabled is false; in
that mode, user taps and automatic deselection are disabled.
Radar v3 migration
Use shared ChartData with one ChartSeries per radar polygon. Categories are
the shared axis labels.
Before
@Composable
private fun ShowRadar() {
val categories = listOf(
"Performance",
"Reliability",
"Usability",
"Security",
"Scalability",
"Observability",
)
val dataSet = listOf(84f, 79f, 76f, 88f, 82f, 74f).toChartDataSet(
title = "Platform Readiness Score",
labels = categories,
)
RadarChart(
dataSet = dataSet,
selectedAxisIndex = 1,
)
}After
RadarChart(
data = chartDataOf(
categories = listOf("Performance", "Reliability", "Usability", "Security", "Scalability", "Observability"),
ChartSeries("Platform Readiness Score", listOf(84.0, 79.0, 76.0, 88.0, 82.0, 74.0)),
),
modifier = Modifier.fillMaxWidth(),
title = "Platform Readiness Score",
selection = rememberChartSelection(initialIndex = 1),
)The old ChartDataSet, MultiChartDataSet, and selectedAxisIndex inputs are
removed. Use the title and selection parameters instead. Use
staticChartSelection(index) for a preset preview or screenshot.
Styles
Move customizations to the grouped RadarChartStyle sections such as grid,
axes, polygon, points, and categories.
Behavior
- Use at least one series and three aligned axes. Values must be finite.
- Categories are optional; when supplied, they must match every series.
- When categories are provided, selecting an axis uses its category as the selected title and exposes the raw value for each series.
interactionEnabled = falsedisables drag gestures while programmatic selection remains visible.
Shared v3 chart contracts
Use ChartData with Double values for the charts migrated to the v3 API.
Before
val data = listOf(24f, 18f).toChartData()After
val sales = listOf(24.0, 18.0).toChartData(
categories = listOf("Mon", "Tue"),
seriesName = "Sales",
)
val comparison = listOf(
"This year" to listOf(24.0, 18.0),
"Last year" to listOf(20.0, 16.0),
).toChartData(categories = listOf("Mon", "Tue"))Convert Int, Float, or string values in your application before creating
ChartData or ChartSeries:
val data = sourceValues.map { it.toDouble() }.toChartData()Handle invalid string values in the application; the library does not choose a parsing or missing-value policy for you.
Categories
Categories are explicit labels for the shared data index. An empty list means that the chart has no category labels. When provided, the category count must match every series. Index labels are not generated automatically.
Selection
Hoist selection with rememberChartSelection() when the application needs to
observe or control it:
val selection = rememberChartSelection(
onSelectionChanged = { index -> onPointSelected(index) },
)Callbacks run only when the selected index changes. Use
staticChartSelection(index) for an initial selection in a preview or
screenshot.
Formatting
ChartValueFormatter receives a Double. Value and axis formatters are
independent, so a custom selected-value format does not change axis labels.
Stacked Area v3 migration
Use shared ChartData with one ChartSeries per contribution series. Categories
identify the shared X positions.
Before
@Composable
private fun ShowStackedArea() {
val items = listOf(
"Free Plan" to listOf(620f, 650f, 690f, 720f, 760f, 800f),
"Standard Plan" to listOf(240f, 260f, 285f, 310f, 340f, 365f),
"Premium Plan" to listOf(90f, 95f, 105f, 118f, 130f, 142f),
)
val dataSet = items.toMultiChartDataSet(
title = "Monthly Active Subscribers by Plan",
categories = listOf("Jan", "Feb", "Mar", "Apr", "May", "Jun"),
)
StackedAreaChart(
dataSet = dataSet,
selectedPointIndex = 1,
)
}After
StackedAreaChart(
data = chartDataOf(
categories = listOf("Jan", "Feb", "Mar", "Apr", "May", "Jun"),
ChartSeries("Free Plan", listOf(620.0, 650.0, 690.0, 720.0, 760.0, 800.0)),
ChartSeries("Standard Plan", listOf(240.0, 260.0, 285.0, 310.0, 340.0, 365.0)),
ChartSeries("Premium Plan", listOf(90.0, 95.0, 105.0, 118.0, 130.0, 142.0)),
),
modifier = Modifier.fillMaxWidth(),
title = "Monthly Active Subscribers by Plan",
selection = rememberChartSelection(initialIndex = 1),
)The old MultiChartDataSet and selectedPointIndex inputs are removed. Use
selection instead; it identifies one source X index shared by all series.
Styles and selection
Move customizations to the grouped StackedAreaChartStyle sections such as
fill, boundary, axis, and selection. Series colors correspond to
contribution series.
Use staticChartSelection(index) for a preset preview or screenshot. Replacing
data clears selection; resizing and density changes preserve it.
Behavior
- Series must be aligned, contain at least two X positions, and use finite, nonnegative values.
- Categories are optional; when supplied, they must match every series.
- Stacking shows absolute totals rather than percentages.
interactionEnabled = falsedisables user controls while programmatic selection remains visible.
Stacked Bar v3 migration
Use shared ChartData with one ChartSeries per stack segment. Each category
is one bar, and each series supplies one contribution for every bar.
Before
@Composable
private fun ShowStackedBar() {
val items = listOf(
"North America" to listOf(320f, 340f, 360f, 390f),
"Europe" to listOf(210f, 230f, 245f, 260f),
"Asia Pacific" to listOf(180f, 205f, 225f, 250f),
)
val dataSet = items.toMultiChartDataSet(
title = "Quarterly Revenue by Region",
prefix = "$",
categories = listOf("Q1", "Q2", "Q3", "Q4"),
)
StackedBarChart(
dataSet = dataSet,
selectedBarIndex = 1,
)
}After
StackedBarChart(
data = chartDataOf(
categories = listOf("Q1", "Q2", "Q3", "Q4"),
ChartSeries("North America", listOf(320.0, 340.0, 360.0, 390.0)),
ChartSeries("Europe", listOf(210.0, 230.0, 245.0, 260.0)),
ChartSeries("Asia Pacific", listOf(180.0, 205.0, 225.0, 250.0)),
),
modifier = Modifier.fillMaxWidth(),
title = "Quarterly Revenue by Region",
selection = rememberChartSelection(initialIndex = 1),
)The old row-oriented MultiChartDataSet input is removed. Transpose legacy
rows when migrating: each row becomes one bar, its label becomes the category
label, and each new series contains one segment column.
Styles and selection
Move customizations to the grouped StackedBarChartStyle sections such as
segments, layout, axis, and selection. Segment colors correspond to
series, not bars.
Use selection instead of selectedBarIndex. Selection applies to a whole bar,
not an individual segment. Use staticChartSelection(index) for a preset
preview or screenshot.
The old dataset prefix is not a parameter on the v3 stacked-bar API; selected
values use the chart's default formatting.
Behavior
- Series must be aligned, contain at least two bars, and use finite, nonnegative values.
- Categories are optional; when supplied, they must match every series.
- Stacks show absolute totals rather than percentages.
- Replacing data clears selection. Resizing and density changes preserve it.
interactionEnabled = falsedisables user controls while programmatic selection remains visible.