Line Chart
Line charts are optimized for expressing data changes over time. They visually show trends, patterns, and continuity, and Headless Chart’s Line Chart allows complete customization.
Interface
LineChart Props
interface LineChartProps {
// Required properties
data: LineChartData;
// Optional properties
custom?: Partial<LineChartCustom>;
title?: string;
getScale?: (data: LineChartData) => LineChartScale;
}
Data Structure
type LineChartData = {
labels: string[]; // X-axis labels (time/categories)
datasets: {
legend: string; // Dataset name
values: number[]; // Y-axis values
}[];
};
type LineChartScale = {
min: number;
max: number;
step: number;
};
Basic Usage
import Widget from '@meursyphus/flitter-react';
import { LineChart } from '@meursyphus/headless-chart';
const data = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
legend: 'Visitors',
values: [1200, 1900, 3000, 2500, 4200, 3800]
}]
};
function BasicLineChart() {
return (
<Widget
width="600px"
height="400px"
widget={LineChart({ data })}
/>
);
}
Chart Components
Line Chart consists of the following hierarchical structure:
LineChart
└── Layout (overall layout)
├── Title (chart title)
├── Legend (legend)
└── Plot (plot area)
├── XAxis (X-axis)
├── YAxis (Y-axis)
├── Grid (grid)
└── Series (data series)
└── Line (individual line)
Customizable Elements
You can customize 18 components through the custom
prop:
1. Layout Elements
layout
Controls the overall chart layout. Manages the arrangement of title, legend, and plot area.
custom: {
layout: ({ title, legends, plot }) => {
return Container({
padding: EdgeInsets.all(20),
child: Column({
children: [
Row({
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [title, Row({ children: legends })]
}),
SizedBox({ height: 16 }),
Expanded({ child: plot })
]
})
});
}
}
title
Displays the chart title.
legend
Renders individual legend items.
2. Plot Area Elements
plot
The main plot area containing axes, grid, and series.
3. Data Visualization Elements
series
Container for all lines.
custom: {
series: ({ lines }) => {
return Stack({
children: lines
});
}
}
line
Individual line element. This is the most commonly customized element.
custom: {
line: ({ values, legend, index }) => {
return CustomPaint({
painter: {
svg: {
createDefaultSvgEl: (context) => ({
line: context.createSvgEl("path")
}),
paint: ({ line }, { width, height }) => {
const path = createLinePath({ values, width, height });
line.setAttribute("fill", "none");
line.setAttribute("stroke", getColor(index));
line.setAttribute("stroke-width", "2");
line.setAttribute("d", path.getD());
}
},
canvas: {
paint: (context, { width, height }) => {
const path = createLinePath({ values, width, height });
context.canvas.strokeStyle = getColor(index);
context.canvas.lineWidth = 2;
context.canvas.stroke(path.toCanvasPath());
}
}
}
});
}
}
dataLabel
Labels displayed at data points on the line.
4. Axis Elements
xAxis / yAxis
Containers for the entire axis.
xAxisLine / yAxisLine
The baseline of the axis.
xAxisLabel / yAxisLabel
Displays axis labels.
xAxisTick / yAxisTick
Tick marks on the axis.
5. Grid Elements
grid
Container for the entire grid.
gridXLine / gridYLine
Vertical/horizontal grid lines.
Real-World Usage Examples
1. Lines with Different Styles
import { CustomPaint, Path } from '@meursyphus/flitter';
const styledLineChart = LineChart({
data,
custom: {
line: ({ values, index }) => {
const styles = [
{ color: '#3b82f6', width: 2, dash: null },
{ color: '#10b981', width: 3, dash: [10, 5] },
{ color: '#f59e0b', width: 2, dash: [2, 2] }
];
const style = styles[index % styles.length];
return CustomPaint({
painter: {
svg: {
createDefaultSvgEl: (context) => ({
line: context.createSvgEl("path")
}),
paint: ({ line }, { width, height }) => {
const path = createLinePath({ values, width, height });
line.setAttribute("fill", "none");
line.setAttribute("stroke", style.color);
line.setAttribute("stroke-width", style.width.toString());
if (style.dash) {
line.setAttribute("stroke-dasharray", style.dash.join(' '));
}
line.setAttribute("d", path.getD());
}
}
}
});
}
}
});
2. Line with Data Points
import { Stack, Positioned, Container, BoxDecoration } from '@meursyphus/flitter';
const lineWithPoints = LineChart({
data,
custom: {
line: ({ values, index }) => {
const colors = ['#3b82f6', '#10b981', '#f59e0b'];
const color = colors[index % colors.length];
return Stack({
clipBehavior: 'none',
children: [
// Line
CustomPaint({
painter: createLinePainter(values, color)
}),
// Data points
...values.map((value, i) => {
const position = calculatePosition(i, value, values.length);
return Positioned({
left: position.x - 4,
top: position.y - 4,
child: Container({
width: 8,
height: 8,
decoration: new BoxDecoration({
shape: 'circle',
color: '#ffffff',
border: Border.all({
color: color,
width: 2
})
})
})
});
})
]
});
}
}
});
3. Area-Filled Line
const areaLineChart = LineChart({
data,
custom: {
line: ({ values, index }) => {
const colors = [
{ line: '#3b82f6', area: 'rgba(59, 130, 246, 0.1)' },
{ line: '#10b981', area: 'rgba(16, 185, 129, 0.1)' }
];
const { line: lineColor, area: areaColor } = colors[index % colors.length];
return Stack({
children: [
// Area fill
CustomPaint({
painter: {
svg: {
createDefaultSvgEl: (context) => ({
area: context.createSvgEl("path")
}),
paint: ({ area }, { width, height }) => {
const path = createAreaPath({ values, width, height });
area.setAttribute("fill", areaColor);
area.setAttribute("d", path.getD());
}
}
}
}),
// Line
CustomPaint({
painter: createLinePainter(values, lineColor)
})
]
});
}
}
});
4. Curved Line (Smooth)
const smoothLineChart = LineChart({
data,
custom: {
line: ({ values, index }) => {
const colors = ['#3b82f6', '#10b981', '#f59e0b'];
return CustomPaint({
painter: {
svg: {
createDefaultSvgEl: (context) => ({
line: context.createSvgEl("path")
}),
paint: ({ line }, { width, height }) => {
// Create smooth line with Bezier curves
const path = createSmoothPath({ values, width, height });
line.setAttribute("fill", "none");
line.setAttribute("stroke", colors[index % colors.length]);
line.setAttribute("stroke-width", "2");
line.setAttribute("d", path.getD());
}
}
}
});
}
}
});
function createSmoothPath({ values, width, height }) {
const path = new Path();
const points = values.map((value, index) => ({
x: (index / (values.length - 1)) * width,
y: height - (value / Math.max(...values)) * height
}));
path.moveTo(points[0]);
// Catmull-Rom spline
for (let i = 1; i < points.length; i++) {
const p0 = points[Math.max(0, i - 2)];
const p1 = points[i - 1];
const p2 = points[i];
const p3 = points[Math.min(points.length - 1, i + 1)];
const cp1x = p1.x + (p2.x - p0.x) / 6;
const cp1y = p1.y + (p2.y - p0.y) / 6;
const cp2x = p2.x - (p3.x - p1.x) / 6;
const cp2y = p2.y - (p3.y - p1.y) / 6;
path.cubicTo({ x: cp1x, y: cp1y }, { x: cp2x, y: cp2y }, p2);
}
return path;
}
Adding Animation
import { StatefulWidget, State, AnimationController, Animation } from '@meursyphus/flitter';
class AnimatedLine extends StatefulWidget {
constructor({ values, color }) {
super();
this.values = values;
this.color = color;
}
createState() {
return new AnimatedLineState();
}
}
class AnimatedLineState extends State {
initState() {
super.initState();
this.controller = new AnimationController({
duration: { milliseconds: 1500 }
});
this.animation = new Animation({
controller: this.controller,
value: Tween({ begin: 0, end: 1 })
});
this.controller.forward();
}
build() {
return AnimatedBuilder({
animation: this.animation,
builder: () => {
const progress = this.animation.value;
const visibleValues = this.widget.values.slice(
0,
Math.floor(this.widget.values.length * progress)
);
return CustomPaint({
painter: createLinePainter(visibleValues, this.widget.color)
});
}
});
}
dispose() {
this.controller.dispose();
super.dispose();
}
}
// Usage
const animatedChart = LineChart({
data,
custom: {
line: ({ values, index }) => {
const colors = ['#3b82f6', '#10b981', '#f59e0b'];
return new AnimatedLine({
values,
color: colors[index % colors.length]
});
}
}
});
Performance Optimization Tips
- Limit Data Points: Sample large datasets for display
- Path Optimization: Simplify complex paths
- Limit Animations: Use only when necessary
- Memory Management: Remove old data in real-time charts
Next Steps
- For more examples, check out the demo page
- To emphasize areas, see Area Chart
- To highlight data points, see Scatter Chart