Features
A high-performance Flutter timeline/Gantt chart widget for displaying project schedules, milestones, and activities. Optimized for large datasets with:
- Lazy Rendering: Only renders visible items plus a configurable buffer
- Native Scroll Management: Uses Flutter's native ScrollController for all scroll operations
- Direct Calculations: Calculates scroll state using pure functions without abstraction layers
- Data Caching: Caches formatted data to avoid redundant calculations
- Scroll Throttling: Limits scroll calculations to ~60 FPS
- Conditional Rebuilds: Rebuilds only affected widgets on state changes
- Memory Efficient: Proper resource cleanup and disposal
- Standard Scrolling: Native Flutter scrolling with mouse wheel, trackpad, and touch gestures
Getting started
Add this package to your pubspec.yaml:
dependencies:
swiip_pubdev_timeline: ^latest_version
Then run:
flutter pub get
Usage
Basic Usage
import 'package:swiip_pubdev_timeline/timeline.dart';
Timeline(
colors: {
'primaryText': Colors.black,
'secondaryText': Colors.grey,
'accent1': Colors.blue,
// ... other color definitions
},
infos: {
'startDate': '2024-01-01',
'endDate': '2024-12-31',
'lmax': 8,
},
elements: [
{
'pre_id': 'task1',
'nat': 'task',
'date': '2024-01-15',
'status': 'pending',
// ... other element properties
},
],
elementsDone: [],
capacities: [],
stages: [
{
'prs_id': 'stage1',
'type': 'milestone',
'sdate': '2024-01-01',
'edate': '2024-01-31',
'name': 'Phase 1',
// ... other stage properties
},
],
openDayDetail: (day) {
// Handle day click
},
)
Performance Optimization
The timeline widget includes several performance optimizations that work automatically:
1. Scroll Architecture
The timeline uses a native-only scroll architecture that relies entirely on Flutter's built-in ScrollController:
Native ScrollControllers:
_controllerHorizontal: Manages horizontal scroll through timeline days_controllerVerticalStages: Manages vertical scroll through stage rows- Direct access to scroll position via
controller.offset
Direct State Management:
- Local state variables track scroll state (
_centerItemIndex,_visibleStart,_visibleEnd) - No custom controller abstraction layer
- Values passed explicitly to child widgets
Pure Calculation Functions (scroll_calculations.dart):
calculateCenterDateIndex(): Calculates which day is at the viewport centercalculateTargetVerticalOffset(): Calculates the vertical scroll position for a given dateshouldEnableAutoScroll(): Determines if auto-scroll should be enabled
These functions are pure (same inputs = same outputs) with no side effects, making them:
- Easy to test in isolation
- Predictable and maintainable
- Optimizable by the compiler
Throttled Updates:
- Timer-based throttling limits scroll calculations to ~60 FPS
- State updates only occur when values actually change
- Prevents excessive rebuilds during rapid scrolling
This architecture is simpler than using a custom controller class because:
- Fewer abstraction layers to understand
- Direct use of native Flutter APIs
- Transparent calculation logic
- Easier to debug and maintain
2. TimelineDataManager
Data formatting is cached automatically:
// The Timeline widget uses TimelineDataManager internally
// Formatted data is cached and only recomputed when input changes
// This significantly improves performance for large datasets
3. Lazy Rendering
Only visible items are rendered:
// The Timeline automatically calculates which items are visible
// and renders only those items plus a configurable buffer
// This keeps memory usage low even with hundreds of days
4. Standard Scrolling
The timeline uses native Flutter scrolling mechanisms:
// Horizontal scrolling works with:
// - Mouse wheel (with Shift key on some platforms)
// - Trackpad horizontal gestures
// - Touch drag gestures
// - Programmatic scrollTo() method
//
// Vertical scrolling works independently with the same gestures
External Configuration
The timeline now supports external configuration through a JSON file, allowing you to customize performance and rendering parameters without modifying code. This is especially useful for optimizing the timeline for different dataset sizes.
Quick Start with Configuration
-
Copy the template file:
cp timeline_config.template.json timeline_config.json -
Add to pubspec.yaml:
flutter: assets: - timeline_config.json -
Customize parameters:
{ "dayWidth": 50.0, "bufferDays": 10, } -
Restart your app to apply changes
Recommended Configurations
- Small datasets (< 100 days):
dayWidth: 70.0,bufferDays: 5 - Medium datasets (100-500 days): Default values -
dayWidth: 65.0,bufferDays: 8 - Large datasets (> 500 days):
dayWidth: 50.0,bufferDays: 10
Configuration Parameters
All parameters are optional. If not specified, sensible defaults are used:
| Parameter | Type | Range | Default | Description |
|---|---|---|---|---|
dayWidth |
double | 20.0 - 100.0 | 65.0 | Width of each day column |
dayMargin |
double | 0.0 - 20.0 | 5.0 | Spacing between days |
bufferDays |
int | 1 - 20 | 8 | Days rendered outside viewport |
animationDurationMs |
int | 100 - 500 | 250 | Animation duration (ms) |
rowHeight |
double | 20.0 - 60.0 | 30.0 | Height of each stage row |
rowMargin |
double | 0.0 - 10.0 | 3.0 | Spacing between rows |
datesHeight |
double | 40.0 - 100.0 | 65.0 | Height of date header |
For detailed configuration documentation, see CONFIGURATION.md.
Performance Tips
For optimal performance with large datasets:
-
Use recommended configurations: Start with the recommended values for your dataset size from the template file:
- Small datasets (< 100 days): Better visuals, lower buffer
- Medium datasets (100-500 days): Default values (balanced)
- Large datasets (> 500 days): Optimized for smoothness
-
Adjust buffer size: The
bufferDaysparameter has the biggest impact on memory usage:- Small datasets:
5days - Medium datasets:
8days (default) - Large datasets:
10-12days - ⚠️ Values > 12 can cause memory issues
- Small datasets:
-
Minimize data updates: The timeline caches formatted data. Avoid unnecessary updates to
elements,stages, orcapacitiesprops. -
Provide stable keys: When using the timeline in a list or with dynamic data, provide stable keys to help Flutter optimize rebuilds.
-
Monitor performance: Enable debug mode to see active configuration:
TimelineConfigurationManager.enableDebugMode();
Advanced Usage
Custom Colors
final customColors = {
'primaryText': Color(0xFF000000),
'secondaryText': Color(0xFF666666),
'accent1': Color(0xFF999999),
'accent2': Color(0xFFCCCCCC),
'background': Color(0xFFFFFFFF),
'border': Color(0xFFE0E0E0),
'success': Color(0xFF4CAF50),
'warning': Color(0xFFFFC107),
'error': Color(0xFFF44336),
// ... add more custom colors
};
Timeline(
colors: customColors,
// ... other props
)
Handling Interactions
Timeline(
openDayDetail: (Map<String, dynamic> day) {
// Handle day click
print('Clicked day: ${day['date']}');
// Show detail dialog, navigate, etc.
},
openEditStage: (Map<String, dynamic> stage) {
// Handle stage edit
print('Edit stage: ${stage['name']}');
},
openEditElement: (Map<String, dynamic> element) {
// Handle element edit
print('Edit element: ${element['pre_id']}');
},
// ... other props
)
Performance Benchmarks
On a typical device with 500 days and 100 stages:
- Initial render: < 500ms
- Scroll performance: 60 FPS maintained
- Memory usage: ~50MB (vs ~200MB without optimizations)
- Rebuild count: 90% reduction compared to unoptimized version
Troubleshooting
Issue: Timeline renders slowly with large datasets
Solution: Use the recommended configuration for large datasets in your configuration file, or adjust bufferDays parameters. See CONFIGURATION.md for detailed tuning guide.
Issue: Configuration not loading
Solution: Ensure the file is named timeline_config.json and is in the package root directory. Check console for validation errors. Enable debug mode to see active configuration.
Issue: Blank areas appear during fast scrolling
Solution: Increase bufferDays in your configuration (try 8-10 for large datasets). Note that higher values use more memory.
Issue: Memory usage is high
Solution: Reduce bufferDays to 5-8 and dayWidth to 50-60. Use the recommended configuration for your dataset size. Verify proper widget disposal.
For more troubleshooting help, see CONFIGURATION.md.
Migration Guide
Upgrading to External Configuration
If you're upgrading from a previous version, your existing code will continue to work without any changes. The external configuration system is completely optional and backward compatible.
No Changes Required
Your existing Timeline widgets will work exactly as before:
Timeline(
width: 800,
height: 600,
// ... your existing parameters
)
Gradual Adoption (Recommended)
-
Start with recommended values: Create
timeline_config.jsonwith recommended values for your dataset size:{ "dayWidth": 50.0, "bufferDays": 10, } -
Test and verify: Run your app and verify everything works as expected.
-
Fine-tune as needed: Add or adjust specific parameters to customize further:
{ "dayWidth": 50.0, "bufferDays": 12, }
Moving from Hardcoded Values
If you were using custom configuration values in your code, you can now move them to the configuration file:
Before:
// Custom values hardcoded in your app
final customDayWidth = 50.0;
final customBufferDays = 10;
// ... use these values
After:
{
"dayWidth": 50.0,
"bufferDays": 10
}
The configuration will be loaded automatically from timeline_config.json.
Benefits of Migration
- Easier tuning: Adjust performance without rebuilding your app
- Environment-specific configs: Different settings for development vs production
- Better performance: Use optimized configurations for your dataset size
- Cleaner code: Separate configuration from application logic
For detailed migration instructions and examples, see CONFIGURATION.md.
Additional Information
Documentation
- CONFIGURATION.md: Comprehensive guide to external configuration
- timeline_config.template.json: Template configuration file with detailed comments
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
License
See LICENSE file for details.
Support
For issues and feature requests, please use the GitHub issue tracker.