charty

Colors and Animations#

Charty uses two custom abstractions — ChartyColor and Animation — across all chart types. This page explains both, documents the built-in color palette, and shows how to customise colors per data item, configure animation speed, and use gradient fills.


ChartyColor#

ChartyColor is a sealed class that represents either a solid color or a multi-stop gradient. All chart APIs that accept a color accept a ChartyColor rather than a raw Color.

Kotlin
sealed class ChartyColor {    abstract val value: List<Color>    data class Solid(val color: Color) : ChartyColor()    data class Gradient(val colors: List<Color>) : ChartyColor()}

Internally the library calls .value on a ChartyColor to obtain a List<Color>. For Solid this returns two identical colors — a degenerate gradient, so the same brush-building code path serves both cases without branching. For Gradient it returns the color list as given. You never need to call .value yourself.

ChartyColor.Gradient requires at least one color; an empty list throws.

Every public color parameter in Charty is a ChartyColor, never a raw Color — chart color/colors, PersistentMarker.dotColor, ChartCrosshairConfig.verticalLineColor, ChartJumpToLatestPill.backgroundColor, axis and grid colours, tooltip background, reference-line colour, and every role in ChartyTheme. The one exception is CalendarHeatmapConfig.intensityColors and its companion emptyColor: those are stops on a single interpolated intensity scale, where a stop that is itself a gradient has no meaning.

Creating colors#

Kotlin
// Solid colorval red = ChartyColor.Solid(Color.Red)val brand = ChartyColor.Solid(Color(0xFF6200EE))// Gradient (three-stop)val sunset = ChartyColor.Gradient(    listOf(Color(0xFFFF6F00), Color(0xFFE91E63), Color(0xFF9C27B0)))// Two-stop gradientval oceanGradient = ChartyColor.Gradient(    listOf(Color(0xFF006064), Color(0xFF00BCD4)))

Using ChartyColor in charts#

color (or colors on multi-series charts) is a top-level parameter on every chart composable, not nested inside the config object:

Kotlin
BarChart(    data = { barData },    color = ChartyColor.Solid(Color(0xFF1565C0)),)LineChart(    data = { lineData },    color = ChartyColor.Gradient(        listOf(Color(0xFF1565C0), Color(0xFF42A5F5))    ),)

Per-item color override#

BarData and PieData both accept an optional color: ChartyColor? field — a solid or a gradient. When set, it overrides the chart-level color for that specific data point.

Kotlin
val barData = listOf(    BarData(value = 40f, label = "Jan"),                              // uses chart-level color    BarData(value = 75f, label = "Feb", color = ChartyColor.Solid(Color.Red)), // highlighted    BarData(value = 55f, label = "Mar"),    BarData(value = 90f, label = "Apr", color = ChartyColor.Solid(Color(0xFF388E3C))), // green)BarChart(    data = { barData },    color = ChartyColor.Solid(Color(0xFF1565C0)), // default for unlabeled bars)

The same pattern applies to PieData:

Kotlin
val pieData = listOf(    PieData(value = 30f, label = "A", color = ChartyColor.Solid(Color(0xFF1565C0))),    PieData(value = 20f, label = "B", color = ChartyColor.Gradient(listOf(ChartyColors.Pink, ChartyColors.Purple))),    PieData(value = 50f, label = "C"),)

ChartyColors palette#

ChartyColors is an object that exposes a set of ready-to-use Color and ChartyColor values that match Charty's design language.

NameTypeNotes
ChartyColors.BlueColorPrimary blue (#2196F3)
ChartyColors.RedColorAccent red (#F44336)
ChartyColors.GreenColorSuccess green (#4CAF50)
ChartyColors.OrangeColorWarning orange (#FF9800)
ChartyColors.PurpleColorPurple (#9C27B0)
ChartyColors.PinkColorPink (#E91E63)
ChartyColors.CyanColorCyan (#00BCD4)
ChartyColors.TealColorTeal (#009688)
ChartyColors.IndigoColorIndigo (#3F51B5)
ChartyColors.AmberColorAmber (#FFC107)
ChartyColors.DefaultSolidChartyColor.SolidDefault solid color (Blue)
ChartyColors.DefaultGradientChartyColor.GradientBlue → Green → Orange; default for stacked/multi-value charts
ChartyColors.DefaultMultilineChartyColor.GradientPink → Blue → Green; default for multiline and comparison charts
ChartyColors.ModernPaletteChartyColor.GradientBlue, Cyan, Purple, Pink, Orange
ChartyColors.WarmPaletteChartyColor.GradientRed, Orange, Amber, Pink
ChartyColors.CoolPaletteChartyColor.GradientBlue, Cyan, Teal, Indigo
ChartyColors.NaturePaletteChartyColor.GradientGreen, Teal, Cyan, Blue
ChartyColors.VibrantPaletteChartyColor.Gradient8-stop high-contrast palette
ChartyColors.PastelPaletteChartyColor.GradientSoft, muted pastel colors
ChartyColors.DarkPaletteChartyColor.GradientDeep, rich dark shades
ChartyColors.MonochromeBlueChartyColor.GradientFive shades of blue
ChartyColors.BusinessPaletteChartyColor.GradientProfessional corporate colors
ChartyColors.FinancialGradientChartyColor.GradientRed → Orange → Amber → Green

Example usage#

Kotlin
// Solid color from the paletteBarChart(    data = { barData },    color = ChartyColor.Solid(ChartyColors.Teal),)// Pre-built gradient as the chart colorLineChart(    data = { lineData },    color = ChartyColors.DefaultGradient,)// Per-segment color overrides in a BarGroupval groupData = listOf(    BarGroup(        label = "Q1",        values = listOf(60f, 45f),        colors = listOf(            ChartyColor.Solid(ChartyColors.Blue),            ChartyColor.Solid(ChartyColors.Orange),        )    ),)

Animation#

All chart configs expose an animation property of type Animation. It drives the chart's entry reveal, and — where enabled — value tweening and the streaming slide.

Kotlin
sealed interface Animation {    data object Disabled : Animation    data class Enabled(        val duration: Int = 800,        val easing: Easing = FastOutSlowInEasing,    ) : Animation    data class Spring(        val dampingRatio: Float = Spring.DampingRatioNoBouncy,        val stiffness: Float = Spring.StiffnessLow,    ) : Animation    companion object {        val Default = Enabled()                                         // 800 ms tween        val Fast    = Enabled(duration = 400)                           // 400 ms tween        val Slow    = Enabled(duration = 1200)                          // 1200 ms tween        val Smooth  = Spring()                                          // non-bouncy spring        val Bouncy  = Spring(dampingRatio = Spring.DampingRatioMediumBouncy)    }}

There are three variants, not two: alongside Disabled and the duration-based Enabled, a physics-based Spring produces natural motion that is not bound to a fixed duration.

Presets#

PresetBehaviour
Animation.Default800 ms tween, FastOutSlowInEasing
Animation.Fast400 ms tween
Animation.Slow1 200 ms tween
Animation.SmoothSmooth, non-bouncy spring
Animation.BouncySpring with a gentle bounce
Animation.DisabledNo animation

Custom easing#

Enabled takes an easing alongside its duration:

Kotlin
BarChart(    data = { barData },    color = ChartyColor.Solid(ChartyColors.Blue),    barConfig = BarChartConfig(        animation = Animation.Enabled(duration = 600, easing = LinearOutSlowInEasing),    ),)

Springs#

Kotlin
LineChart(    data = { lineData },    color = ChartyColor.Solid(ChartyColors.Blue),    lineConfig = LineChartConfig(        animation = Animation.Spring(dampingRatio = 0.6f, stiffness = 400f),    ),)

Enabled(duration) must be positive; the init block throws otherwise.

Using presets#

Kotlin
BarChart(    data = { barData },    barConfig = BarChartConfig(        animation = Animation.Fast,    ),)LineChart(    data = { lineData },    lineConfig = LineChartConfig(        animation = Animation.Slow,    ),)

Custom duration#

Pass any integer millisecond value to Animation.Enabled:

Kotlin
BarChart(    data = { barData },    barConfig = BarChartConfig(        animation = Animation.Enabled(duration = 600),    ),)

Disabling animation#

Use Animation.Disabled to skip the entrance animation entirely. This is useful in tests or on lower-end devices:

Kotlin
BarChart(    data = { barData },    barConfig = BarChartConfig(        animation = Animation.Disabled,    ),)

Animating data changes — animateValueChanges#

animation governs the chart's entry reveal: the one-off draw when the chart first appears. animateValueChanges is a separate, opt-in switch that tweens values every time the data changes afterwards, so bars and points glide to their new heights instead of jumping.

Kotlin
BarChart(    data = { liveSales },    color = ChartyColor.Solid(ChartyColors.Blue),    barConfig = BarChartConfig(        animation = Animation.Fast,        animateValueChanges = true,    ),)
TypeBoolean
Defaultfalse — new data appears instantly
Driven bythe config's animation
No effect whenanimation = Animation.Disabled

A change in the number of points snaps to the new shape rather than tweening: with lists of different sizes there is no sensible per-index correspondence to interpolate along.

It is available on all 15 Cartesian chart configs — see Common Configuration.


The one animation Animation.Disabled does not stop#

On a chart with a rolling visibleWindow, the axis rescale always eases, even when you set Animation.Disabled. When a new extreme enters (or an old one leaves) the window, the value range glides to the new scale — falling back to Animation.Fast if you disabled animation.

This is deliberate, not a bug. Animation describes an entry reveal: a discrete, one-off event. A rescale is a continuous response to the window moving, and an axis that teleports underneath a sliding series reads as a rendering glitch rather than as "animations off".

Everything else respects Animation.Disabled as you would expect — including the window slide itself, which snaps to the plain "show last N" behaviour. See the streaming guide.


Smooth rendering#

Beyond the entrance Animation, two additional techniques produce visually smoother charts.

Line interpolation#

LineChartConfig.interpolation decides how points are connected. It applies to LineChart, AreaChart, MultilineChart, and StackedAreaChart; the area-filled charts fill under the interpolated outline, so a stepped fill follows its steps.

Kotlin
enum class LineInterpolation { LINEAR, SMOOTH, STEP }
ValueResult
LINEAR (default)Straight segments between points.
SMOOTHA cubic-Bézier curve through the points.
STEPHorizontal-then-vertical steps: the value holds until the next point, then jumps.
Kotlin
LineChart(    data = { lineData },    color = ChartyColor.Solid(ChartyColors.Purple),    lineConfig = LineChartConfig(        interpolation = LineInterpolation.SMOOTH,        animation = Animation.Default,    ),)

The older smoothCurve: Boolean is kept for compatibility. Prefer interpolation — it takes precedence, except that when interpolation is LINEAR and smoothCurve is true, the line is drawn smooth.

Kotlin
MultilineChart(    data = { seriesData },    colors = ChartyColor.Gradient(        listOf(ChartyColors.Blue, ChartyColors.Pink, ChartyColors.Teal)    ),    lineConfig = LineChartConfig(        interpolation = LineInterpolation.SMOOTH,        animation = Animation.Default,        showGradientFill = true,        gradientFillAlpha = 0.2f,    ),)

Smooth easing for WavyChart#

WavyChartConfig exposes animationEasing: Easing. The default is FastOutSlowInEasing, which gives a natural acceleration-then-deceleration feel. You can swap it for any other Compose Easing:

Kotlin
WavyChart(    data = { wavyData },    color = ChartyColor.Solid(Color(0xFF6650A4)),    wavyConfig = WavyChartConfig(        waveSegments = 40,        animationDurationMillis = 800,        animationEasing = FastOutSlowInEasing,        phaseOffsetPerBar = 0.3f,  // ripple each bar slightly out of phase    ),)
EasingFeel
FastOutSlowInEasingAccelerates then decelerates — most natural (default)
LinearEasingConstant speed
EaseOutBounceBouncy overshoot on completion
Kotlin
// Bar chart — smooth entrance, rounded topsBarChartConfig(    cornerRadius = CornerRadius.Large,    animation = Animation.Default,      // 800 ms ease-in-out)// Line chart — bezier curves + gradient fillLineChartConfig(    interpolation = LineInterpolation.SMOOTH,    animation = Animation.Default,    showGradientFill = true,    gradientFillAlpha = 0.2f,)// Wavy chart — smooth deceleration + rippleWavyChartConfig(    waveSegments = 40,    animationDurationMillis = 800,    animationEasing = FastOutSlowInEasing,    phaseOffsetPerBar = 0.3f,)

MultilineChart — per-series color assignment#

MultilineChart accepts a colors: ChartyColor parameter. Each series is assigned a color by index from the gradient's color list using a wrap-around:

seriesColor = colors.value[seriesIndex % colors.value.size]

The default is ChartyColors.DefaultMultiline (Pink → Blue → Green). Pass a ChartyColor.Gradient with one color per series to override:

Kotlin
MultilineChart(    data = { seriesData },    colors = ChartyColor.Gradient(        listOf(            ChartyColors.Blue,            ChartyColors.Red,            ChartyColors.Green,            ChartyColors.Orange,        )    ),    lineConfig = LineChartConfig(),)

If you have more series than colors in the gradient list, the colors repeat from the beginning, so you only need to supply as many colors as you want in rotation.


Legend color coordination#

When legendLabels is set on LineChartConfig, Charty renders a legend row below (or above) the chart. Each legend swatch is automatically colored using the same per-series color that the chart line uses, so the legend always stays in sync with the data without any extra work.

Kotlin
MultilineChart(    data = { seriesData },    colors = ChartyColor.Gradient(        listOf(ChartyColors.Blue, ChartyColors.Red, ChartyColors.Green)    ),    lineConfig = LineChartConfig(        legendLabels = listOf("Revenue", "Expenses", "Profit"),        legendTextStyle = TextStyle(            fontSize = 12.sp,            color = Color.Unspecified, // Color.Unspecified lets each label inherit its series color        ),    ),)

The same legendLabels / legendTextStyle properties are available on StackedAreaChart:

Kotlin
StackedAreaChart(    data = { areaData },    lineConfig = LineChartConfig(        legendLabels = listOf("Layer A", "Layer B", "Layer C"),    ),)

Pass Color.Unspecified as legendTextStyle.color to make each legend label render in its own series color. Pass any explicit color (e.g. Color.Black) to use the same color for all labels.


Gradient fill in MultilineChart#

MultilineChart supports an optional shaded area beneath each line. The fill uses the same color as the line, drawn at a reduced alpha so the lines remain legible.

Enable it via LineChartConfig:

Kotlin
MultilineChart(    data = { seriesData },    lineConfig = LineChartConfig(        showGradientFill = true,        gradientFillAlpha = 0.3f, // 0.0 (transparent) to 1.0 (opaque)    ),)
PropertyDefaultDescription
showGradientFillfalseWhen true, draws a filled area from each line down to the x-axis.
gradientFillAlpha0.3fAlpha applied to the line's color when rendering the filled area. Lower values produce a lighter fill.

The fill is drawn before the line in the rendering order, so the line always appears on top of the shaded area regardless of overlap between series.

Kotlin
// Subtle fillLineChartConfig(showGradientFill = true, gradientFillAlpha = 0.15f)// Strong fillLineChartConfig(showGradientFill = true, gradientFillAlpha = 0.5f)

showGradientFill is only available on MultilineChart. AreaChart and StackedAreaChart always render filled areas as part of their core visual.