Documentation Index Fetch the complete documentation index at: https://docs.cristalyse.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Cristalyse provides rich interaction capabilities that make your charts engaging and informative. From simple tooltips to complex pan and zoom operations, every interaction is optimized for performance and accessibility. Support for pinch gestures, scroll wheel, and floating buttons brings intuitive zooming to all platforms.
Show contextual information when users hover over data points:
CristalyseChart ()
. data (data)
. mapping (x : 'month' , y : 'revenue' )
. geomPoint ()
. tooltip ( DefaultTooltips . simple ( 'revenue' ))
. build ()
Display multiple data fields in formatted tooltips:
CristalyseChart ()
. data (data)
. mapping (x : 'week' , y : 'sales' , color : 'region' )
. geomBar ()
. tooltip ( DefaultTooltips . multi ({
'week' : 'Week' ,
'sales' : 'Sales ($)' ,
'region' : 'Region' ,
'growth' : 'Growth Rate' ,
}))
. build ()
Create rich, branded tooltips with custom styling:
CristalyseChart ()
. data (data)
. mapping (x : 'date' , y : 'price' , color : 'symbol' )
. geomLine ()
. tooltip ((point) {
return Container (
padding : const EdgeInsets . all ( 12 ),
decoration : BoxDecoration (
color : Colors .black. withOpacity ( 0.9 ),
borderRadius : BorderRadius . circular ( 8 ),
border : Border . all (color : Colors .white24),
boxShadow : const [
BoxShadow (
color : Colors .black26,
blurRadius : 12.0 ,
offset : Offset ( 0 , 4 ),
),
],
),
child : Column (
mainAxisSize : MainAxisSize .min,
crossAxisAlignment : CrossAxisAlignment .start,
children : [
Text (
' ${ point . getDisplayValue ( 'symbol' )} ' ,
style : const TextStyle (
color : Colors .white,
fontWeight : FontWeight .bold,
fontSize : 14 ,
),
),
const SizedBox (height : 4 ),
Text (
'Price: \$ ${ point . getDisplayValue ( 'price' )} ' ,
style : const TextStyle (color : Colors .white),
),
Text (
'Date: ${ point . getDisplayValue ( 'date' )} ' ,
style : const TextStyle (color : Colors .white70, fontSize : 12 ),
),
],
),
);
})
. build ()
Fine-tune tooltip behavior and appearance:
CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomPoint ()
. interaction (
tooltip : TooltipConfig (
builder : DefaultTooltips . simple ( 'value' ),
showDelay : const Duration (milliseconds : 100 ),
hideDelay : const Duration (milliseconds : 500 ),
followPointer : true ,
backgroundColor : const Color ( 0xFF323232 ),
textColor : Colors .white,
borderRadius : 8.0 ,
padding : const EdgeInsets . all ( 12 ),
shadow : const BoxShadow (
color : Colors .black26,
blurRadius : 8.0 ,
offset : Offset ( 0 , 2 ),
),
),
)
. build ()
Hover Interactions
Basic Hover Detection
Respond to mouse hover events on data points:
CristalyseChart ()
. data (data)
. mapping (x : 'category' , y : 'value' )
. geomBar ()
. onHover ((point) {
if (point != null ) {
print ( 'Hovering over: ${ point . getDisplayValue ( 'category' )} ' );
} else {
print ( 'No longer hovering' );
}
})
. build ()
Advanced Hover Configuration
Control hover sensitivity and debouncing:
CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomPoint (size : 8.0 )
. interaction (
hover : HoverConfig (
onHover : (point) {
// Handle hover start
if (point != null ) {
setState (() {
hoveredPoint = point;
});
}
},
onExit : (point) {
// Handle hover end
setState (() {
hoveredPoint = null ;
});
},
hitTestRadius : 20.0 , // Generous hit area
debounce : const Duration (milliseconds : 50 ),
),
)
. build ()
Click Interactions
Simple Click Handlers
Handle tap events on data points:
CristalyseChart ()
. data (data)
. mapping (x : 'month' , y : 'revenue' )
. geomBar ()
. onClick ((point) {
Navigator . push (
context,
MaterialPageRoute (
builder : (context) => DetailPage (data : point.data),
),
);
})
. build ()
Multiple Click Types
Support different interaction patterns:
CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' , color : 'category' )
. geomPoint ()
. interaction (
click : ClickConfig (
onTap : (point) {
showBottomSheet (
context : context,
builder : (context) => DataPointDetails (point : point),
);
},
onDoubleTap : (point) {
showDialog (
context : context,
builder : (context) => AlertDialog (
title : Text ( 'Edit Data Point' ),
content : EditDataPointForm (point : point),
),
);
},
onLongPress : (point) {
Clipboard . setData ( ClipboardData (
text : 'Value: ${ point . getDisplayValue ( 'y' )} ' ,
));
ScaffoldMessenger . of (context). showSnackBar (
const SnackBar (content : Text ( 'Copied to clipboard' )),
);
},
hitTestRadius : 15.0 ,
),
)
. build ()
Pan Interactions
Basic Panning
Enable horizontal scrolling through large datasets:
CristalyseChart ()
. data (largeTimeSeriesData)
. mapping (x : 'timestamp' , y : 'value' )
. geomLine ()
. onPan ((info) {
print ( 'Visible range: ${ info . visibleMinX } to ${ info . visibleMaxX } ' );
// Update data source or trigger lazy loading
if (info.state == PanState .end) {
fetchDataForRange (info.visibleMinX, info.visibleMaxX);
}
})
. build ()
Advanced Pan Configuration
Full control over pan behavior:
CristalyseChart ()
. data (data)
. mapping (x : 'time' , y : 'metric' )
. geomLine ()
. geomPoint ()
. interaction (
pan : PanConfig (
enabled : true ,
updateXDomain : true , // Enable X-axis panning
updateYDomain : false , // Disable Y-axis panning
onPanStart : (info) {
setState (() {
isPanning = true ;
});
},
onPanUpdate : (info) {
setState (() {
visibleRange = ' ${ info . visibleMinX ?. toStringAsFixed ( 1 )} - ${ info . visibleMaxX ?. toStringAsFixed ( 1 )} ' ;
});
},
onPanEnd : (info) {
setState (() {
isPanning = false ;
});
// Fetch new data for visible range
loadDataForVisibleRange (info.visibleMinX, info.visibleMaxX);
},
throttle : const Duration (milliseconds : 16 ), // 60 FPS
),
)
. build ()
Programmatic Pan Control
Control chart panning programmatically with external UI controls:
class ProgrammaticPanChart extends StatefulWidget {
@override
_ProgrammaticPanChartState createState () => _ProgrammaticPanChartState ();
}
class _ProgrammaticPanChartState extends State < ProgrammaticPanChart > {
final panController = PanController ();
double visibleMinX = 500 ;
double visibleMaxX = 1500 ;
@override
void dispose () {
panController. dispose ();
super . dispose ();
}
@override
Widget build ( BuildContext context) {
return Column (
children : [
// External controls
Row (
children : [
ElevatedButton (
onPressed : () {
panController. panTo ( PanInfo (
visibleMinX : visibleMinX,
visibleMaxX : visibleMaxX,
state : PanState .update,
));
},
child : Text ( 'Pan to Range' ),
),
ElevatedButton (
onPressed : () => panController. panReset (),
child : Text ( 'Reset View' ),
),
],
),
// Chart with controller
Expanded (
child : CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomLine ()
. interaction (
pan : PanConfig (
enabled : true ,
controller : panController, // Connect controller
onPanUpdate : (info) {
setState (() {
visibleMinX = info.visibleMinX ?? visibleMinX;
visibleMaxX = info.visibleMaxX ?? visibleMaxX;
});
},
),
)
. build (),
),
],
);
}
}
Key Features:
panTo(): Jump to specific data range programmatically
panReset(): Return to original chart view
Works alongside gesture-based panning
Full lifecycle management with dispose()
Use Cases:
Zoom buttons for specific time ranges (“Last 7 days”, “Last month”)
Coordinated panning across multiple charts
Jump to bookmarked positions
Reset button after exploration
External slider controls for pan position
Zoom Interactions
Enable intuitive zooming with pinch gestures, scroll wheel, and UI buttons:
Basic Zoom Setup
Quick zoom configuration with one method:
CristalyseChart ()
. data (data)
. mapping (x : 'day' , y : 'revenue' )
. geomLine ()
. onZoom ((info) {
print ( 'Zoom scale: ${ info . scaleX } x' );
print ( 'Visible range: ${ info . visibleMinX } to ${ info . visibleMaxX } ' );
})
. build ()
Zoom Modes
Zoom can be configured for different axis combinations:
// X-axis only (default)
CristalyseChart ()
. data (data)
. mapping (x : 'time' , y : 'value' )
. geomLine ()
. onZoom (
(info) => print ( 'X zoom: ${ info . scaleX } x' ),
axis : ZoomAxis .x,
)
. build ()
// Y-axis only
CristalyseChart ()
. data (data)
. mapping (x : 'time' , y : 'value' )
. geomLine ()
. onZoom (
(info) => print ( 'Y zoom: ${ info . scaleY } x' ),
axis : ZoomAxis .y,
)
. build ()
// Both axes simultaneously
CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomPoint ()
. onZoom (
(info) => print ( 'Scale X: ${ info . scaleX } x, Y: ${ info . scaleY } x' ),
axis : ZoomAxis .both,
)
. build ()
Advanced Zoom Configuration
Full control over zoom behavior and appearance:
CristalyseChart ()
. data (data)
. mapping (x : 'timestamp' , y : 'metric' , color : 'series' )
. geomLine (strokeWidth : 2.0 )
. geomPoint ()
. interaction (
zoom : ZoomConfig (
enabled : true ,
axes : ZoomAxis .both, // Zoom X and Y axes
maxScale : 16.0 , // Maximum 16x zoom
minScale : 1.0 , // Minimum 1x (no zoom out past original)
wheelSensitivity : 0.0015 , // Scroll wheel sensitivity (0.0005-0.0035)
buttonStep : 1.4 , // Zoom 40% per button press
showButtons : true , // Show +/- floating buttons
buttonAlignment : Alignment .bottomRight, // Button position
buttonPadding : const EdgeInsets . all ( 20 ), // Space from chart edge
onZoomStart : (info) {
print ( 'Zoom started at ${ info . visibleMinX } ' );
},
onZoomUpdate : (info) {
print ( 'Zooming - Scale: ${ info . scaleX } x' );
// Update UI in real-time
setState (() {
currentZoomLevel = info.scaleX ?? 1.0 ;
visibleRange = ' ${ info . visibleMinX ?. toStringAsFixed ( 2 )} - ${ info . visibleMaxX ?. toStringAsFixed ( 2 )} ' ;
});
},
onZoomEnd : (info) {
print ( 'Zoom ended' );
// Fetch new data for visible range if needed
if (info.visibleMinX != null && info.visibleMaxX != null ) {
loadDataForRange (info.visibleMinX ! , info.visibleMaxX ! );
}
},
),
)
. build ()
Pinch Gesture (Touch):
Two-finger pinch to zoom in/out
Zoom centered at pinch focal point
Works with configured axes
Scroll Wheel (Desktop):
Scroll up to zoom in, scroll down to zoom out
Zoom centered at cursor position
Configurable sensitivity with wheelSensitivity
Precise zoom control for time-series and technical charts
Floating Buttons (All Platforms):
Optional +/- buttons for touch-friendly zoom
Configurable positioning with buttonAlignment
Adjustable zoom increment with buttonStep
Accessible for users with limited touch capability
Single Finger (Touch):
Single finger performs pan gesture
Works alongside zoom when multi-touch is detected
The ZoomInfo object provides comprehensive zoom state data:
ZoomInfo (
// Visible axis ranges in data coordinates
visibleMinX : 100.0 ,
visibleMaxX : 500.0 ,
visibleMinY : 10.0 ,
visibleMaxY : 90.0 ,
// Zoom scale factors (1.0 = no zoom, 4.0 = 4x zoom)
scaleX : 2.0 , // 2x zoom on X-axis
scaleY : 1.5 , // 1.5x zoom on Y-axis
// Current zoom lifecycle state
state : ZoomState .update, // start, update, or end
)
Use ZoomState to differentiate interaction phases:
ZoomState.start - User begins zoom gesture
ZoomState.update - Zoom in progress (fires continuously)
ZoomState.end - User completes zoom gesture
Combined Zoom & Pan
Zoom and pan work seamlessly together:
CristalyseChart ()
. data (timeSeriesData)
. mapping (x : 'timestamp' , y : 'value' , color : 'category' )
. geomLine (strokeWidth : 2.0 )
. geomPoint (size : 5.0 )
. interaction (
zoom : ZoomConfig (
enabled : true ,
axes : ZoomAxis .x, // Zoom on X-axis only (time dimension)
wheelSensitivity : 0.0015 ,
showButtons : true ,
onZoomUpdate : (info) {
setState (() {
zoomDisplay = 'Zoom: ${ info . scaleX ?. toStringAsFixed ( 1 )} x • '
'Range: ${ info . visibleMinX ?. toStringAsFixed ( 0 )} - ${ info . visibleMaxX ?. toStringAsFixed ( 0 )} ' ;
});
},
),
pan : PanConfig (
enabled : true ,
updateXDomain : true , // Pan X-axis
updateYDomain : false , // Keep Y-axis fixed
throttle : const Duration (milliseconds : 16 ),
onPanUpdate : (info) => print ( 'Panning to ${ info . visibleMinX } ' ),
),
tooltip : TooltipConfig (
builder : DefaultTooltips . multi ({
'timestamp' : 'Time' ,
'value' : 'Value' ,
'category' : 'Category' ,
}),
),
)
. build ()
Use Cases
Time-Series Exploration:
// Users can zoom into specific time periods
// then pan to explore surrounding data
CristalyseChart ()
. data (stockData)
. mapping (x : 'date' , y : 'price' )
. geomLine ()
. interaction (
zoom : ZoomConfig (
axes : ZoomAxis .x,
showButtons : true , // Easy zoom on mobile
),
pan : PanConfig (enabled : true , updateXDomain : true ),
)
. build ()
Scientific Visualizations:
// Independent X/Y zooming for scatter plots
CristalyseChart ()
. data (measurementData)
. mapping (x : 'temperature' , y : 'pressure' , color : 'material' )
. geomPoint (size : 8.0 )
. interaction (
zoom : ZoomConfig (
axes : ZoomAxis .both, // Zoom both axes independently
maxScale : 32.0 , // Allow deep zoom for detailed analysis
),
)
. build ()
Mobile Dashboards:
// Touch-friendly zoom buttons for accessibility
CristalyseChart ()
. data (mobileData)
. mapping (x : 'hour' , y : 'users' )
. geomBar ()
. interaction (
zoom : ZoomConfig (
axes : ZoomAxis .x,
showButtons : true ,
buttonAlignment : Alignment .topRight, // Accessible location
buttonStep : 1.5 , // Larger steps for mobile
),
)
. build ()
Pan with Data Loading
Implement infinite scrolling patterns:
class PanningChart extends StatefulWidget {
@override
_PanningChartState createState () => _PanningChartState ();
}
class _PanningChartState extends State < PanningChart > {
List < Map < String , dynamic >> visibleData = [];
double currentMinX = 0 ;
double currentMaxX = 100 ;
@override
void initState () {
super . initState ();
loadDataForRange (currentMinX, currentMaxX);
}
void loadDataForRange ( double ? minX, double ? maxX) async {
if (minX == null || maxX == null ) return ;
// Simulate API call
final newData = await fetchTimeSeriesData (minX, maxX);
setState (() {
visibleData = newData;
currentMinX = minX;
currentMaxX = maxX;
});
}
@override
Widget build ( BuildContext context) {
return CristalyseChart ()
. data (visibleData)
. mapping (x : 'timestamp' , y : 'value' , color : 'series' )
. geomLine (strokeWidth : 2.0 )
. scaleXContinuous (min : currentMinX, max : currentMaxX)
. interaction (
pan : PanConfig (
enabled : true ,
updateXDomain : true ,
onPanEnd : (info) => loadDataForRange (
info.visibleMinX,
info.visibleMaxX,
),
),
tooltip : TooltipConfig (
builder : DefaultTooltips . multi ({
'timestamp' : 'Time' ,
'value' : 'Value' ,
'series' : 'Series' ,
}),
),
)
. build ();
}
}
Combined Interactions
Rich Interactive Dashboard
Combine multiple interaction types for powerful user experience:
CristalyseChart ()
. data (data)
. mapping (x : 'date' , y : 'price' , color : 'symbol' , size : 'volume' )
. geomPoint (alpha : 0.8 )
. interaction (
tooltip : TooltipConfig (
builder : (point) => RichTooltip (
title : point. getDisplayValue ( 'symbol' ),
fields : {
'price' : 'Price' ,
'volume' : 'Volume' ,
'change' : 'Change %' ,
},
point : point,
),
showDelay : const Duration (milliseconds : 50 ),
hideDelay : const Duration (milliseconds : 300 ),
),
hover : HoverConfig (
onHover : (point) => highlightRelatedPoints (point),
onExit : (point) => clearHighlights (),
hitTestRadius : 12.0 ,
),
click : ClickConfig (
onTap : (point) => showStockDetails (point),
onDoubleTap : (point) => addToWatchlist (point),
),
pan : PanConfig (
enabled : true ,
updateXDomain : true ,
onPanUpdate : (info) => updateVisibleTimeRange (info),
),
)
. build ()
Large Dataset Interactions
Optimize interactions for thousands of data points:
CristalyseChart ()
. data (largeDataset) // 10,000+ points
. mapping (x : 'x' , y : 'y' , color : 'category' )
. geomPoint (size : 3.0 , alpha : 0.7 )
. interaction (
tooltip : TooltipConfig (
builder : DefaultTooltips . simple ( 'y' ),
showDelay : const Duration (milliseconds : 10 ), // Fast response
),
hover : HoverConfig (
hitTestRadius : 8.0 , // Smaller hit area for performance
debounce : const Duration (milliseconds : 16 ), // 60 FPS
),
pan : PanConfig (
enabled : true ,
throttle : const Duration (milliseconds : 32 ), // 30 FPS for panning
),
)
. build ()
Reuse tooltip widgets to reduce memory allocation:
class PerformantTooltips {
static final _tooltipPool = < Widget > [];
static Widget pooled ( DataPointInfo point) {
// Reuse existing tooltip widgets when possible
return Text ( 'Value: ${ point . getDisplayValue ( 'value' )} ' );
}
}
CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomPoint ()
. tooltip ( PerformantTooltips .pooled)
. build ()
Accessibility
Screen Reader Support
Ensure interactions work with assistive technologies:
CristalyseChart ()
. data (data)
. mapping (x : 'month' , y : 'revenue' )
. geomBar ()
. interaction (
tooltip : TooltipConfig (
builder : (point) => Semantics (
label : 'Revenue for ${ point . getDisplayValue ( 'month' )} : \$ ${ point . getDisplayValue ( 'revenue' )} k' ,
child : DefaultTooltips . simple ( 'revenue' )(point),
),
),
click : ClickConfig (
onTap : (point) {
// Announce selection to screen readers
SemanticsService . announce (
'Selected ${ point . getDisplayValue ( 'month' )} with revenue \$ ${ point . getDisplayValue ( 'revenue' )} k' ,
TextDirection .ltr,
);
},
),
)
. build ()
Keyboard Navigation
Support keyboard-only users:
class KeyboardNavigableChart extends StatefulWidget {
@override
_KeyboardNavigableChartState createState () => _KeyboardNavigableChartState ();
}
class _KeyboardNavigableChartState extends State < KeyboardNavigableChart > {
int ? selectedIndex;
@override
Widget build ( BuildContext context) {
return Focus (
onKey : (node, event) {
if (event is RawKeyDownEvent ) {
if (event.logicalKey == LogicalKeyboardKey .arrowRight) {
setState (() {
selectedIndex = (selectedIndex ?? - 1 ) + 1 ;
selectedIndex = selectedIndex ! . clamp ( 0 , data.length - 1 );
});
return KeyEventResult .handled;
}
// Handle other keys...
}
return KeyEventResult .ignored;
},
child : CristalyseChart ()
. data (data)
. mapping (x : 'x' , y : 'y' )
. geomPoint ()
. build (),
);
}
}
Best Practices
Use generous hit test radii (15-30px) for touch devices
Smaller hit areas (8-15px) for mouse-only interfaces
Consider overlapping points in dense visualizations
Test on actual devices for optimal sizing
Provide immediate visual feedback for all interactions
Use consistent interaction patterns across charts
Show loading states during data fetch operations
Implement undo/redo for destructive actions
Support keyboard navigation where appropriate
Provide alternative text for screen readers
Ensure sufficient color contrast in tooltips
Test with assistive technologies
Interaction Examples
Rich Tooltips Multi-column tooltips with custom styling and animations
Click Actions Navigation, details, and context menus triggered by clicks
Zoom Interactions Pinch, scroll wheel, and button-based zoom for all platforms
Pan Interactions Explore large datasets with smooth panning gestures
Hover Effects Visual highlights and data previews on mouse hover
Next Steps
Animations Combine interactions with smooth animations
Theming Style interaction elements to match your design
Performance Optimize interactions for large datasets
Export Export charts while preserving interaction data