Skip to content
HDCharts

Migration Guide

Migration notes are grouped by release. If you are upgrading, read every section newer than your current version.

2.3.0

Upgrade note: If upgrading from 2.2.0 or earlier, read these migration notes.

charts-core

Reported API symbols

  • io.github.dautovicharis.charts.style.ChartViewDefaults.style(...)

Do I need to update call sites?

  • No, if you already call ChartViewDefaults.style(...) and keep the default square chart area.
  • Yes, if you want a non-square chart area; pass the new modifierChart argument.

What changed

  • ChartViewDefaults.style(...) now includes modifierChart: Modifier (default Modifier.aspectRatio(1f)) and threads it through ChartViewStyle.

Migration (only if required)

// Before
val chartViewStyle = ChartViewDefaults.style()

// After
val chartViewStyle = ChartViewDefaults.style(
    modifierChart = Modifier.aspectRatio(16f / 9f),
)
  • Recommended: Prefer named arguments when calling style factory functions.

charts-pie

Reported API symbols

  • io.github.dautovicharis.charts.style.PieChartDefaults.style(...)

Do I need to update call sites?

  • No, if you never passed innerPadding to PieChartDefaults.style(...).
  • Yes, if you previously passed innerPadding; move that value into chartViewStyle.

What changed

  • PieChartDefaults.style(...) removed innerPadding.
  • Pie content padding now comes from chartViewStyle.innerPadding.

Migration (only if required)

// Before
val pieStyle = PieChartDefaults.style(
    innerPadding = 24.dp,
)

// After
val pieStyle = PieChartDefaults.style(
    chartViewStyle = ChartViewDefaults.style(innerPadding = 24.dp),
)
  • Recommended: Keep pie container spacing and chart-view spacing aligned through one ChartViewStyle instance.

charts-bar

Reported API symbols

  • io.github.dautovicharis.charts.style.BarChartDefaults.style(...)
  • io.github.dautovicharis.charts.internal.BarValidationKt.validateBarData(...)

Do I need to update call sites?

  • No, if you call BarChartDefaults.style(...) with named arguments only.
  • Yes, if you pass BarChartDefaults.style(...) arguments positionally after barColor, or if you call internal validateBarData(...) directly.

What changed

  • BarChartDefaults.style(...) adds barColors: List<Color> = emptyList() immediately after barColor.
  • validateBarData(...) now accepts colorsSize to validate barColors length against data points.

Migration (only if required)

// Before
val barStyle = BarChartDefaults.style(
    MaterialTheme.colorScheme.primary,
    0.4f,
    10.dp,
)

// After
val barStyle = BarChartDefaults.style(
    barColor = MaterialTheme.colorScheme.primary,
    barAlpha = 0.4f,
    space = 10.dp,
)
  • Recommended: Use named arguments for BarChartDefaults.style(...) to stay resilient to future parameter additions.

3.0.0

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

  • title is separate from seriesName and category labels.
  • An empty category list hides X-axis labels; supplied categories must match the value count.
  • valueFormatter formats selected values and axisValueFormatter formats Y-axis ticks independently.
  • Selection identifies a source bar or bin. Replacing the data clears it; resizing and density changes preserve it.
  • Set interactionEnabled = false to 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)
Umbrellaio.github.dautovicharis:chartsio.github.hdcharts:charts
Modulesio.github.dautovicharis:charts-lineio.github.hdcharts:line
BOMio.github.dautovicharis:charts-bomio.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 = false disables 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 = false disables 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 = false disables user controls while programmatic selection remains visible.