{
double _bubbleScale = 1.0;
final _marketData = [
{
'name': 'TechCorp Solutions',
'revenue': 250.0,
'customers': 180.0,
'marketShare': 28.0,
'category': 'Enterprise',
},
{
'name': 'StartupX Labs',
'revenue': 85.0,
'customers': 120.0,
'marketShare': 12.0,
'category': 'Startup',
},
{
'name': 'MidSize Systems',
'revenue': 150.0,
'customers': 160.0,
'marketShare': 18.0,
'category': 'SMB',
},
// ... more data
];
@override
Widget build(BuildContext context) {
final minSize = 8.0 + _bubbleScale * 5.0;
final maxSize = 15.0 + _bubbleScale * 15.0;
return Scaffold(
body: Column(
children: [
// Header with controls
Container(
padding: EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Market Performance Analysis',
style: Theme.of(context).textTheme.headlineSmall,
),
Text('Revenue vs Customer Base'),
],
),
),
// Bubble size control
Column(
children: [
Text('Bubble Size'),
Slider(
value: _bubbleScale,
min: 0.1,
max: 2.0,
divisions: 19,
label: '${(_bubbleScale * 100).round()}%',
onChanged: (value) => setState(() => _bubbleScale = value),
),
],
),
],
),
),
// Chart
Expanded(
child: Padding(
padding: EdgeInsets.all(16),
child: CristalyseChart()
.data(_marketData)
.mapping(
x: 'revenue',
y: 'customers',
size: 'marketShare',
color: 'category',
)
.geomBubble(
minSize: minSize,
maxSize: maxSize,
alpha: 0.75,
borderWidth: 2.0,
borderColor: Colors.white,
)
.scaleXContinuous(
labels: (value) => '\$${value.toStringAsFixed(0)}M',
)
.scaleYContinuous(
labels: (value) => '${value.toStringAsFixed(0)}K',
)
.theme(ChartTheme.defaultTheme())
.animate(
duration: Duration(milliseconds: 1000),
curve: Curves.easeOutCubic,
)
.interaction(
tooltip: TooltipConfig(
builder: _buildRichTooltip,
),
)
.build(),
),
),
// Legend and insights
Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Category legend
_buildLegend(),
SizedBox(height: 16),
// Key insights
_buildInsightsPanel(),
],
),
),
],
),
);
}
Widget _buildRichTooltip(DataPointInfo point) {
final name = point.getDisplayValue('name');
final revenue = point.getDisplayValue('revenue');
final customers = point.getDisplayValue('customers');
final marketShare = point.getDisplayValue('marketShare');
final category = point.getDisplayValue('category');
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text('Revenue: \$${revenue}M', style: TextStyle(color: Colors.white)),
Text('Customers: ${customers}K', style: TextStyle(color: Colors.white)),
Text('Market Share: ${marketShare}%', style: TextStyle(color: Colors.white)),
Text('Category: ${category}', style: TextStyle(color: Colors.white)),
],
),
);
}
Widget _buildLegend() {
final categories = ['Enterprise', 'SMB', 'Startup'];
final colors = [Colors.blue, Colors.green, Colors.orange];
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: categories.asMap().entries.map((entry) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: colors[entry.key],
shape: BoxShape.circle,
),
),
SizedBox(width: 6),
Text(entry.value),
],
),
);
}).toList(),
);
}
Widget _buildInsightsPanel() {
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue[200]!),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.insights, color: Colors.blue[700]),
SizedBox(width: 8),
Text(
'Key Insights',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue[900],
),
),
],
),
SizedBox(height: 8),
Text(
'• Bubble size represents market share percentage\n'
'• Hover over bubbles for detailed company metrics\n'
'• Colors indicate company categories',
style: TextStyle(fontSize: 12, color: Colors.blue[700]),
),
],
),
);
}
}
```
## Best Practices
* Use bubble size for positive, quantitative variables
* Keep size ratios between 2:1 and 5:1 for optimal perception
* Ensure smallest bubbles are clearly visible (minimum 5-8px radius)
* Consider area vs radius scaling based on your data distribution
* Use alpha (transparency) for overlapping bubbles
* Limit color categories to 6-8 for clarity
* Add white borders to improve bubble separation
* Consider colorblind-friendly palettes
* For dense data, increase transparency and reduce size
* Use tooltips instead of labels for cluttered charts
* Consider data aggregation or filtering for very large datasets
* Provide zoom/pan functionality for detailed exploration
* Always include informative tooltips
* Use consistent hover states
* Consider click-through navigation to detail views
* Provide size controls for user customization
* For 500+ bubbles, reduce border width or disable borders
* Use lower alpha values for better performance
* Consider data virtualization for very large datasets
* Test on different device sizes and performance levels
## Common Use Cases
Revenue vs customers with market share sizing
Risk vs return with position size bubbles
Multi-dimensional KPI dashboards
Three-variable correlation analysis
Population vs GDP with area sizing
Price vs quality with popularity sizing
## Next Steps
Basic two-dimensional point charts
Matrix-based data visualization
Add tooltips and click handlers
Smooth entrance and transition effects
# Dual Axis Charts
Source: https://docs.cristalyse.com/charts/dual-axis
Combine different metrics on independent scales for enhanced data analysis
See dual axis charts in action with interactive examples
## Overview
Dual axis charts allow you to correlate two different metrics using independent Y-scales. Cristalyse offers full support for dual axis configurations across various chart types, providing a complete view of complex data relationships.
Dual axis charts for correlating two different metrics on independent scales
## Basic Dual Axis Chart
Combine bars and lines with dual Y-axes:
```dart theme={null}
final data = [
{'month': 'Jan', 'revenue': 100, 'growth': 2.5},
{'month': 'Feb', 'revenue': 150, 'growth': 3.0},
{'month': 'Mar', 'revenue': 130, 'growth': 2.8},
];
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'revenue') // Primary Y-axis
.mappingY2('growth') // Secondary Y-axis
.geomBar(yAxis: YAxis.primary) // Bind bars to primary axis
.geomLine(
yAxis: YAxis.secondary,
strokeWidth: 2.0,
color: Colors.orange,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0, max: 200)
.scaleY2Continuous(min: 0, max: 5)
.build()
```
## Stacked Dual Axis Example
Utilize dual axes with stacked bars:
```dart theme={null}
final energyData = [
{'year': '2020', 'coal': 30, 'renewables': 20, 'efficiency': 0.8},
{'year': '2021', 'coal': 25, 'renewables': 30, 'efficiency': 0.9},
{'year': '2022', 'coal': 20, 'renewables': 35, 'efficiency': 1.0},
];
CristalyseChart()
.data(energyData)
.mapping(x: 'year', y: 'coal') // Primary Y-axis
.mappingY2('efficiency') // Secondary Y-axis
.geomBar(yAxis: YAxis.primary, // Bind to primary axis
color: Colors.blue,
alpha: 0.7,
style: BarStyle.stacked
)
.geomLine(
yAxis: YAxis.secondary,
strokeWidth: 3.0,
color: Colors.green,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.scaleY2Continuous(min: 0, max: 2)
.build()
```
## Interactive Features
### Hover and Zoom
Add interactivity to enhance data exploration:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'revenue')
.mappingY2('growth')
.geomBar(yAxis: YAxis.primary)
.geomLine(
yAxis: YAxis.secondary,
strokeWidth: 2.0,
color: Colors.orange,
)
.interaction(
tooltip: TooltipConfig(
builder: (point) {
return Text(
'${point.getDisplayValue('month')}: Revenue ${point.getDisplayValue('revenue')}k, Growth ${point.getDisplayValue('growth')}%',
style: TextStyle(color: Colors.white),
);
},
),
)
.build()
```
## Styling Options
Customize colors, scales, and styles to match your needs. Dual axis charts provide flexibility in how data series are presented, whether using bars, lines, or points across primary and secondary axes.
## Best Practices
* Choose complementary data series for dual axis charts
* Ensure the scales' units are relevant and helpful
* Keep the design clear with distinct colors
* Avoid information overload - focus on key metrics
* Limit the number of points displayed for clarity
* Use staggered animations for smoother rendering
## Common Use Cases
Correlate revenue and profit margins for comprehensive insights
Track sales growth vs conversion rates using multiple axes
Compare fuel types and efficiency improvements over time
Investigate relationships between distinct datasets
## Ready to explore more?
Bring charts to life with smooth transitions
Customize chart appearances for brand consistency
# Heat Map Charts
Source: https://docs.cristalyse.com/charts/heat-map-charts
Visualize 2D data patterns with color-coded intensity heat maps
See heat map charts in action with interactive examples
## Overview
Heat maps are perfect for visualizing 2D data patterns where color intensity represents value magnitude. Cristalyse heat maps support customizable color schemes (via `colorGradient`), interactive tooltips, and responsive layouts for effective data exploration.
**API Note**: Heat maps use `.mappingHeatMap()` instead of `.mapping()`, and color gradients are specified via the `colorGradient` parameter in `.geomHeatMap()`. If no `colorGradient` is specified, the default `GradientColorScale.heatMap()` gradient (dark blue → cyan → lime green → bright red) is automatically applied.
Heat maps with customizable color gradients and interactive cell highlighting
## Basic Heat Map
Create a simple heat map to show data density:
```dart theme={null}
final performanceData = [
{'region': 'North', 'month': 'Jan', 'sales': 120},
{'region': 'North', 'month': 'Feb', 'sales': 135},
{'region': 'North', 'month': 'Mar', 'sales': 98},
{'region': 'South', 'month': 'Jan', 'sales': 85},
{'region': 'South', 'month': 'Feb', 'sales': 92},
{'region': 'South', 'month': 'Mar', 'sales': 110},
{'region': 'West', 'month': 'Jan', 'sales': 165},
{'region': 'West', 'month': 'Feb', 'sales': 140},
{'region': 'West', 'month': 'Mar', 'sales': 180},
];
CristalyseChart()
.data(performanceData)
.mappingHeatMap(x: 'month', y: 'region', value: 'sales')
.geomHeatMap(
cellSpacing: 2.0,
cellBorderRadius: BorderRadius.circular(4),
showValues: true,
// Specify custom gradient or omit for default GradientColorScale.heatMap()
colorGradient: [Colors.lightBlue.shade100, Colors.blue.shade800],
interpolateColors: true,
)
.scaleXOrdinal()
.scaleYOrdinal()
.theme(ChartTheme.defaultTheme())
.build()
```
## Business Performance Heat Map
Monitor regional sales performance across months:
```dart theme={null}
final businessData = [
{'region': 'North America', 'month': 'Q1', 'sales': 450},
{'region': 'North America', 'month': 'Q2', 'sales': 520},
{'region': 'North America', 'month': 'Q3', 'sales': 380},
{'region': 'North America', 'month': 'Q4', 'sales': 620},
{'region': 'Europe', 'month': 'Q1', 'sales': 320},
{'region': 'Europe', 'month': 'Q2', 'sales': 290},
{'region': 'Europe', 'month': 'Q3', 'sales': 410},
{'region': 'Europe', 'month': 'Q4', 'sales': 480},
{'region': 'Asia Pacific', 'month': 'Q1', 'sales': 180},
{'region': 'Asia Pacific', 'month': 'Q2', 'sales': 220},
{'region': 'Asia Pacific', 'month': 'Q3', 'sales': 280},
{'region': 'Asia Pacific', 'month': 'Q4', 'sales': 350},
];
CristalyseChart()
.data(businessData)
.mappingHeatMap(x: 'month', y: 'region', value: 'sales')
.geomHeatMap(
cellSpacing: 2.0,
cellBorderRadius: BorderRadius.circular(6),
showValues: true,
colorGradient: [Colors.red.shade200, Colors.orange, Colors.green.shade600],
interpolateColors: true,
valueFormatter: (value) => '\$${value.toStringAsFixed(0)}K',
valueTextStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
),
)
.scaleXOrdinal()
.scaleYOrdinal()
.theme(ChartTheme.defaultTheme())
.build()
```
## System Monitoring Heat Map
Visualize server response times across hours and services:
```dart theme={null}
final monitoringData = [
{'hour': '00:00', 'service': 'API Gateway', 'responseTime': 45},
{'hour': '00:00', 'service': 'Auth Service', 'responseTime': 32},
{'hour': '00:00', 'service': 'Database', 'responseTime': 28},
{'hour': '06:00', 'service': 'API Gateway', 'responseTime': 120},
{'hour': '06:00', 'service': 'Auth Service', 'responseTime': 85},
{'hour': '06:00', 'service': 'Database', 'responseTime': 95},
{'hour': '12:00', 'service': 'API Gateway', 'responseTime': 200},
{'hour': '12:00', 'service': 'Auth Service', 'responseTime': 150},
{'hour': '12:00', 'service': 'Database', 'responseTime': 180},
{'hour': '18:00', 'service': 'API Gateway', 'responseTime': 180},
{'hour': '18:00', 'service': 'Auth Service', 'responseTime': 130},
{'hour': '18:00', 'service': 'Database', 'responseTime': 160},
];
CristalyseChart()
.data(monitoringData)
.mappingHeatMap(x: 'hour', y: 'service', value: 'responseTime')
.geomHeatMap(
cellSpacing: 1.0,
cellBorderRadius: BorderRadius.circular(2),
showValues: true,
minValue: 0,
maxValue: 250,
colorGradient: [
Colors.green.shade400,
Colors.yellow,
Colors.red.shade600,
],
interpolateColors: true,
nullValueColor: Colors.grey.shade300,
valueFormatter: (value) => '${value.toInt()}ms',
valueTextStyle: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: Colors.white,
),
)
.scaleXOrdinal()
.scaleYOrdinal()
.theme(ChartTheme.darkTheme())
.build()
```
## Correlation Matrix Heat Map
Display statistical correlations with diverging color scheme:
```dart theme={null}
final correlationData = [
{'var1': 'Revenue', 'var2': 'Marketing', 'correlation': 0.85},
{'var1': 'Revenue', 'var2': 'Sales Team', 'correlation': 0.92},
{'var1': 'Revenue', 'var2': 'Customer Sat', 'correlation': 0.74},
{'var1': 'Marketing', 'var2': 'Revenue', 'correlation': 0.85},
{'var1': 'Marketing', 'var2': 'Sales Team', 'correlation': 0.68},
{'var1': 'Marketing', 'var2': 'Customer Sat', 'correlation': 0.45},
{'var1': 'Sales Team', 'var2': 'Revenue', 'correlation': 0.92},
{'var1': 'Sales Team', 'var2': 'Marketing', 'correlation': 0.68},
{'var1': 'Sales Team', 'var2': 'Customer Sat', 'correlation': 0.58},
{'var1': 'Customer Sat', 'var2': 'Revenue', 'correlation': 0.74},
{'var1': 'Customer Sat', 'var2': 'Marketing', 'correlation': 0.45},
{'var1': 'Customer Sat', 'var2': 'Sales Team', 'correlation': 0.58},
];
CristalyseChart()
.data(correlationData)
.mappingHeatMap(x: 'var1', y: 'var2', value: 'correlation')
.geomHeatMap(
cellSpacing: 2.0,
cellBorderRadius: BorderRadius.circular(3),
showValues: true,
minValue: -1.0,
maxValue: 1.0,
// Diverging gradient: negative (blue) → neutral (white) → positive (red)
colorGradient: [
Colors.blue.shade700,
Colors.white,
Colors.red.shade700,
],
interpolateColors: true,
valueFormatter: (value) => value.toStringAsFixed(2),
valueTextStyle: TextStyle(fontSize: 11),
cellAspectRatio: 1.0, // Square cells
)
.scaleXOrdinal()
.scaleYOrdinal()
.theme(ChartTheme.defaultTheme())
.build()
```
## Styling Options
### Color Gradients
Define custom color schemes for different data types:
```dart theme={null}
// Single color gradient (low to high)
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
colorGradient: [Colors.white, Colors.deepPurple],
interpolateColors: true,
)
.build()
// Multi-step gradient
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
colorGradient: [
Colors.blue.shade900,
Colors.blue.shade300,
Colors.white,
Colors.orange.shade300,
Colors.red.shade900,
],
interpolateColors: true,
)
.build()
// Diverging gradient
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
colorGradient: [Colors.blue, Colors.white, Colors.red],
interpolateColors: true,
)
.build()
```
### Cell Styling
Customize cell appearance and borders:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
cellSpacing: 3.0,
cellBorderRadius: BorderRadius.circular(8),
cellAspectRatio: 1.5, // width:height ratio
showValues: true,
)
.build()
```
### Value Text Styling
Control value text appearance:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
showValues: true,
valueTextStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
)
.build()
```
## Data Handling
### Missing Values
Handle null or missing data points:
```dart theme={null}
CristalyseChart()
.data(dataWithNulls)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
nullValueColor: Colors.grey.shade200,
)
.build()
```
### Value Ranges
Set explicit minimum and maximum values:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap(
minValue: 0,
maxValue: 100,
// values outside range are clamped
colorGradient: [Colors.white, Colors.red],
interpolateColors: true,
)
.build()
```
### Value Formatting
Customize how values are displayed:
```dart theme={null}
import 'package:intl/intl.dart';
// Currency formatting
CristalyseChart()
.data(salesData)
.mappingHeatMap(x: 'month', y: 'region', value: 'revenue')
.geomHeatMap(
showValues: true,
valueFormatter: (value) => NumberFormat.simpleCurrency().format(value),
)
.build()
// Percentage formatting
CristalyseChart()
.data(percentageData)
.mappingHeatMap(x: 'category', y: 'segment', value: 'percentage')
.geomHeatMap(
showValues: true,
valueFormatter: (value) => '${(value * 100).toStringAsFixed(1)}%',
)
.build()
// Custom formatting
CristalyseChart()
.data(temperatureData)
.mappingHeatMap(x: 'hour', y: 'day', value: 'temperature')
.geomHeatMap(
showValues: true,
valueFormatter: (value) => '${value.toStringAsFixed(1)}°C',
)
.build()
```
## Interactive Features
### Hover Effects
Add rich tooltips on cell hover:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap()
.interaction(
tooltip: TooltipConfig(
builder: (point) {
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.8),
borderRadius: BorderRadius.circular(6),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${point.getDisplayValue('x')} - ${point.getDisplayValue('y')}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
Text(
'Value: ${point.getDisplayValue('value')}',
style: TextStyle(color: Colors.white),
),
],
),
);
},
),
)
.build()
```
### Click Handlers
React to cell selection:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap()
.interaction(
click: ClickConfig(
onTap: (point) {
print('Clicked cell: ${point.data}');
// Show detailed view
showCellDetails(point.data);
},
),
)
.build()
```
## Animation Options
### Fade In Animation
Cells appear with smooth fade transition:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap()
.animate(
duration: Duration(milliseconds: 1000),
curve: Curves.easeInOut,
)
.build()
```
### Staggered Animation
Each cell animates with a slight delay:
```dart theme={null}
CristalyseChart()
.data(data)
.mappingHeatMap(x: 'x', y: 'y', value: 'value')
.geomHeatMap()
.animate(
duration: Duration(milliseconds: 1500),
curve: Curves.elasticOut,
stagger: Duration(milliseconds: 50),
)
.build()
```
## Best Practices
### When to Use Heat Maps
**Good for:**
* 2D categorical data visualization
* Correlation matrices
* Time-based patterns (hour vs day)
* Geographic data on grids
* Performance monitoring dashboards
**Avoid for:**
* Continuous spatial data
* Data with more than 20x20 cells
* Precise value comparison
* Single-dimension data
### Design Tips
* Use intuitive color schemes (cool to warm for intensity)
* Ensure sufficient color contrast for accessibility
* Limit grid size to maintain readability
* Consider showing values for precise reading
* Use diverging colors for data with meaningful zero point
### Performance Considerations
* Optimize for datasets with \< 400 cells (20x20)
* Use simpler styling for large grids
* Consider data aggregation for very large datasets
* Test color schemes for colorblind accessibility
## Common Patterns
### Website Analytics Heat Map
```dart theme={null}
final analyticsData = [
{'page': 'Home', 'hour': '9 AM', 'visitors': 245},
{'page': 'Home', 'hour': '12 PM', 'visitors': 380},
{'page': 'Home', 'hour': '6 PM', 'visitors': 520},
{'page': 'Products', 'hour': '9 AM', 'visitors': 180},
{'page': 'Products', 'hour': '12 PM', 'visitors': 290},
{'page': 'Products', 'hour': '6 PM', 'visitors': 350},
];
CristalyseChart()
.data(analyticsData)
.mappingHeatMap(x: 'hour', y: 'page', value: 'visitors')
.geomHeatMap(
cellSpacing: 2.0,
showValues: true,
colorGradient: [Colors.blue.shade100, Colors.blue.shade800],
interpolateColors: true,
)
.build()
```
### Financial Risk Matrix
```dart theme={null}
final riskData = [
{'probability': 'Low', 'impact': 'Low', 'risk': 1},
{'probability': 'Low', 'impact': 'Medium', 'risk': 2},
{'probability': 'Low', 'impact': 'High', 'risk': 3},
{'probability': 'Medium', 'impact': 'Low', 'risk': 2},
{'probability': 'Medium', 'impact': 'Medium', 'risk': 4},
{'probability': 'Medium', 'impact': 'High', 'risk': 6},
{'probability': 'High', 'impact': 'Low', 'risk': 3},
{'probability': 'High', 'impact': 'Medium', 'risk': 6},
{'probability': 'High', 'impact': 'High', 'risk': 9},
];
CristalyseChart()
.data(riskData)
.mappingHeatMap(x: 'probability', y: 'impact', value: 'risk')
.geomHeatMap(
cellSpacing: 2.0,
cellAspectRatio: 1.25,
showValues: true,
colorGradient: [Colors.green, Colors.yellow, Colors.red],
interpolateColors: true,
)
.build()
```
## Next Steps
Explore relationships between continuous variables
Add advanced interactive features to charts
Customize colors and styling themes
Save heat maps as images or data files
# Line Charts
Source: https://docs.cristalyse.com/charts/line-charts
Time series and trend analysis with multi-series support
See line charts in action with interactive examples
## Overview
Line charts are essential for displaying trends and patterns in data over a period. Cristalyse provides an elegant API for crafting intuitive line charts.
Progressive line drawing with customizable themes and multi-series support
## Basic Line Chart
Display a simple line chart to show trends:
```dart theme={null}
data = [
{'month': 'Jan', 'value': 30},
{'month': 'Feb', 'value': 45},
{'month': 'Mar', 'value': 60},
];
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine(strokeWidth: 2.0, color: Colors.blue)
.scaleXOrdinal()
.scaleYContinuous()
.build();
```
## Multi-Series Line Chart
Compare series by plotting multiple lines:
```dart theme={null}
data = [
{'month': 'Jan', 'platform': 'iOS', 'users': 1200},
{'month': 'Jan', 'platform': 'Android', 'users': 800},
{'month': 'Feb', 'platform': 'iOS', 'users': 1350},
{'month': 'Feb', 'platform': 'Android', 'users': 950},
];
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'users', color: 'platform')
.geomLine(strokeWidth: 3.0)
.geomPoint(size: 4.0)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build();
```
## Progressive Drawing
Animate lines progressively from start to end:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine(strokeWidth: 2.0)
.animate(duration: Duration(milliseconds: 1500))
.build();
```
## Styling Lines
### Dashed and Dotted Lines
Change line styles for variety and emphasis:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine(
strokeWidth: 3.0,
style: LineStyle.dashed,
)
.build();
```
### Highlighting Points
Enhance visual storytelling by emphasizing points:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine(strokeWidth: 2.0)
.geomPoint(size: 6.0, color: Colors.red)
.build();
```
## Custom Category Colors
Assign specific colors to line series for brand consistency or semantic meaning:
```dart theme={null}
// Product launch performance tracking
final productColors = {
'Premium Edition': const Color(0xFFAB47BC), // Purple - luxury
'Standard Edition': const Color(0xFF42A5F5), // Blue - reliable
'Lite Edition': const Color(0xFF66BB6A), // Green - accessible
'Beta Version': const Color(0xFFFF7043), // Orange - experimental
};
CristalyseChart()
.data(launchMetrics)
.mapping(x: 'week', y: 'downloads', color: 'product')
.geomLine(strokeWidth: 2.5)
.geomPoint(size: 4.0)
.customPalette(categoryColors: productColors)
.build();
```
### Performance Status Lines
Use semantic colors for status or performance indicators:
```dart theme={null}
// Network latency by data center
final dcColors = {
'US-East': const Color(0xFF1E88E5), // Blue - primary
'US-West': const Color(0xFF43A047), // Green - secondary
'EU-Central': const Color(0xFFE53935), // Red - international
'Asia-Pacific': const Color(0xFFFF9800), // Orange - distant
};
CristalyseChart()
.data(latencyData)
.mapping(x: 'timestamp', y: 'latency_ms', color: 'datacenter')
.geomLine(strokeWidth: 2.0, alpha: 0.8)
.customPalette(categoryColors: dcColors)
.build();
```
## Interactive Features
### Tooltips
Provide context with informative tooltips:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine()
.interaction(
tooltip: TooltipConfig(
builder: (point) {
return Text(
'Value: ${point.getDisplayValue('value')}',
style: TextStyle(color: Colors.white),
);
},
),
)
.build();
```
## Performance Optimizations
Cristalyse is designed to handle large datasets efficiently. For optimal performance:
* Keep stroke width moderate
* Use lightweight themes
* Optimize data points and mapping categories
Ready to explore more chart types? [Back to Chart Gallery](charts/area-charts)
# Pie Charts
Source: https://docs.cristalyse.com/charts/pie-charts
Visualize proportions and percentages with animated pie and donut charts
See pie charts in action with interactive examples
## Overview
Pie charts are perfect for displaying proportional data and percentages. Cristalyse supports both traditional pie charts and donut charts with smooth animations, customizable styling, and interactive features.
Pie and donut charts with slice-by-slice progressive animation
## Basic Pie Chart
Create a simple pie chart to show proportions:
```dart theme={null}
final marketData = [
{'company': 'Apple', 'marketShare': 28.5},
{'company': 'Samsung', 'marketShare': 22.1},
{'company': 'Google', 'marketShare': 15.8},
{'company': 'Xiaomi', 'marketShare': 12.3},
{'company': 'Others', 'marketShare': 21.3},
];
CristalyseChart()
.data(marketData)
.mapping(
pieValue: 'marketShare',
pieCategory: 'company',
)
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: true,
)
.theme(ChartTheme.defaultTheme())
.build()
```
## Donut Chart
Create a donut chart with a hollow center:
```dart theme={null}
final salesData = [
{'region': 'North America', 'sales': 450000},
{'region': 'Europe', 'sales': 320000},
{'region': 'Asia Pacific', 'sales': 280000},
{'region': 'Latin America', 'sales': 150000},
{'region': 'Middle East', 'sales': 100000},
];
CristalyseChart()
.data(salesData)
.mapping(
pieValue: 'sales',
pieCategory: 'region',
)
.geomPie(
outerRadius: 180,
innerRadius: 80, // Creates donut effect
showLabels: true,
showPercentages: false,
)
.theme(ChartTheme.defaultTheme())
.build()
```
## Exploded Pie Chart
Create separation between slices for emphasis:
```dart theme={null}
final budgetData = [
{'category': 'Development', 'amount': 450000},
{'category': 'Marketing', 'amount': 280000},
{'category': 'Operations', 'amount': 320000},
{'category': 'Sales', 'amount': 180000},
];
CristalyseChart()
.data(budgetData)
.mapping(
pieValue: 'amount',
pieCategory: 'category',
)
.geomPie(
outerRadius: 160,
explodeSlices: true,
explodeDistance: 15,
showLabels: true,
showPercentages: true,
)
.theme(ChartTheme.defaultTheme())
.build()
```
## Styling Options
### Custom Colors
Override default color mapping:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
colors: [
Colors.blue,
Colors.green,
Colors.orange,
Colors.red,
Colors.purple,
],
)
.build()
```
### Stroke Styling
Add borders to pie slices:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
strokeWidth: 2.0,
strokeColor: Colors.white,
showLabels: true,
)
.build()
```
### Custom Label Styling
Customize label appearance:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: true,
labelRadius: 180,
labelStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
)
.build()
```
## Animation Options
### Progressive Slice Animation
Slices animate in sequence:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
animationDuration: Duration(milliseconds: 1500),
)
.animate(
duration: Duration(milliseconds: 1500),
curve: Curves.easeInOutCubic,
)
.build()
```
### Custom Start Angle
Control where the pie starts:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
startAngle: -pi / 2, // Start at top (12 o'clock)
)
.build()
```
## Label Formatting
Transform pie chart labels by passing a callback to the `labels` parameter.
### Simple Custom Labels
For basic formatting, you can use a simple callback:
```dart theme={null}
CristalyseChart()
.data(salesData)
.mappingPie(value: 'revenue', category: 'region')
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: false,
labels: (value) => '\$${value}K', // $450K
)
.build()
```
### Using NumberFormat
For robust, locale-aware formatting, use NumberFormat:
```dart theme={null}
import 'package:intl/intl.dart';
// Currency formatting
CristalyseChart()
.data(revenueData)
.mappingPie(value: 'revenue', category: 'department')
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: false,
labels: NumberFormat.simpleCurrency().format, // $1,234.56
)
.build()
// Compact user counts
CristalyseChart()
.data(userData)
.mappingPie(value: 'users', category: 'platform')
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: false,
labels: NumberFormat.compact().format, // 1.2K
)
.build()
```
### Percentage Formatting
When `showPercentages: true`, your callback receives ratio values (0.0-1.0):
```dart theme={null}
// Default percentage formatting
CristalyseChart()
.data(surveyData)
.mappingPie(value: 'responses', category: 'rating')
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: true, // Uses NumberFormat.percentPattern()
)
.build()
// Custom percentage with NumberFormat
CristalyseChart()
.data(surveyData)
.mappingPie(value: 'responses', category: 'rating')
.geomPie(
outerRadius: 150,
showLabels: true,
showPercentages: true,
labels: NumberFormat.percentPattern().format, // 23%
)
.build()
```
## Real-World Examples
### E-commerce Sales by Category
```dart theme={null}
final ecommerceData = [
{'category': 'Electronics', 'sales': 2850000, 'color': Colors.blue},
{'category': 'Clothing', 'sales': 1920000, 'color': Colors.green},
{'category': 'Home & Garden', 'sales': 1450000, 'color': Colors.orange},
{'category': 'Books', 'sales': 890000, 'color': Colors.red},
{'category': 'Sports', 'sales': 720000, 'color': Colors.purple},
];
CristalyseChart()
.data(ecommerceData)
.mapping(
pieValue: 'sales',
pieCategory: 'category',
)
.geomPie(
outerRadius: 180,
innerRadius: 60,
showLabels: true,
showPercentages: true,
labelRadius: 220,
strokeWidth: 3,
strokeColor: Colors.white,
explodeSlices: true,
explodeDistance: 8,
)
.theme(ChartTheme.defaultTheme())
.build()
```
### Project Time Allocation
```dart theme={null}
final projectData = [
{'phase': 'Planning', 'hours': 120},
{'phase': 'Development', 'hours': 480},
{'phase': 'Testing', 'hours': 160},
{'phase': 'Deployment', 'hours': 80},
{'phase': 'Documentation', 'hours': 60},
];
CristalyseChart()
.data(projectData)
.mapping(
pieValue: 'hours',
pieCategory: 'phase',
)
.geomPie(
outerRadius: 160,
showLabels: true,
showPercentages: true,
labelStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
),
)
.theme(ChartTheme.defaultTheme())
.build()
```
## Advanced Features
### Interactive Pie Charts
Add hover effects and click handling:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: 150,
showLabels: true,
onSliceClick: (data, index) {
print('Clicked slice: ${data['category']}');
},
)
.interactions(
enableHover: true,
enableClick: true,
)
.build()
```
### Responsive Sizing
Automatically adjust based on container size:
```dart theme={null}
LayoutBuilder(
builder: (context, constraints) {
final radius = math.min(constraints.maxWidth, constraints.maxHeight) / 3;
return CristalyseChart()
.data(data)
.mapping(
pieValue: 'value',
pieCategory: 'category',
)
.geomPie(
outerRadius: radius,
innerRadius: radius * 0.4,
showLabels: constraints.maxWidth > 300,
)
.build();
},
)
```
## Best Practices
### When to Use Pie Charts
**Good for:**
* Showing parts of a whole
* Displaying percentages
* Comparing proportions (5-7 categories max)
* Simple data visualization
**Avoid for:**
* Too many categories (>7)
* Comparing exact values
* Time series data
* Negative values
### Design Tips
* Limit to 5-7 slices for readability
* Use contrasting colors
* Order slices by size (largest first)
* Consider donut charts for modern look
* Use exploded slices sparingly for emphasis
### Performance Considerations
* Optimize for large datasets by grouping small values
* Use appropriate animation durations
* Consider static rendering for print/export
* Test on different screen sizes
## Common Patterns
### Market Share Analysis
```dart theme={null}
final marketShare = [
{'company': 'Leader', 'share': 35.2},
{'company': 'Challenger', 'share': 24.8},
{'company': 'Follower', 'share': 18.5},
{'company': 'Niche', 'share': 12.1},
{'company': 'Others', 'share': 9.4},
];
CristalyseChart()
.data(marketShare)
.mapping(pieValue: 'share', pieCategory: 'company')
.geomPie(
outerRadius: 160,
showLabels: true,
showPercentages: true,
explodeSlices: true,
explodeDistance: 10,
)
.build()
```
### Survey Results
```dart theme={null}
final surveyResults = [
{'response': 'Very Satisfied', 'count': 45},
{'response': 'Satisfied', 'count': 38},
{'response': 'Neutral', 'count': 12},
{'response': 'Dissatisfied', 'count': 3},
{'response': 'Very Dissatisfied', 'count': 2},
];
CristalyseChart()
.data(surveyResults)
.mapping(pieValue: 'count', pieCategory: 'response')
.geomPie(
outerRadius: 150,
innerRadius: 50,
showLabels: true,
showPercentages: true,
)
.build()
```
# Progress Bars
Source: https://docs.cristalyse.com/charts/progress-bars
Professional progress visualization with multiple orientations and advanced styles
See progress bars in action with interactive examples
## Overview
Progress bars are essential for visualizing completion status, goal achievement, and performance metrics. Cristalyse provides comprehensive progress bar support with multiple orientations and advanced styling options perfect for dashboards, project management tools, and analytics applications.
## Basic Progress Bars
### Horizontal Progress Bar
The classic left-to-right progress visualization:
```dart theme={null}
final taskData = [
{'task': 'Design', 'completion': 75.0},
{'task': 'Development', 'completion': 60.0},
{'task': 'Testing', 'completion': 30.0},
];
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(
orientation: ProgressOrientation.horizontal,
barThickness: 20.0,
cornerRadius: 10.0,
showLabels: true,
)
.scaleYContinuous(min: 0, max: 100)
.build()
```
### Vertical Progress Bar
Bottom-to-top progress display:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(
orientation: ProgressOrientation.vertical,
barThickness: 30.0,
cornerRadius: 15.0,
showLabels: true,
)
.scaleXContinuous(min: 0, max: 100)
.build()
```
### Circular Progress Bar
Ring-style progress indicators:
```dart theme={null}
CristalyseChart()
.data([{'metric': 'CPU Usage', 'value': 68.0}])
.mappingProgress(category: 'metric', value: 'value')
.geomProgress(
orientation: ProgressOrientation.circular,
barThickness: 15.0,
startAngle: -90.0, // Start at top
endAngle: 270.0, // Full circle
)
.build()
```
## Advanced Styles
### Stacked Progress Bars
Multi-segment progress showing completion by category:
```dart theme={null}
final projectData = [
{'project': 'Mobile App', 'stage': 'Design', 'progress': 30.0},
{'project': 'Mobile App', 'stage': 'Development', 'progress': 50.0},
{'project': 'Mobile App', 'stage': 'Testing', 'progress': 20.0},
];
CristalyseChart()
.data(projectData)
.mappingProgress(
category: 'project',
value: 'progress',
group: 'stage', // Groups create segments
)
.geomProgress(
style: ProgressStyle.stacked,
orientation: ProgressOrientation.horizontal,
barThickness: 25.0,
showLabels: true,
)
.build()
```
**Perfect for:**
* Project phase tracking
* Budget allocation visualization
* Time spent analysis
* Multi-category completion
### Grouped Progress Bars
Side-by-side progress bars for category comparison:
```dart theme={null}
final teamData = [
{'team': 'Engineering', 'metric': 'Sprint Goals', 'achievement': 85.0},
{'team': 'Engineering', 'metric': 'Code Quality', 'achievement': 92.0},
{'team': 'Design', 'metric': 'Sprint Goals', 'achievement': 78.0},
{'team': 'Design', 'metric': 'Code Quality', 'achievement': 88.0},
];
CristalyseChart()
.data(teamData)
.mappingProgress(
category: 'team',
value: 'achievement',
group: 'metric',
)
.geomProgress(
style: ProgressStyle.grouped,
orientation: ProgressOrientation.vertical,
barThickness: 20.0,
)
.legend()
.build()
```
**Perfect for:**
* Team performance comparison
* KPI tracking across departments
* A/B test results
* Multi-metric dashboards
### Gauge Style
Semi-circular gauge-style indicators:
```dart theme={null}
CristalyseChart()
.data([
{'metric': 'CPU', 'usage': 68.0},
{'metric': 'Memory', 'usage': 82.0},
{'metric': 'Disk', 'usage': 45.0},
])
.mappingProgress(category: 'metric', value: 'usage')
.geomProgress(
style: ProgressStyle.gauge,
orientation: ProgressOrientation.circular,
gaugeRadius: 120.0, // Required for gauge style
barThickness: 18.0,
startAngle: -135.0, // Bottom-left
endAngle: 135.0, // Bottom-right (270° arc)
showLabels: true,
)
.build()
```
**Required Parameter:** Gauge style progress bars require the `gaugeRadius` parameter to be specified.
**Perfect for:**
* System resource monitoring
* Performance dashboards
* Speed/capacity indicators
* Real-time metrics
### Concentric Circles
Nested circular progress rings for multi-metric displays:
```dart theme={null}
final metricsData = [
{'metric': 'Sales Target', 'achievement': 85.0},
{'metric': 'Quality Score', 'achievement': 92.0},
{'metric': 'Customer Satisfaction', 'achievement': 78.0},
];
CristalyseChart()
.data(metricsData)
.mappingProgress(category: 'metric', value: 'achievement')
.geomProgress(
style: ProgressStyle.concentric,
orientation: ProgressOrientation.circular,
barThickness: 12.0,
startAngle: -90.0,
endAngle: 270.0,
)
.legend(position: LegendPosition.bottom)
.build()
```
**Perfect for:**
* Multi-KPI dashboards
* Goal achievement tracking
* Health/fitness metrics
* Balanced scorecard visualizations
## Styling & Customization
### Colors & Themes
Progress bars automatically adapt to your theme:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion', group: 'priority')
.geomProgress(
style: ProgressStyle.stacked,
orientation: ProgressOrientation.horizontal,
)
.theme(ChartTheme.darkTheme()) // Dark mode support
.legend()
.build()
```
### Custom Colors
Use custom color palettes for semantic meaning:
```dart theme={null}
final statusColors = {
'Critical': Colors.red,
'Warning': Colors.orange,
'Normal': Colors.green,
};
CristalyseChart()
.data(systemData)
.mappingProgress(category: 'system', value: 'health', group: 'status')
.geomProgress(style: ProgressStyle.grouped)
.customPalette(categoryColors: statusColors)
.build()
```
### Track Colors
Customize the background track color:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(
orientation: ProgressOrientation.horizontal,
trackColor: Colors.grey.shade200, // Custom track
barThickness: 20.0,
cornerRadius: 10.0,
)
.build()
```
## Labels & Formatting
### Show Percentage Labels
Display completion percentages:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(
orientation: ProgressOrientation.horizontal,
showLabels: true,
labelFormatter: (value) => '${value.toStringAsFixed(1)}%',
labelStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
)
.build()
```
### Custom Label Formatting
```dart theme={null}
import 'package:intl/intl.dart';
// Currency formatting for budget progress
CristalyseChart()
.data(budgetData)
.mappingProgress(category: 'department', value: 'spent')
.geomProgress(
showLabels: true,
labelFormatter: (value) => NumberFormat.simpleCurrency().format(value),
)
.build()
```
## Interactive Features
### Tooltips
Add hover information:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(orientation: ProgressOrientation.horizontal)
.interaction(
tooltip: TooltipConfig(
builder: (point) {
final task = point.getDisplayValue('task');
final completion = point.getDisplayValue('completion');
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'$task: $completion%',
style: TextStyle(color: Colors.white),
),
);
},
),
)
.build()
```
### Click Handlers
Respond to user interactions:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(orientation: ProgressOrientation.horizontal)
.interaction(
click: ClickConfig(
onTap: (point) {
final task = point.getDisplayValue('task');
showTaskDetails(context, task);
},
),
)
.build()
```
## Animations
### Progressive Filling
Animate progress filling:
```dart theme={null}
CristalyseChart()
.data(taskData)
.mappingProgress(category: 'task', value: 'completion')
.geomProgress(
orientation: ProgressOrientation.horizontal,
barThickness: 20.0,
)
.animate(
duration: Duration(milliseconds: 1200),
curve: Curves.easeOutCubic,
)
.build()
```
### Circular Animations
Smooth circular progress animations:
```dart theme={null}
CristalyseChart()
.data([{'metric': 'Loading', 'progress': 75.0}])
.mappingProgress(category: 'metric', value: 'progress')
.geomProgress(
orientation: ProgressOrientation.circular,
barThickness: 15.0,
)
.animate(
duration: Duration(milliseconds: 1500),
curve: Curves.elasticOut,
)
.build()
```
## Real-World Examples
### Project Management Dashboard
```dart theme={null}
final projectData = [
{'project': 'Website Redesign', 'phase': 'Planning', 'progress': 100.0},
{'project': 'Website Redesign', 'phase': 'Design', 'progress': 80.0},
{'project': 'Website Redesign', 'phase': 'Development', 'progress': 45.0},
{'project': 'Website Redesign', 'phase': 'Testing', 'progress': 0.0},
];
CristalyseChart()
.data(projectData)
.mappingProgress(
category: 'project',
value: 'progress',
group: 'phase',
)
.geomProgress(
style: ProgressStyle.stacked,
orientation: ProgressOrientation.horizontal,
barThickness: 30.0,
cornerRadius: 15.0,
showLabels: true,
)
.theme(ChartTheme.defaultTheme())
.legend(position: LegendPosition.bottom)
.build()
```
### System Monitoring Dashboard
```dart theme={null}
final systemMetrics = [
{'resource': 'CPU', 'usage': 68.0},
{'resource': 'Memory', 'usage': 82.0},
{'resource': 'Disk', 'usage': 45.0},
{'resource': 'Network', 'usage': 35.0},
];
final statusColors = {
'CPU': Colors.blue,
'Memory': Colors.orange,
'Disk': Colors.green,
'Network': Colors.purple,
};
CristalyseChart()
.data(systemMetrics)
.mappingProgress(category: 'resource', value: 'usage')
.geomProgress(
style: ProgressStyle.gauge,
orientation: ProgressOrientation.circular,
gaugeRadius: 100.0,
barThickness: 16.0,
startAngle: -135.0,
endAngle: 135.0,
showLabels: true,
)
.customPalette(categoryColors: statusColors)
.legend(position: LegendPosition.right)
.build()
```
### Fitness Tracker
```dart theme={null}
final fitnessGoals = [
{'goal': 'Daily Steps', 'achievement': 85.0},
{'goal': 'Calories Burned', 'achievement': 92.0},
{'goal': 'Active Minutes', 'achievement': 78.0},
{'goal': 'Sleep Hours', 'achievement': 95.0},
];
CristalyseChart()
.data(fitnessGoals)
.mappingProgress(category: 'goal', value: 'achievement')
.geomProgress(
style: ProgressStyle.concentric,
orientation: ProgressOrientation.circular,
barThickness: 14.0,
startAngle: -90.0,
endAngle: 270.0,
)
.theme(ChartTheme.solarizedLightTheme())
.legend(position: LegendPosition.bottom)
.animate(duration: Duration(milliseconds: 1500))
.build()
```
## Configuration Options
### ProgressOrientation
* `horizontal` - Left-to-right progress
* `vertical` - Bottom-to-top progress
* `circular` - Ring-style circular progress
### ProgressStyle
* `standard` - Single progress bar (default)
* `stacked` - Multi-segment stacked progress
* `grouped` - Side-by-side grouped progress
* `gauge` - Semi-circular gauge (requires `gaugeRadius`)
* `concentric` - Nested circular rings
### Common Parameters
| Parameter | Type | Description |
| ---------------- | ----------- | ------------------------------------------------------- |
| `barThickness` | `double` | Height (horizontal) or width (vertical) of progress bar |
| `cornerRadius` | `double` | Border radius for rounded corners |
| `trackColor` | `Color` | Background track color |
| `showLabels` | `bool` | Display value labels |
| `labelFormatter` | `Function` | Custom label formatting function |
| `labelStyle` | `TextStyle` | Text style for labels |
| `startAngle` | `double` | Start angle for circular progress (degrees) |
| `endAngle` | `double` | End angle for circular progress (degrees) |
| `gaugeRadius` | `double` | Radius for gauge-style progress (required for gauge) |
## Best Practices
### Data Structure
Ensure your data has the required fields:
```dart theme={null}
// Single progress bar
final data = [
{'category': 'Task Name', 'value': 75.0},
];
// Grouped/Stacked progress
final data = [
{'category': 'Project A', 'value': 30.0, 'group': 'Phase 1'},
{'category': 'Project A', 'value': 50.0, 'group': 'Phase 2'},
];
```
### Value Ranges
Keep values within meaningful ranges:
```dart theme={null}
// Percentages: 0-100
.scaleYContinuous(min: 0, max: 100)
// Absolute values
.scaleYContinuous(min: 0, max: 1000)
```
### Performance
For real-time updates:
```dart theme={null}
// Use StatefulWidget for live progress
class LiveProgress extends StatefulWidget {
@override
_LiveProgressState createState() => _LiveProgressState();
}
class _LiveProgressState extends State {
double _progress = 0.0;
@override
void initState() {
super.initState();
// Update progress periodically
Timer.periodic(Duration(seconds: 1), (timer) {
setState(() => _progress = (_progress + 5) % 100);
});
}
@override
Widget build(BuildContext context) {
return CristalyseChart()
.data([{'task': 'Processing', 'value': _progress}])
.mappingProgress(category: 'task', value: 'value')
.geomProgress(orientation: ProgressOrientation.horizontal)
.build();
}
}
```
## Troubleshooting
Make sure you've specified the required `gaugeRadius` parameter:
```dart theme={null}
.geomProgress(
style: ProgressStyle.gauge,
gaugeRadius: 120.0, // Required!
)
```
Adjust the `barThickness` parameter:
```dart theme={null}
.geomProgress(
barThickness: 25.0, // Increase for thicker bars
)
```
Ensure your data includes the `group` parameter in mapping:
```dart theme={null}
.mappingProgress(
category: 'project',
value: 'progress',
group: 'stage', // Required for stacked/grouped
)
```
Adjust the `startAngle` parameter:
```dart theme={null}
.geomProgress(
startAngle: -90.0, // Start at top (12 o'clock)
endAngle: 270.0, // Full circle
)
```
## Related Documentation
* [Animations](/features/animations) - Add smooth progress animations
* [Theming](/features/theming) - Customize progress bar appearance
* [Interactions](/features/interactions) - Add tooltips and click handlers
* [Legends](/features/legends) - Display category legends
Need help? Check our [GitHub Discussions](https://github.com/rudi-q/cristalyse/discussions) or [report an issue](https://github.com/rudi-q/cristalyse/issues).
# Scatter Plots
Source: https://docs.cristalyse.com/charts/scatter-plots
Point-based visualizations with size and color mapping
See scatter plots in action with interactive examples
## Overview
Scatter plots are perfect for exploring relationships between two continuous variables. Cristalyse's scatter plots support multi-dimensional encoding through color, size, and shape mappings.
Interactive scatter plots with smooth animations and multi-dimensional data mapping
## Basic Scatter Plot
The simplest scatter plot maps X and Y coordinates to data:
```dart theme={null}
final data = [
{'x': 1, 'y': 2},
{'x': 2, 'y': 3},
{'x': 3, 'y': 1},
{'x': 4, 'y': 4},
];
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint()
.scaleXContinuous()
.scaleYContinuous()
.build()
```
## Color Mapping
Add categorical grouping with color:
```dart theme={null}
final data = [
{'x': 1, 'y': 2, 'category': 'A'},
{'x': 2, 'y': 3, 'category': 'B'},
{'x': 3, 'y': 1, 'category': 'A'},
{'x': 4, 'y': 4, 'category': 'C'},
];
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(size: 8.0, alpha: 0.8)
.scaleXContinuous()
.scaleYContinuous()
.theme(ChartTheme.defaultTheme())
.build()
```
## Size Mapping
Encode a third dimension with point size:
```dart theme={null}
final salesData = [
{'revenue': 100, 'deals': 25, 'region': 'North', 'team_size': 5},
{'revenue': 150, 'deals': 30, 'region': 'South', 'team_size': 8},
{'revenue': 80, 'deals': 20, 'region': 'East', 'team_size': 3},
{'revenue': 200, 'deals': 35, 'region': 'West', 'team_size': 12},
];
CristalyseChart()
.data(salesData)
.mapping(
x: 'revenue',
y: 'deals',
color: 'region',
size: 'team_size'
)
.geomPoint(alpha: 0.7)
.scaleXContinuous(min: 0)
.scaleYContinuous(min: 0)
.build()
```
## Point Styling
### Shape Options
Customize point shapes for different data categories:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint(
size: 10.0,
shape: PointShape.triangle, // circle, square, triangle, diamond
borderWidth: 2.0,
alpha: 0.8,
)
.build()
```
Available shapes:
* `PointShape.circle` (default)
* `PointShape.square`
* `PointShape.triangle`
* `PointShape.diamond`
### Advanced Styling
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(
size: 12.0,
alpha: 0.8,
shape: PointShape.circle,
borderWidth: 1.5, // Border thickness
color: Colors.blue, // Override color mapping
)
.build()
```
## Multi-Dimensional Analysis
### Business Intelligence Example
Analyze sales performance across multiple dimensions:
```dart theme={null}
final performanceData = [
{
'revenue': 150000,
'customer_satisfaction': 4.2,
'region': 'North America',
'deal_count': 45,
'rep_experience': 3.5,
},
{
'revenue': 120000,
'customer_satisfaction': 3.8,
'region': 'Europe',
'deal_count': 38,
'rep_experience': 2.1,
},
{
'revenue': 180000,
'customer_satisfaction': 4.6,
'region': 'Asia Pacific',
'deal_count': 52,
'rep_experience': 5.2,
},
];
CristalyseChart()
.data(performanceData)
.mapping(
x: 'revenue',
y: 'customer_satisfaction',
color: 'region',
size: 'deal_count',
)
.geomPoint(alpha: 0.7, borderWidth: 1.0)
.scaleXContinuous(min: 0)
.scaleYContinuous(min: 1, max: 5)
.theme(ChartTheme.defaultTheme())
.build()
```
## Interactive Scatter Plots
### Tooltips
Add rich hover information:
```dart theme={null}
CristalyseChart()
.data(performanceData)
.mapping(x: 'revenue', y: 'customer_satisfaction', color: 'region')
.geomPoint(size: 8.0)
.interaction(
tooltip: TooltipConfig(
builder: (point) {
final region = point.getDisplayValue('region');
final revenue = point.getDisplayValue('revenue');
final satisfaction = point.getDisplayValue('customer_satisfaction');
return Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
region,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'Revenue: \$${revenue}',
style: TextStyle(color: Colors.white),
),
Text(
'Satisfaction: ${satisfaction}/5',
style: TextStyle(color: Colors.white),
),
],
),
);
},
),
)
.build()
```
### Click Handlers
React to point selection:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(size: 8.0)
.interaction(
click: ClickConfig(
onTap: (point) {
print('Selected point: ${point.data}');
// Navigate to detail view
// Show dialog
// Update other charts
},
),
)
.build()
```
## Animation
### Entrance Animation
Animate points appearing:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(size: 8.0, alpha: 0.8)
.animate(
duration: Duration(milliseconds: 800),
curve: Curves.elasticOut,
)
.build()
```
### Staggered Animation
Points appear in sequence:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint(size: 10.0)
.animate(
duration: Duration(milliseconds: 1200),
curve: Curves.easeInOutCubic,
)
.build()
```
## Dual Y-Axis Support
Use scatter plots on secondary Y-axis:
```dart theme={null}
final mixedData = [
{'quarter': 'Q1', 'revenue': 120, 'efficiency': 85, 'satisfaction': 4.2},
{'quarter': 'Q2', 'revenue': 150, 'efficiency': 92, 'satisfaction': 4.5},
{'quarter': 'Q3', 'revenue': 110, 'efficiency': 78, 'satisfaction': 3.9},
];
CristalyseChart()
.data(mixedData)
.mapping(x: 'quarter', y: 'revenue')
.mappingY2('satisfaction')
.geomBar(yAxis: YAxis.primary) // Revenue bars
.geomPoint( // Satisfaction points
yAxis: YAxis.secondary,
size: 10.0,
color: Colors.orange,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.scaleY2Continuous(min: 1, max: 5)
.build()
```
## Best Practices
* Use alpha (transparency) for overlapping points
* Consider point size relative to data density
* For 1000+ points, reduce size and increase alpha
* Limit color categories to 8-10 for readability
* Use consistent color palettes across charts
* Consider colorblind-friendly palettes
* Use size for quantitative variables only
* Ensure size differences are perceptually meaningful
* Avoid extreme size ratios (keep within 2:1 to 5:1)
* For large datasets (1000+ points), disable borders
* Use lower alpha values for dense datasets
* Consider data sampling for very large datasets
## Common Use Cases
Explore relationships between variables
Identify unusual data points visually
Discover natural groupings in data
Analyze 3-4 dimensions simultaneously
## Next Steps
Connect data points with lines
Add tooltips and click handlers
Smooth entrance and transition effects
Customize colors and visual styles
# Cristalyse MCP Server
Source: https://docs.cristalyse.com/cristalyse-mcp-server
Connect Cristalyse documentation to your AI coding assistant using the Model Context Protocol (MCP)
The Cristalyse MCP Server provides AI coding assistants with direct access to Cristalyse documentation, examples, and best practices through the Model Context Protocol (MCP). This enables your AI assistant to provide accurate, up-to-date information about Cristalyse directly in your coding environment.
## What is MCP?
The Model Context Protocol (MCP) is a standardized way for AI applications to securely connect to external data sources and tools. By adding the Cristalyse MCP server, your AI assistant gains access to:
* Complete Cristalyse documentation
* Code examples and snippets
* Best practices and patterns
* API reference materials
* Troubleshooting guides
## Setup Instructions
Choose your AI coding assistant below for specific setup instructions:
Add the following configuration to your MCP settings file:
```json theme={null}
{
"mcpServers": {
"cristalyse_docs": {
"command": "npx",
"args": [
"mcp-remote",
"https://docs.cristalyse.com/mcp"
]
}
}
}
```
Add the following configuration to your MCP settings file:
```json theme={null}
{
"mcpServers": {
"cristalyse_docs": {
"url": "https://docs.cristalyse.com/mcp"
}
}
}
```
Add the following configuration to your MCP settings file:
```json theme={null}
{
"mcpServers": {
"cristalyse_docs": {
"url": "https://docs.cristalyse.com/mcp"
}
}
}
```
Add the following configuration to your MCP settings file:
```json theme={null}
{
"mcpServers": {
"cristalyse_docs": {
"url": "https://docs.cristalyse.com/mcp"
}
}
}
```
Run the following command in your terminal:
```bash theme={null}
claude mcp add --transport http cristalyse_docs https://docs.cristalyse.com/mcp
```
Follow these steps to add the Cristalyse MCP server to Claude:
### Step 1: Add the MCP Server
1. Navigate to the **Connectors** page in the Claude settings
2. Select **Add custom connector**
3. Add the Cristalyse MCP server with these details:
* **Name**: `CristalyseDocs`
* **URL**: `https://docs.cristalyse.com/mcp`
4. Select **Add**
### Step 2: Use the MCP Server in Your Chat
1. When using Claude, select the **attachments button** (the plus icon)
2. Select the **CristalyseDocs** MCP server
3. Ask Claude any question about Cristalyse documentation
## Example Usage
Once connected, you can ask your AI assistant questions like:
* "How do I create a scatter plot with Cristalyse?"
* "What are the available chart types in Cristalyse?"
* "Show me an example of custom theming"
* "How do I add animations to my charts?"
* "What's the best way to handle large datasets?"
The AI assistant will have access to the complete Cristalyse documentation and can provide accurate, contextual responses with code examples.
## Benefits
**Always Up-to-Date**: Access the latest documentation directly from the source\
**Context-Aware**: Get relevant examples based on your current code\
**Faster Development**: No need to switch between your IDE and documentation\
**Better Code Quality**: Follow best practices and patterns recommended in the docs\
**Instant Help**: Get immediate answers to Cristalyse-related questions
## Troubleshooting
If you're having issues connecting to the MCP server:
1. **Check your internet connection** - The MCP server requires internet access
2. **Verify the URL** - Make sure you're using `https://docs.cristalyse.com/mcp`
3. **Restart your AI assistant** - Sometimes a restart is needed after adding new MCP servers
4. **Check your MCP settings** - Ensure the JSON configuration is valid
Need help? Visit our [Questions section](https://github.com/rudi-q/cristalyse/discussions/categories/q-a) for community support.
# Examples
Source: https://docs.cristalyse.com/examples
Real-world chart examples and use cases
Want to see Cristalyse in action? [Try our interactive demo →](https://example.cristalyse.com)
## Chart Gallery
Explore different chart types and their use cases with complete code examples.
Point-based visualizations with size and color mapping
Time series and trend analysis
Categorical data with multiple variations
Volume and cumulative data visualization
Business dashboards with multiple metrics
Tooltips, pan/zoom, and click handlers
## Scatter Plots
### Basic Scatter Plot
Perfect for exploring relationships between two variables:
```dart theme={null}
final data = [
{'x': 1, 'y': 2.3, 'category': 'A'},
{'x': 2, 'y': 3.1, 'category': 'B'},
{'x': 3, 'y': 1.8, 'category': 'A'},
{'x': 4, 'y': 4.2, 'category': 'C'},
{'x': 5, 'y': 2.9, 'category': 'B'},
];
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(
size: 8.0,
alpha: 0.8,
shape: PointShape.circle,
borderWidth: 1.5,
)
.scaleXContinuous()
.scaleYContinuous()
.theme(ChartTheme.defaultTheme())
.animate(duration: Duration(milliseconds: 800))
.build()
```
### Multi-Dimensional Scatter Plot
Using size to encode a third dimension:
```dart theme={null}
final salesData = [
{'revenue': 100, 'deals': 25, 'region': 'North', 'rep_count': 5},
{'revenue': 150, 'deals': 30, 'region': 'South', 'rep_count': 8},
{'revenue': 80, 'deals': 20, 'region': 'East', 'rep_count': 3},
{'revenue': 200, 'deals': 35, 'region': 'West', 'rep_count': 12},
];
CristalyseChart()
.data(salesData)
.mapping(x: 'revenue', y: 'deals', color: 'region', size: 'rep_count')
.geomPoint(alpha: 0.7)
.scaleXContinuous(min: 0)
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build()
```
## Line Charts
### Time Series
Classic time series visualization:
```dart theme={null}
final timeSeriesData = [
{'month': 'Jan', 'users': 1200, 'platform': 'iOS'},
{'month': 'Feb', 'users': 1350, 'platform': 'iOS'},
{'month': 'Mar', 'users': 1100, 'platform': 'iOS'},
{'month': 'Jan', 'users': 800, 'platform': 'Android'},
{'month': 'Feb', 'users': 950, 'platform': 'Android'},
{'month': 'Mar', 'users': 1200, 'platform': 'Android'},
];
CristalyseChart()
.data(timeSeriesData)
.mapping(x: 'month', y: 'users', color: 'platform')
.geomLine(strokeWidth: 3.0)
.geomPoint(size: 6.0)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.animate(duration: Duration(milliseconds: 1200))
.build()
```
### Combined Line + Points
Enhanced visualization with both lines and points:
```dart theme={null}
CristalyseChart()
.data(analyticsData)
.mapping(x: 'week', y: 'engagement')
.geomLine(strokeWidth: 2.0, alpha: 0.8)
.geomPoint(size: 5.0, alpha: 0.9)
.scaleXContinuous()
.scaleYContinuous()
.theme(ChartTheme.darkTheme())
.build()
```
### Custom Category Colors
Assign specific colors to categories for consistent branding and semantic meaning:
```dart theme={null}
// E-commerce performance by channel
final channelColors = {
'Organic Search': const Color(0xFF34D399), // Green - growth
'Paid Ads': const Color(0xFF60A5FA), // Blue - investment
'Social Media': const Color(0xFFF59E0B), // Yellow - engagement
'Email': const Color(0xFFEF4444), // Red - direct
'Referrals': const Color(0xFF8B5CF6), // Purple - partnership
};
final channelData = [
{'quarter': 'Q1', 'revenue': 45000, 'channel': 'Organic Search'},
{'quarter': 'Q2', 'revenue': 52000, 'channel': 'Organic Search'},
{'quarter': 'Q1', 'revenue': 28000, 'channel': 'Paid Ads'},
{'quarter': 'Q2', 'revenue': 35000, 'channel': 'Paid Ads'},
{'quarter': 'Q1', 'revenue': 18000, 'channel': 'Social Media'},
{'quarter': 'Q2', 'revenue': 22000, 'channel': 'Social Media'},
{'quarter': 'Q1', 'revenue': 15000, 'channel': 'Email'},
{'quarter': 'Q2', 'revenue': 19000, 'channel': 'Email'},
];
CristalyseChart()
.data(channelData)
.mapping(x: 'quarter', y: 'revenue', color: 'channel')
.geomLine(strokeWidth: 2.5)
.geomPoint(size: 6.0)
.customPalette(categoryColors: channelColors)
.scaleXOrdinal()
.scaleYContinuous(min: 0, labels: (value) => '\$${(value/1000).toInt()}K')
.build()
```
### Server Health Monitoring
Semantic colors for system status tracking:
```dart theme={null}
// Infrastructure monitoring dashboard
final healthColors = {
'Healthy': const Color(0xFF10B981), // Green - all good
'Warning': const Color(0xFFF59E0B), // Amber - attention needed
'Critical': const Color(0xFFEF4444), // Red - urgent
'Maintenance': const Color(0xFF6B7280), // Gray - planned downtime
};
final serverData = [
{'hour': '00:00', 'count': 12, 'status': 'Healthy'},
{'hour': '06:00', 'count': 15, 'status': 'Healthy'},
{'hour': '12:00', 'count': 8, 'status': 'Healthy'},
{'hour': '00:00', 'count': 2, 'status': 'Warning'},
{'hour': '06:00', 'count': 3, 'status': 'Warning'},
{'hour': '12:00', 'count': 1, 'status': 'Warning'},
{'hour': '00:00', 'count': 0, 'status': 'Critical'},
{'hour': '06:00', 'count': 1, 'status': 'Critical'},
{'hour': '12:00', 'count': 2, 'status': 'Critical'},
];
CristalyseChart()
.data(serverData)
.mapping(x: 'hour', y: 'count', color: 'status')
.geomArea(strokeWidth: 1.5, alpha: 0.6)
.customPalette(categoryColors: healthColors)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.darkTheme())
.build()
```
## Bar Charts
### Vertical Bar Chart
Standard categorical data visualization:
```dart theme={null}
final revenueData = [
{'quarter': 'Q1', 'revenue': 120},
{'quarter': 'Q2', 'revenue': 150},
{'quarter': 'Q3', 'revenue': 110},
{'quarter': 'Q4', 'revenue': 180},
];
CristalyseChart()
.data(revenueData)
.mapping(x: 'quarter', y: 'revenue')
.geomBar(
width: 0.8,
alpha: 0.9,
borderRadius: BorderRadius.circular(4),
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build()
```
### Grouped Bar Chart
Compare multiple series side-by-side:
```dart theme={null}
final productData = [
{'quarter': 'Q1', 'revenue': 120, 'product': 'Widget A'},
{'quarter': 'Q2', 'revenue': 150, 'product': 'Widget A'},
{'quarter': 'Q1', 'revenue': 80, 'product': 'Widget B'},
{'quarter': 'Q2', 'revenue': 110, 'product': 'Widget B'},
];
CristalyseChart()
.data(productData)
.mapping(x: 'quarter', y: 'revenue', color: 'product')
.geomBar(
style: BarStyle.grouped,
width: 0.8,
alpha: 0.9,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build()
```
### Stacked Bar Chart
Show composition and totals:
```dart theme={null}
final budgetData = [
{'department': 'Marketing', 'amount': 50, 'category': 'Personnel'},
{'department': 'Marketing', 'amount': 30, 'category': 'Technology'},
{'department': 'Marketing', 'amount': 20, 'category': 'Travel'},
{'department': 'Sales', 'amount': 80, 'category': 'Personnel'},
{'department': 'Sales', 'amount': 25, 'category': 'Technology'},
];
CristalyseChart()
.data(budgetData)
.mapping(x: 'department', y: 'amount', color: 'category')
.geomBar(
style: BarStyle.stacked,
width: 0.8,
alpha: 0.9,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build()
```
### Horizontal Bar Chart
Great for long category names:
```dart theme={null}
CristalyseChart()
.data(departmentData)
.mapping(x: 'department', y: 'headcount')
.geomBar(
borderRadius: BorderRadius.circular(4),
borderWidth: 1.0,
)
.coordFlip() // Makes it horizontal
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.theme(ChartTheme.defaultTheme())
.build()
```
## Area Charts
### Basic Area Chart
Perfect for showing volume over time:
```dart theme={null}
final trafficData = [
{'month': 'Jan', 'visitors': 1200},
{'month': 'Feb', 'visitors': 1350},
{'month': 'Mar', 'visitors': 1100},
{'month': 'Apr', 'visitors': 1800},
{'month': 'May', 'visitors': 1650},
];
CristalyseChart()
.data(trafficData)
.mapping(x: 'month', y: 'visitors')
.geomArea(
strokeWidth: 2.0,
alpha: 0.3,
fillArea: true,
color: Colors.blue,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.animate(duration: Duration(milliseconds: 1200))
.build()
```
### Multi-Series Area Chart
Compare multiple categories:
```dart theme={null}
final platformData = [
{'month': 'Jan', 'users': 800, 'platform': 'Mobile'},
{'month': 'Feb', 'users': 900, 'platform': 'Mobile'},
{'month': 'Jan', 'users': 400, 'platform': 'Desktop'},
{'month': 'Feb', 'users': 450, 'platform': 'Desktop'},
];
CristalyseChart()
.data(platformData)
.mapping(x: 'month', y: 'users', color: 'platform')
.geomArea(strokeWidth: 1.5, alpha: 0.4)
.scaleXOrdinal()
.scaleYContinuous(min: 0)
.build()
```
### Combined Area + Line + Points
Layered visualization for rich data stories:
```dart theme={null}
CristalyseChart()
.data(analyticsData)
.mapping(x: 'date', y: 'value')
.geomArea(alpha: 0.2, strokeWidth: 0) // Background fill
.geomLine(strokeWidth: 3.0) // Trend line
.geomPoint(size: 6.0) // Data points
.scaleXContinuous()
.scaleYContinuous(min: 0)
.build()
```
## Dual Y-Axis
### Business Dashboard
Revenue vs conversion rate on independent scales:
```dart theme={null}
final businessData = [
{'month': 'Jan', 'revenue': 120, 'conversion_rate': 2.3},
{'month': 'Feb', 'revenue': 150, 'conversion_rate': 2.8},
{'month': 'Mar', 'revenue': 110, 'conversion_rate': 2.1},
{'month': 'Apr', 'revenue': 180, 'conversion_rate': 3.2},
];
CristalyseChart()
.data(businessData)
.mapping(x: 'month', y: 'revenue') // Primary Y-axis
.mappingY2('conversion_rate') // Secondary Y-axis
.geomBar(
yAxis: YAxis.primary, // Revenue bars (left scale)
alpha: 0.7,
)
.geomLine(
yAxis: YAxis.secondary, // Conversion line (right scale)
strokeWidth: 3.0,
color: Colors.orange,
)
.geomPoint(
yAxis: YAxis.secondary, // Points on conversion line
size: 8.0,
color: Colors.orange,
)
.scaleXOrdinal()
.scaleYContinuous(min: 0) // Left axis: Revenue
.scaleY2Continuous(min: 0, max: 5) // Right axis: Percentage
.theme(ChartTheme.defaultTheme())
.build()
```
### Sales Volume vs Customer Satisfaction
```dart theme={null}
CristalyseChart()
.data(salesData)
.mapping(x: 'week', y: 'sales_volume')
.mappingY2('satisfaction_score')
.geomBar(yAxis: YAxis.primary) // Volume bars
.geomLine(yAxis: YAxis.secondary) // Satisfaction trend
.scaleY2Continuous(min: 1, max: 5) // Rating scale
.build()
```
## Interactive Charts
### Tooltips and Click Handlers
Add rich interactivity to your charts:
```dart theme={null}
CristalyseChart()
.data(salesData)
.mapping(x: 'week', y: 'revenue', color: 'rep')
.geomPoint(size: 8.0)
.interaction(
tooltip: TooltipConfig(
builder: (point) {
final category = point.getDisplayValue('rep');
final value = point.getDisplayValue('revenue');
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.8),
borderRadius: BorderRadius.circular(4),
),
child: Text(
'$category: \$${value}k',
style: const TextStyle(color: Colors.white, fontSize: 12),
),
);
},
),
click: ClickConfig(
onTap: (point) {
print('Tapped on: ${point.data}');
// Show dialog, navigate, etc.
},
),
)
.build()
```
### Panning for Large Datasets
Interactive exploration of time series data:
```dart theme={null}
CristalyseChart()
.data(largeTimeSeriesData)
.mapping(x: 'timestamp', y: 'value', color: 'series')
.geomLine(strokeWidth: 2.0)
.geomPoint(size: 4.0)
.interaction(
pan: PanConfig(
enabled: true,
updateXDomain: true, // Allow horizontal panning
updateYDomain: false, // Disable vertical panning
onPanUpdate: (info) {
print('Visible range: ${info.visibleMinX} - ${info.visibleMaxX}');
// Load more data, update UI, etc.
},
),
)
.build()
```
## Theming Examples
### Dark Theme
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint()
.theme(ChartTheme.darkTheme())
.build()
```
### Custom Theme
```dart theme={null}
final customTheme = ChartTheme(
backgroundColor: Colors.grey[50]!,
primaryColor: Colors.deepPurple,
colorPalette: [Colors.blue, Colors.red, Colors.green],
gridColor: Colors.grey[300]!,
axisTextStyle: TextStyle(fontSize: 14, color: Colors.black87),
padding: EdgeInsets.all(40),
);
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint()
.theme(customTheme)
.build()
```
## Animation Examples
### Elastic Animation
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint()
.animate(
duration: Duration(milliseconds: 1200),
curve: Curves.elasticOut,
)
.build()
```
### Staggered Bar Animation
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'category', y: 'value')
.geomBar()
.animate(
duration: Duration(milliseconds: 1400),
curve: Curves.easeInOutCubic,
)
.build()
```
Ready to build your own charts? Start with the [Quick Start Guide](/quickstart) or explore specific [Chart Types](/charts/scatter-plots)!
# Animations
Source: https://docs.cristalyse.com/features/animations
Smooth 60fps transitions and progressive rendering
## Overview
Cristalyse leverages Flutter's animation engine to deliver buttery-smooth 60fps animations. Every chart type supports customizable animations with different curves, durations, and effects.
## Basic Animation
Add smooth entrance animations to any chart:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint()
.animate(
duration: Duration(milliseconds: 800),
curve: Curves.easeInOut,
)
.build()
```
## Animation Curves
Choose from Flutter's extensive curve library:
### Elastic Effects
Perfect for playful interfaces:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'category', y: 'value')
.geomBar()
.animate(
duration: Duration(milliseconds: 1200),
curve: Curves.elasticOut,
)
.build()
```
### Bounce Animation
Add character to your visualizations:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'x', y: 'y')
.geomPoint(size: 8.0)
.animate(
duration: Duration(milliseconds: 1000),
curve: Curves.bounceOut,
)
.build()
```
### Smooth Transitions
For professional dashboards:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'month', y: 'value')
.geomLine(strokeWidth: 3.0)
.animate(
duration: Duration(milliseconds: 1500),
curve: Curves.easeInOutCubic,
)
.build()
```
## Chart-Specific Animations
### Progressive Line Drawing
Lines animate from start to finish:
```dart theme={null}
CristalyseChart()
.data(timeSeriesData)
.mapping(x: 'date', y: 'price')
.geomLine(strokeWidth: 2.0)
.animate(duration: Duration(milliseconds: 2000))
.build()
```
### Staggered Bar Growth
Bars animate individually with delays:
```dart theme={null}
CristalyseChart()
.data(categoryData)
.mapping(x: 'category', y: 'value')
.geomBar()
.animate(
duration: Duration(milliseconds: 1400),
curve: Curves.easeInOutBack,
)
.build()
```
### Point Emergence
Scatter plot points appear progressively:
```dart theme={null}
CristalyseChart()
.data(scatterData)
.mapping(x: 'x', y: 'y', color: 'category')
.geomPoint(size: 6.0, alpha: 0.8)
.animate(
duration: Duration(milliseconds: 800),
curve: Curves.elasticOut,
)
.build()
```
### Area Fill Animation
Area charts fill progressively:
```dart theme={null}
CristalyseChart()
.data(volumeData)
.mapping(x: 'month', y: 'volume')
.geomArea(alpha: 0.3, strokeWidth: 2.0)
.animate(
duration: Duration(milliseconds: 1600),
curve: Curves.easeInOutCubic,
)
.build()
```
## Advanced Animation Techniques
### Layered Animations
Combine multiple geometries with different timings:
```dart theme={null}
CristalyseChart()
.data(data)
.mapping(x: 'week', y: 'engagement')
.geomArea(alpha: 0.2, strokeWidth: 0) // Background area
.geomLine(strokeWidth: 3.0) // Trend line
.geomPoint(size: 6.0) // Data points
.animate(
duration: Duration(milliseconds: 1800),
curve: Curves.easeInOutCubic,
)
.build()
```
### Stacked Bar Segments
Each segment animates individually:
```dart theme={null}
CristalyseChart()
.data(stackedData)
.mapping(x: 'quarter', y: 'revenue', color: 'category')
.geomBar(style: BarStyle.stacked)
.animate(
duration: Duration(milliseconds: 1600),
curve: Curves.easeInOutQuart,
)
.build()
```
### Dual Axis Coordination
Synchronize animations across different Y-axes:
```dart theme={null}
CristalyseChart()
.data(businessData)
.mapping(x: 'month', y: 'revenue')
.mappingY2('conversion_rate')
.geomBar(yAxis: YAxis.primary)
.geomLine(yAxis: YAxis.secondary, strokeWidth: 3.0)
.animate(
duration: Duration(milliseconds: 1400),
curve: Curves.easeInOutCubic,
)
.build()
```
## Animation Curves Reference
* `Curves.easeIn` - Slow start, fast finish
* `Curves.easeOut` - Fast start, slow finish
* `Curves.easeInOut` - Slow start and finish
* `Curves.easeInOutCubic` - Smooth S-curve
* `Curves.elasticIn` - Spring compression
* `Curves.elasticOut` - Spring release
* `Curves.bounceIn` - Bouncing entrance
* `Curves.bounceOut` - Bouncing exit
* `Curves.easeInBack` - Slight overshoot at start
* `Curves.easeOutBack` - Slight overshoot at end
* `Curves.easeInOutBack` - Overshoot both ends
* `Curves.fastOutSlowIn` - Material Design standard
* `Curves.slowMiddle` - Fast start/end, slow middle
* `Curves.decelerate` - Quick deceleration
## Performance Optimization
### Duration Guidelines
Choose appropriate durations for different chart types:
* **Scatter plots**: 600-1000ms
* **Line charts**: 1000-2000ms
* **Bar charts**: 800-1400ms
* **Area charts**: 1200-1800ms
* **Complex dual-axis**: 1400-2200ms
### Memory Efficiency
Cristalyse animations are optimized for:
* **GPU acceleration** - Leverages Flutter's rendering pipeline
* **Minimal redraws** - Only animates changing elements
* **Smooth 60fps** - Consistent frame rates across devices
* **Memory efficient** - Automatic cleanup after animation
## Interactive Animation
### Data Updates
Smooth transitions when data changes:
```dart theme={null}
class AnimatedDashboard extends StatefulWidget {
@override
_AnimatedDashboardState createState() => _AnimatedDashboardState();
}
class _AnimatedDashboardState extends State {
List