flutter_roadmap_viewer 0.1.8
flutter_roadmap_viewer: ^0.1.8 copied to clipboard
A highly customizable roadmap and skill tree visualization widget with interactive pan/zoom, multiple layout orientations, and progress tracking.
Flutter Roadmap Viewer #
A highly customizable roadmap and skill tree visualization widget for Flutter with interactive pan/zoom, multiple layout orientations, and progress tracking.
Features #
- Simple API - Create roadmaps with just a list of tuples or JSON data
- Hierarchical Layout - Sugiyama-style layered graph drawing with crossing reduction
- Automatic Graph Building - Just provide skill data, relationships are computed automatically
- Progress Tracking - Four status types: Not Started, Current, Complete, Skipped
- Multiple Orientations - Horizontal (traditional) or vertical layouts
- Edge Styles - Curved bezier or orthogonal (right-angle) connections
- Multiple Node Types - Rectangular, capsule, or circular node widgets
- Custom Themes - Pre-built themes (light, dark, vibrant) or create your own
- Custom Node Builders - Full control over node rendering
- Zoom Controls - Optional zoom in/out and fit-to-screen buttons
- Progress Utilities - Calculate completion percentage, find ready skills, etc.
- Transitive Reduction - Automatically removes redundant prerequisite edges
Installation #
Add to your pubspec.yaml:
dependencies:
flutter_roadmap_viewer: ^0.1.8
Then run:
flutter pub get
Quick Start #
Simplest Usage #
import 'package:flutter_roadmap_viewer/flutter_roadmap_viewer.dart';
// Just provide (id, name, prerequisites) tuples
RoadmapViewer.simple([
('basics', 'Learn Basics', []),
('widgets', 'Master Widgets', ['basics']),
('state', 'State Management', ['basics']),
('advanced', 'Advanced Topics', ['widgets', 'state']),
])
From JSON #
// Perfect for API responses
RoadmapViewer.fromJson([
{'id': 'basics', 'name': 'Learn Basics', 'prerequisiteIds': []},
{'id': 'widgets', 'name': 'Master Widgets', 'prerequisiteIds': ['basics']},
])
With SkillData Objects #
final skillData = [
SkillData.simple('basics', 'Flutter Basics', [],
progressStatus: ProgressStatus.complete),
SkillData.simple('widgets', 'Widgets', ['basics'],
progressStatus: ProgressStatus.current),
SkillData.simple('state', 'State Management', ['basics']),
];
RoadmapViewer(
skillData: skillData,
onNodeTap: (node) => print('Tapped: ${node.name}'),
)
Usage Examples #
With Theme #
RoadmapViewer(
skillData: mySkillData,
theme: RoadmapThemeData.dark(),
// Or create custom:
// theme: RoadmapThemeData(
// completeColor: Colors.teal,
// currentColor: Colors.amber,
// ),
)
With Zoom Controls #
RoadmapViewer(
skillData: mySkillData,
showZoomControls: true,
showFitButton: true,
minScale: 0.2,
maxScale: 4.0,
)
With Custom Node Builder #
RoadmapViewer(
skillData: mySkillData,
nodeBuilder: (context, node, config) {
// Return custom widget or null for default
return Container(
width: config.width,
height: config.height,
decoration: config.getDefaultDecoration(),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.star, color: config.borderColor),
Text(node.name),
if (config.isComplete)
Icon(Icons.check, color: Colors.green, size: 16),
],
),
);
},
)
With Vertical Orientation #
RoadmapViewer(
skillData: mySkillData,
orientation: RoadmapOrientation.vertical,
edgeStyle: EdgeStyle.curved,
)
With Node Type Mapping #
RoadmapViewer(
skillData: mySkillData,
nodeTypeToWidgetType: const {
'Course': NodeWidgetType.rectangular,
'Skill': NodeWidgetType.capsule,
'Achievement': NodeWidgetType.circular,
},
)
Without Completion Node #
RoadmapViewer(
skillData: mySkillData,
showCompletionNode: false,
)
Progress Calculation #
final skills = mySkillData;
// Get completion percentage (0.0 - 1.0)
final completion = ProgressCalculator.calculateCompletion(skills);
print('${(completion * 100).round()}% complete');
// Get completion as integer percentage
final percent = ProgressCalculator.calculateCompletionPercent(skills);
print('$percent% complete');
// Get counts by status
final counts = ProgressCalculator.getStatusCounts(skills);
print('Complete: ${counts[ProgressStatus.complete]}');
print('In Progress: ${counts[ProgressStatus.current]}');
// Get skills ready to start (prerequisites complete)
final ready = ProgressCalculator.getReadySkills(skills);
print('Ready to start: ${ready.map((s) => s.name).join(', ')}');
// Get the critical path (longest dependency chain)
final criticalPath = ProgressCalculator.getCriticalPath(skills);
print('Critical path: ${criticalPath.length} skills');
// Get formatted summary
final summary = ProgressCalculator.getProgressSummary(skills);
print(summary); // "10/25 complete (40%)"
API Reference #
RoadmapViewer #
Main widget for displaying roadmaps.
Constructors:
| Constructor | Description |
|---|---|
RoadmapViewer() |
Standard constructor with SkillData list |
RoadmapViewer.simple() |
Quick setup with (id, name, prerequisites) tuples |
RoadmapViewer.fromJson() |
Create from JSON list |
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
skillData |
List<SkillData> |
required | List of skills to display |
nodeTypeToWidgetType |
Map<String, NodeWidgetType>? |
null |
Maps node types to widget types |
edgeStyle |
EdgeStyle |
orthogonal |
Edge connection style |
orientation |
RoadmapOrientation |
horizontal |
Layout direction |
horizontalSpacing |
double |
200.0 |
Space between nodes horizontally |
verticalSpacing |
double |
100.0 |
Space between nodes vertically |
showCompletionNode |
bool |
true |
Show completion node at end |
theme |
RoadmapThemeData? |
null |
Theme configuration |
nodeBuilder |
NodeBuilder? |
null |
Custom node widget builder |
showZoomControls |
bool |
false |
Show +/- zoom buttons |
showFitButton |
bool |
false |
Show fit-to-screen button |
initialFit |
bool |
false |
Auto-fit on initial load |
minScale |
double |
0.1 |
Minimum zoom scale |
maxScale |
double |
5.0 |
Maximum zoom scale |
onNodeTap |
Function(RoadmapNode)? |
null |
Node tap callback |
onNodeLongPress |
Function(RoadmapNode)? |
null |
Node long-press callback |
SkillData #
Simple data model for creating roadmap items.
Constructors:
// Full constructor
SkillData(
id: 'skill-1',
name: 'Skill Name',
type: 'Skill',
prerequisiteIds: ['prereq-1'],
progressStatus: ProgressStatus.notStarted,
metadata: {'key': 'value'},
)
// Simple factory
SkillData.simple('skill-1', 'Skill Name', ['prereq-1'])
// From JSON
SkillData.fromJson({'id': '...', 'name': '...', ...})
// List from JSON
SkillData.listFromJson(jsonList)
RoadmapThemeData #
Theme configuration for colors and styling.
Pre-built themes:
RoadmapThemeData() // Default pastel theme
RoadmapThemeData.light() // Bright, cheerful colors
RoadmapThemeData.dark() // Muted, sophisticated colors
RoadmapThemeData.vibrant() // Bold, saturated colors
Custom theme:
RoadmapThemeData(
notStartedColor: Color(0xFFE8E8E8),
currentColor: Color(0xFF2196F3),
completeColor: Color(0xFF4CAF50),
skippedColor: Color(0xFFFF9800),
completionNodeColor: Color(0xFFFFD700),
edgeColor: Color(0xFF9E9E9E), // null = use prerequisite status colors
edgeWidth: 2.0,
nodeBorderWidth: 2.0,
showCompletionOverlay: true,
useGradients: false,
showShadows: false,
)
NodeBuildContext #
Context provided to custom node builders.
Properties:
| Property | Type | Description |
|---|---|---|
node |
RoadmapNode |
The node being rendered |
width |
double |
Allocated width |
height |
double |
Allocated height |
color |
Color |
Base color for status |
borderColor |
Color |
Border color |
gradient |
Gradient? |
Optional gradient |
shadows |
List<BoxShadow>? |
Optional shadows |
overlay |
Widget? |
Optional overlay (hatch pattern) |
isComplete |
bool |
Convenience getter |
isCurrent |
bool |
Convenience getter |
isNotStarted |
bool |
Convenience getter |
getDefaultDecoration() |
BoxDecoration |
Default styling |
ProgressStatus #
enum ProgressStatus {
notStarted, // Not yet started
current, // Currently in progress
complete, // Completed
skipped, // Skipped/not applicable
}
EdgeStyle #
enum EdgeStyle {
orthogonal, // Right-angle connections
curved, // Smooth bezier curves
}
NodeWidgetType #
enum NodeWidgetType {
rectangular, // Rounded rectangle (120x60)
capsule, // Pill-shaped (120x60)
circular, // Circle (60x60)
}
Data Validation #
final errors = GraphBuilder.validateSkills(skillData);
if (errors.isNotEmpty) {
print('Validation errors: $errors');
}
Checks for:
- Duplicate IDs
- Empty IDs
- Self-references
- Broken prerequisite references
Best Practices #
Large Roadmaps #
For roadmaps with 50+ nodes:
- Increase spacing values
- Enable zoom controls
- Consider using vertical orientation for deep hierarchies
Performance #
- Use
constconstructors forSkillDatawhen possible - The widget efficiently handles layout recalculation on data changes
Custom Themes #
// Extend a preset
final myTheme = RoadmapThemeData.dark().copyWith(
completeColor: Colors.teal,
showCompletionOverlay: false,
);
Examples #
See the example/ directory for a complete demo app.
License #
MIT License - see LICENSE file for details.
Changelog #
See CHANGELOG.md for version history.