
Theme-agnostic charting for bar and line charts with unlimited series, grouped or stacked bars, smoothed lines with area fill, animated entries, and per-series/stable color assignment.
A theme-agnostic Compose Multiplatform charting library for bar and line charts that scale to any number of series - add a second, third, or tenth ChartSeries and the chart grows automatically. No forked code paths for "2 bars" vs "5 bars."
Built for Android, iOS, and Desktop targets via Kotlin Multiplatform, drawn entirely with Compose Canvas - no platform-specific rendering code, no third-party chart dependency underneath.
ChartDataset/ChartSeries, not a fixed number of parametersChartStyle, nothing is read from MaterialTheme internally, so it looks identical regardless of the host app's themeChartStyle.animationDurationMs
implementation("io.github.jeremy-gitau:multiplot:0.1.0")mavenCentral() is already part of any standard Kotlin Multiplatform project's settings.gradle.kts - no extra repository config needed.
val dataset = ChartDataset(
categories = listOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"),
series = listOf(
ChartSeries(id = "completed", label = "Completed", values = listOf(4f, 6f, 3f, 8f, 5f, 2f, 7f)),
ChartSeries(id = "active", label = "Active", values = listOf(2f, 1f, 4f, 2f, 3f, 1f, 2f)),
ChartSeries(id = "cancelled", label = "Cancelled", values = listOf(1f, 0f, 1f, 0f, 2f, 0f, 1f)),
),
)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.GROUPED)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.STACKED)
MultiplotLineChart(dataset = dataset)Add a fourth series any time - no other code changes needed:
series = listOf(
ChartSeries(id = "completed", label = "Completed", values = listOf(4f, 6f, 3f, 8f, 5f, 2f, 7f)),
ChartSeries(id = "active", label = "Active", values = listOf(2f, 1f, 4f, 2f, 3f, 1f, 2f)),
ChartSeries(id = "cancelled", label = "Cancelled", values = listOf(1f, 0f, 1f, 0f, 2f, 0f, 1f)),
ChartSeries(id = "refunded", label = "Refunded", values = listOf(0f, 1f, 0f, 1f, 0f, 0f, 1f)),
)Every visual knob lives on ChartStyle, passed into either chart:
val style = ChartStyle(
palette = listOf(Color(0xFF7F52FF), Color(0xFFB125EA), Color(0xFFE44857)),
gridColor = Color(0x33808080),
labelColor = Color(0xFF808080),
backgroundColor = Color(0x14808080),
cornerRadius = 12.dp,
ySteps = 5,
animationDurationMs = 900,
)
MultiplotBarChart(dataset = dataset, style = style)Leave ChartSeries.color unset to let ChartStyle.palette assign one automatically (cycling in list order by default, or using a stable hash of ChartSeries.id if you opt into that behavior). Set it explicitly to pin an exact color regardless of palette:
ChartSeries(id = "completed", label = "Completed", values = ..., color = Color(0xFF00B894))Use one of the built-in palette presets if you want a ready-made starting point:
ChartStyle(palette = MultiplotPalettes.Kotlin)
ChartStyle(palette = MultiplotPalettes.Vibrant)
ChartStyle(palette = MultiplotPalettes.Pastel)
ChartStyle(palette = MultiplotPalettes.Monochrome)If you want the same series id to keep the same palette color even when the list order changes, use stable hash assignment:
ChartStyle(
palette = MultiplotPalettes.Kotlin,
colorAssignment = ColorAssignment.ByStableHash,
)The library itself never reads MaterialTheme - build a ChartStyle from it at the call site if you want that:
val style = ChartStyle(
palette = listOf(MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.secondary),
labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
)GROUPED |
STACKED |
|---|---|
| Each series gets its own bar, side by side - best for comparing series against each other at a glance | Series stack into one bar per category - best for showing totals and composition |
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.GROUPED)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.STACKED)MultiplotLineChart(
dataset = dataset,
smooth = true, // cubic-bezier curves vs straight segments
showArea = true, // gradient fill under each line
showDots = true, // dot at every data point
emphasizeLastPoint = true, // larger dot + bold label on the final point, like "today"
)Apache License 2.0 - see LICENSE.
Issues and PRs welcome. This is a young library - if you hit a real limitation (not just a missing convenience), an issue with a minimal reproducing ChartDataset is the fastest way to get it looked at.
A theme-agnostic Compose Multiplatform charting library for bar and line charts that scale to any number of series - add a second, third, or tenth ChartSeries and the chart grows automatically. No forked code paths for "2 bars" vs "5 bars."
Built for Android, iOS, and Desktop targets via Kotlin Multiplatform, drawn entirely with Compose Canvas - no platform-specific rendering code, no third-party chart dependency underneath.
ChartDataset/ChartSeries, not a fixed number of parametersChartStyle, nothing is read from MaterialTheme internally, so it looks identical regardless of the host app's themeChartStyle.animationDurationMs
implementation("io.github.jeremy-gitau:multiplot:0.1.0")mavenCentral() is already part of any standard Kotlin Multiplatform project's settings.gradle.kts - no extra repository config needed.
val dataset = ChartDataset(
categories = listOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"),
series = listOf(
ChartSeries(id = "completed", label = "Completed", values = listOf(4f, 6f, 3f, 8f, 5f, 2f, 7f)),
ChartSeries(id = "active", label = "Active", values = listOf(2f, 1f, 4f, 2f, 3f, 1f, 2f)),
ChartSeries(id = "cancelled", label = "Cancelled", values = listOf(1f, 0f, 1f, 0f, 2f, 0f, 1f)),
),
)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.GROUPED)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.STACKED)
MultiplotLineChart(dataset = dataset)Add a fourth series any time - no other code changes needed:
series = listOf(
ChartSeries(id = "completed", label = "Completed", values = listOf(4f, 6f, 3f, 8f, 5f, 2f, 7f)),
ChartSeries(id = "active", label = "Active", values = listOf(2f, 1f, 4f, 2f, 3f, 1f, 2f)),
ChartSeries(id = "cancelled", label = "Cancelled", values = listOf(1f, 0f, 1f, 0f, 2f, 0f, 1f)),
ChartSeries(id = "refunded", label = "Refunded", values = listOf(0f, 1f, 0f, 1f, 0f, 0f, 1f)),
)Every visual knob lives on ChartStyle, passed into either chart:
val style = ChartStyle(
palette = listOf(Color(0xFF7F52FF), Color(0xFFB125EA), Color(0xFFE44857)),
gridColor = Color(0x33808080),
labelColor = Color(0xFF808080),
backgroundColor = Color(0x14808080),
cornerRadius = 12.dp,
ySteps = 5,
animationDurationMs = 900,
)
MultiplotBarChart(dataset = dataset, style = style)Leave ChartSeries.color unset to let ChartStyle.palette assign one automatically (cycling in list order by default, or using a stable hash of ChartSeries.id if you opt into that behavior). Set it explicitly to pin an exact color regardless of palette:
ChartSeries(id = "completed", label = "Completed", values = ..., color = Color(0xFF00B894))Use one of the built-in palette presets if you want a ready-made starting point:
ChartStyle(palette = MultiplotPalettes.Kotlin)
ChartStyle(palette = MultiplotPalettes.Vibrant)
ChartStyle(palette = MultiplotPalettes.Pastel)
ChartStyle(palette = MultiplotPalettes.Monochrome)If you want the same series id to keep the same palette color even when the list order changes, use stable hash assignment:
ChartStyle(
palette = MultiplotPalettes.Kotlin,
colorAssignment = ColorAssignment.ByStableHash,
)The library itself never reads MaterialTheme - build a ChartStyle from it at the call site if you want that:
val style = ChartStyle(
palette = listOf(MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.secondary),
labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
)GROUPED |
STACKED |
|---|---|
| Each series gets its own bar, side by side - best for comparing series against each other at a glance | Series stack into one bar per category - best for showing totals and composition |
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.GROUPED)
MultiplotBarChart(dataset = dataset, displayMode = BarDisplayMode.STACKED)MultiplotLineChart(
dataset = dataset,
smooth = true, // cubic-bezier curves vs straight segments
showArea = true, // gradient fill under each line
showDots = true, // dot at every data point
emphasizeLastPoint = true, // larger dot + bold label on the final point, like "today"
)Apache License 2.0 - see LICENSE.
Issues and PRs welcome. This is a young library - if you hit a real limitation (not just a missing convenience), an issue with a minimal reproducing ChartDataset is the fastest way to get it looked at.