Comprehensive guide on scales in Cristalyse
CristalyseChart() .data(data) .mapping(x: 'time', y: 'value') .scaleXContinuous(min: 0, max: 100) // X-axis .scaleYContinuous(min: 0, max: 200) // Y-axis .geomLine() .build()
CristalyseChart() .data(data) .mapping(x: 'category', y: 'value') .scaleXOrdinal() // X-axis for categories .geomBar() .build()
CristalyseChart() .data(data) .mapping(x: 'x', y: 'y', color: 'intensity') .geomPoint() .theme( ChartTheme.defaultTheme().copyWith( colorPalette: [Colors.blue, Colors.red], // Gradient ), ) .build()
CristalyseChart() .data(data) .mapping(x: 'x', y: 'y', size: 'value') .geomPoint() .build()
labels
NumberFormat
intl
import 'package:intl/intl.dart'; // Revenue chart with currency formatting CristalyseChart() .data(salesData) .mapping(x: 'quarter', y: 'revenue') .scaleYContinuous(labels: NumberFormat.simpleCurrency().format) // $1,234.56 .build() // Conversion rate chart with percentage formatting CristalyseChart() .data(conversionData) .mapping(x: 'month', y: 'rate') .scaleYContinuous(labels: NumberFormat.percentPattern().format) // 23% .build() // User growth chart with compact formatting CristalyseChart() .data(userGrowthData) .mapping(x: 'date', y: 'users') .scaleYContinuous(labels: NumberFormat.compact().format) // 1.2K, 1.5M .build()
// Create formatter once based on passed locale, reuse callback static String Function(num) createCurrencyFormatter({String locale = 'en_US'}) { final formatter = NumberFormat.simpleCurrency(locale: locale); // Created once return (num value) => formatter.format(value); // Reused callback } // Usage final currencyLabels = createCurrencyFormatter(); // uses default here, but could internationalize // Revenue chart with currency formatting CristalyseChart() .data(salesData) .mapping(x: 'quarter', y: 'revenue') .scaleYContinuous(labels: currencyLabels) // pass callback .build()
// Time duration formatting (seconds to human readable) static String Function(num) createDurationFormatter() { return (num seconds) { final roundedSeconds = seconds.round(); // Round to nearest second first if (roundedSeconds >= 3600) { final hours = roundedSeconds / 3600; if (hours == hours.round()) { return '${hours.round()}h'; // Clean: "1h", "2h", "24h" } return '${hours.toStringAsFixed(1)}h'; // Decimal: "1.5h", "2.3h" } else if (roundedSeconds >= 60) { final minutes = (roundedSeconds / 60).round(); return '${minutes}m'; // "1m", "30m", "59m" } return '${roundedSeconds}s'; // "5s", "30s", "59s" }; } final usageLabels = createDurationFormatter(); CristalyseChart() .data(usageData) .mapping(x: 'day', y: 'usage') .scaleYContinuous(labels: usageLabels) // pass callback .build()
// Implement chart-friendly integer/decimal distinction w/ NumberFormat static String Function(num) createChartCurrencyFormatter() { final formatter = NumberFormat.simpleCurrency(locale: 'en_US'); return (num value) { if (value == value.roundToDouble()) { return formatter.format(value).replaceAll('.00', ''); // $42 } return formatter.format(value); // $42.50 }; } final currencyLabels = createChartCurrencyFormatter(); CristalyseChart() .data(salesData) .mapping(x: 'quarter', y: 'revenue') .scaleYContinuous(labels: currencyLabels) .build()
// Handle negative values differently (P&L charts) static String Function(num) createProfitLossFormatter() { final formatter = NumberFormat.compact(); // Created once return (num value) { final abs = value.abs(); final formatted = formatter.format(abs); // Reuse formatter return value >= 0 ? formatted : '($formatted)'; }; } final currencyLabels = createProfitLossFormatter(); CristalyseChart() .data(salesData) .mapping(x: 'quarter', y: 'revenue') .scaleYContinuous(labels: currencyLabels) .build() // Custom units (basis points for finance - converts decimal rates to bp) static String Function(num) createBasisPointFormatter() { return (num value) => '${(value * 10000).toStringAsFixed(0)}bp'; } final bpLabels = createBasisPointFormatter(); CristalyseChart() .data(yieldData) // Data has decimal rates like 0.0025, 0.0150 .mapping(x: 'maturity', y: 'yield_rate') // yield_rate is decimal (0.0025 = 25bp) .scaleYContinuous(labels: bpLabels) // Converts 0.0025 → "25bp" .build()
CristalyseChart() .data(data) .mapping(x: 'day', y: 'hours') .scaleXOrdinal() .scaleYContinuous(min: 0, max: 24, labels: (v) => '${v}h') // Custom hour labels .build()
CristalyseChart() .data(dualAxisData) .mapping(x: 'month', y: 'revenue') .mappingY2('conversion_rate') .scaleYContinuous(min: 0, max: 100) .scaleY2Continuous(min: 0, max: 1) .geomBar(yAxis: YAxis.primary) .geomLine(yAxis: YAxis.secondary) .build()
CristalyseChart() .data(dynamicData) .mapping(x: 'date', y: 'value') .scaleXContinuous(min: startDate, max: endDate) .scaleYContinuous(min: minValue, max: maxValue) .geomArea() .build()
scale.dart