Heatmap Chart
Heatmap charts represent two-dimensional data through color intensity. They are optimized for visually identifying patterns, correlations, and temporal changes in matrix-form data.
Interface
HeatmapChart Props
interface HeatmapChartProps {
// Required properties
data: HeatmapData;
// Optional properties
custom?: Partial<HeatmapCustom>;
title?: string;
getScale?: (data: HeatmapData) => HeatmapScale;
}
Data Structure
type HeatmapData = {
xLabels: string[]; // X-axis label array
yLabels: string[]; // Y-axis label array
values: number[][]; // 2D value array [yIndex][xIndex]
};
type HeatmapScale = {
min: number; // Minimum value
max: number; // Maximum value
};
Basic Usage
import Widget from '@meursyphus/flitter-react';
import { HeatmapChart } from '@meursyphus/headless-chart';
const data = {
xLabels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
yLabels: ['New York', 'Los Angeles', 'Chicago', 'Houston'],
values: [
[5, 8, 12, 15, 20, 22], // New York
[8, 10, 14, 18, 23, 25], // Los Angeles
[6, 9, 13, 17, 21, 24], // Chicago
[4, 7, 11, 14, 19, 21] // Houston
]
};
function BasicHeatmapChart() {
return (
<Widget
width="600px"
height="400px"
widget={HeatmapChart({ data })}
/>
);
}
Chart Components
Heatmap Chart consists of the following hierarchical structure:
HeatmapChart
└── Layout (overall layout)
├── Title (chart title)
└── Plot (plot area)
├── XAxis (X-axis)
├── YAxis (Y-axis)
└── Heatmap (heatmap area)
└── Segment (individual cell)
Customizable Elements
You can customize 13 components through the custom
prop:
1. Layout Elements
layout
Defines the overall chart layout.
custom: {
layout: ({ title, plot }) => {
return Container({
padding: EdgeInsets.all(20),
child: Column({
children: [
title,
SizedBox({ height: 20 }),
Expanded({ child: plot })
]
})
});
}
}
title
Customizes the chart title.
custom: {
title: ({ name }) => {
return Text(name, {
style: new TextStyle({
fontSize: 20,
fontWeight: 'bold',
color: '#1f2937'
})
});
}
}
plot
The plot area containing the heatmap and axes.
custom: {
plot: ({ xAxis, yAxis, heatmap }) => {
return Container({
child: Column({
children: [
Expanded({
child: Row({
children: [
yAxis,
SizedBox({ width: 10 }),
Expanded({ child: heatmap })
]
})
}),
SizedBox({ height: 10 }),
xAxis
]
})
});
}
}
2. Data Visualization Elements
heatmap
Container for all segments in the heatmap.
custom: {
heatmap: ({ segments }) => {
return Container({
decoration: new BoxDecoration({
border: Border.all({ color: '#e5e7eb', width: 1 })
}),
child: Column({
children: segments.map(row =>
Expanded({
child: Row({
children: row.map(segment =>
Expanded({ child: segment })
)
})
})
)
})
});
}
}
segment
Individual heatmap cell. This is the most commonly customized element.
custom: {
segment: ({ value, xIndex, yIndex }, { scale }) => {
// Normalize value to 0-1 range
const normalizedValue = (value - scale.min) / (scale.max - scale.min);
return Container({
margin: EdgeInsets.all(1),
decoration: new BoxDecoration({
color: `rgba(59, 130, 246, ${normalizedValue})`,
borderRadius: BorderRadius.circular(4)
})
});
}
}
3. Axis Elements
xAxis, yAxis
Define the overall structure of X and Y axes.
custom: {
xAxis: ({ line, labels, tick }) => {
return Column({
children: [
line,
SizedBox({ height: 5 }),
Row({
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: labels
})
]
});
},
yAxis: ({ line, labels, tick }) => {
return Row({
children: [
Column({
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: labels
}),
SizedBox({ width: 5 }),
line
]
});
}
}
xAxisLabel, yAxisLabel
Customize axis labels.
custom: {
xAxisLabel: ({ name, index }) => {
return Container({
padding: EdgeInsets.symmetric({ horizontal: 4, vertical: 2 }),
child: Text(name, {
style: new TextStyle({
fontSize: 12,
color: '#6b7280'
})
})
});
},
yAxisLabel: ({ name, index }) => {
return Container({
padding: EdgeInsets.symmetric({ horizontal: 4, vertical: 2 }),
child: Text(name, {
style: new TextStyle({
fontSize: 12,
color: '#6b7280'
})
})
});
}
}
xAxisLine, yAxisLine, xAxisTick, yAxisTick
Define axis lines and tick marks.
custom: {
xAxisLine: () => Container({ height: 1, color: '#e5e7eb' }),
yAxisLine: () => Container({ width: 1, color: '#e5e7eb' }),
xAxisTick: () => Container({ width: 1, height: 5, color: '#9ca3af' }),
yAxisTick: () => Container({ width: 5, height: 1, color: '#9ca3af' })
}
Real-World Usage Examples
1. Custom Color Scheme
import { Container, BoxDecoration } from '@meursyphus/flitter';
const colorSchemeHeatmap = HeatmapChart({
data,
custom: {
segment: ({ value, xIndex, yIndex }, { scale }) => {
const normalizedValue = (value - scale.min) / (scale.max - scale.min);
// Temperature color scheme (blue → red)
const getTemperatureColor = (value) => {
const r = Math.round(255 * value);
const b = Math.round(255 * (1 - value));
const g = Math.round(128 * (1 - Math.abs(value - 0.5) * 2));
return `rgb(${r}, ${g}, ${b})`;
};
return Container({
decoration: new BoxDecoration({
color: getTemperatureColor(normalizedValue),
borderRadius: BorderRadius.circular(2)
})
});
}
}
});
2. Heatmap with Value Labels
import { Center, Text, TextStyle } from '@meursyphus/flitter';
const valueLabeledHeatmap = HeatmapChart({
data,
custom: {
segment: ({ value, xIndex, yIndex }, { scale }) => {
const normalizedValue = (value - scale.min) / (scale.max - scale.min);
const backgroundColor = `rgba(99, 102, 241, ${normalizedValue})`;
const textColor = normalizedValue > 0.6 ? '#ffffff' : '#1f2937';
return Container({
decoration: new BoxDecoration({
color: backgroundColor,
borderRadius: BorderRadius.circular(4)
}),
child: Center({
child: Text(value.toFixed(1), {
style: new TextStyle({
fontSize: 11,
fontWeight: 'bold',
color: textColor
})
})
})
});
}
}
});
3. Stepped Color Heatmap
const steppedColorHeatmap = HeatmapChart({
data,
custom: {
segment: ({ value }, { scale }) => {
const normalizedValue = (value - scale.min) / (scale.max - scale.min);
// 5-step color classification
const getStepColor = (v) => {
if (v < 0.2) return '#dbeafe'; // Very low
if (v < 0.4) return '#93c5fd'; // Low
if (v < 0.6) return '#3b82f6'; // Medium
if (v < 0.8) return '#1d4ed8'; // High
return '#1e3a8a'; // Very high
};
return Container({
margin: EdgeInsets.all(0.5),
decoration: new BoxDecoration({
color: getStepColor(normalizedValue),
border: Border.all({
color: 'rgba(255, 255, 255, 0.3)',
width: 1
})
})
});
}
}
});
4. Circular Heatmap Cells
const circularHeatmap = HeatmapChart({
data,
custom: {
segment: ({ value }, { scale }) => {
const normalizedValue = (value - scale.min) / (scale.max - scale.min);
const size = 10 + normalizedValue * 30; // Size range 10-40
return Center({
child: Container({
width: size,
height: size,
decoration: new BoxDecoration({
shape: 'circle',
color: `rgba(239, 68, 68, ${normalizedValue})`,
boxShadow: [{
color: 'rgba(0, 0, 0, 0.1)',
blurRadius: 4,
offset: { x: 0, y: 2 }
}]
})
})
});
}
}
});
Adding Animation
import { StatefulWidget, State, AnimationController, Animation } from '@meursyphus/flitter';
class AnimatedSegment extends StatefulWidget {
constructor({ value, scale }) {
super();
this.value = value;
this.scale = scale;
}
createState() {
return new AnimatedSegmentState();
}
}
class AnimatedSegmentState extends State {
initState() {
super.initState();
this.controller = new AnimationController({
duration: { milliseconds: 800 }
});
const normalizedValue = (this.widget.value - this.widget.scale.min) /
(this.widget.scale.max - this.widget.scale.min);
this.colorAnimation = new Animation({
controller: this.controller,
value: Tween({
begin: 0,
end: normalizedValue
}).chain(
CurveTween({ curve: Curves.easeInOut })
)
});
// Random delay for sequential animation effect
setTimeout(() => {
this.controller.forward();
}, Math.random() * 500);
}
build() {
return AnimatedBuilder({
animation: this.colorAnimation,
builder: () => {
const opacity = this.colorAnimation.value;
return Container({
margin: EdgeInsets.all(1),
decoration: new BoxDecoration({
color: `rgba(16, 185, 129, ${opacity})`,
borderRadius: BorderRadius.circular(4)
})
});
}
});
}
dispose() {
this.controller.dispose();
super.dispose();
}
}
// Usage example
const animatedHeatmap = HeatmapChart({
data,
custom: {
segment: ({ value, xIndex, yIndex }, { scale }) => {
return new AnimatedSegment({ value, scale });
}
}
});
Performance Optimization Tips
- Large Datasets: Use simple colors rather than complex decorations when there are many cells
- Virtualization: Apply virtualization techniques to render only visible cells
- Memoization: Cache repetitive calculations like color computations
- Simple Rendering: Use simple Containers rather than SVG
Next Steps
- For more examples, check out the demo page
- To display correlations, try combining multiple heatmaps
- Time series data works effectively with animations