riverpod_timeline 1.0.0
riverpod_timeline: ^1.0.0 copied to clipboard
A Riverpod debugging and state-inspection package that records every provider update, tracks dependencies, visualizes state changes, and supports time-travel debugging.
riverpod_timeline #
A production-grade Riverpod debugging and state-inspection package. Records every provider update, tracks dependency relationships, visualizes state changes, and supports time-travel debugging — all with zero changes to your application logic.
| Feature | Description |
|---|---|
| Provider Observer | Automatic tracking of creation, disposal, updates, refreshes, and errors |
| Timeline Recording | Every state change recorded with timestamps, durations, and optional stack traces |
| Timeline Viewer | Built-in Flutter widget with search, filter by provider/event type, expand/collapse, and copy-to-clipboard |
| Dependency Graph | Visualize parent/child provider relationships and detect circular dependencies |
| Time Travel Debugging | Play, pause, resume, step forward/backward through recorded state transitions |
| Performance Metrics | Track rebuild counts, update frequency, average duration, and identify slow providers |
| Export & Import | Full session export/import as JSON for sharing, offline analysis, or replay |
| Developer Overlay | Draggable floating debug button for quick access during development |
| Production Safety | Automatically disabled in release builds; explicit opt-in available |
Supported Versions #
- Flutter
>=3.22.0 - Riverpod
^3.0.0 - Dart
^3.12.1
Supports all Riverpod provider types: Provider, NotifierProvider, AsyncNotifierProvider, FutureProvider, StreamProvider, StreamNotifierProvider.
Installation #
dependencies:
flutter_riverpod: ^3.0.0
riverpod_timeline: ^1.0.0
Quick Start #
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_timeline/riverpod_timeline.dart';
final _navigatorKey = GlobalKey<NavigatorState>();
void main() {
// 1. Initialize the timeline engine (call before runApp)
RiverpodTimeline.initialize(
maxEvents: 10000,
enablePerformanceTracking: true,
enableDependencyTracking: true,
enableLogging: true, // prints events to the debug console
);
runApp(
// 2. RiverpodTimelineScope wraps ProviderScope + auto-adds the observer
RiverpodTimelineScope(
navigatorKey: _navigatorKey,
child: MaterialApp(
navigatorKey: _navigatorKey,
home: MyApp(),
),
),
);
}
Once initialized, the timeline automatically records all provider activity. Open the built-in viewer at any time:
final tl = RiverpodTimeline.instance;
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ProviderTimelineView(controller: tl.controller),
),
);
Screenshots #
| Timeline Viewer | Dependency Graph |
|---|---|
| [Timeline Viewer] | [Dependency Graph] |
| Search, filter, expand/collapse events | Visualize provider relationships |
| Performance Dashboard | Time Travel |
|---|---|
| [Performance Dashboard] | [Time Travel] |
| Rebuild counts, durations, slowest providers | Step through recorded state transitions |
| Export / Import |
|---|
| [Export / Import] |
| Full session JSON export and import |
Run the example app to try all components:
cd example && flutter run
Configuration #
RiverpodTimeline.initialize(
/// Maximum number of timeline events kept in memory.
/// Oldest events are pruned when this limit is exceeded.
maxEvents: 10000,
/// Track per-provider rebuild counts, update frequency, and durations.
enablePerformanceTracking: true,
/// Record parent/child dependency relationships between providers.
enableDependencyTracking: true,
/// If true, the timeline remains active even in release builds.
/// Defaults to false — timeline is automatically disabled in release mode.
enableReleaseMode: false,
/// Capture stack traces with each event (can be expensive; disable in production).
captureStackTraces: false,
/// Print provider events to the debug console via [debugPrint].
/// Useful for following provider activity without the UI.
enableLogging: false,
);
Architecture #
The package is organized into independent modules around a central RiverpodTimeline singleton:
lib/
├── riverpod_timeline.dart # Barrel export — import this one file
└── src/
├── config.dart # RiverpodTimelineConfig
├── riverpod_timeline_api.dart # RiverpodTimeline singleton (main entry point)
├── models/ # Data models used throughout the package
│ ├── event_type.dart # Enum: created, updated, disposed, refreshed, error
│ ├── timeline_event.dart # Single recorded event with value snapshots
│ ├── provider_info.dart # Metadata about a tracked provider
│ ├── dependency_node.dart # Dependency graph node + graph algorithms
│ └── performance_metrics.dart # Per-provider performance statistics
├── observer/
│ └── timeline_observer.dart # ProviderObserver implementation
├── storage/
│ └── timeline_storage.dart # In-memory ring buffer with pruning
├── timeline/
│ └── timeline_controller.dart # Query, filter, and search interface
├── graph/
│ └── dependency_graph.dart # DependencyGraphController
├── replay/
│ └── time_travel_controller.dart # Play/pause/step replay engine
├── metrics/
│ └── metrics_collector.dart # Performance data collector
├── widgets/ # Flutter UI widgets
│ ├── timeline_viewer.dart # ProviderTimelineView
│ ├── dependency_graph_widget.dart # ProviderDependencyGraph
│ ├── performance_dashboard.dart # RiverpodPerformanceDashboard
│ └── event_detail_widget.dart # Expandable event detail card
├── overlay/
│ └── debug_overlay.dart # RiverpodTimelineOverlay + floating button
└── export/
└── timeline_exporter.dart # JSON export/import
Data Flow #
ProviderContainer ──> TimelineObserver ──> TimelineStorage (in-memory)
│ │
DependencyGraph TimelineController
Controller │
│ Widgets / UI
MetricsCollector │
│ TimeTravelController
PerformanceSummary (replay engine)
Full API Reference #
RiverpodTimeline (singleton) #
| Method | Description |
|---|---|
RiverpodTimeline.initialize(...) |
Initialize the engine. Must be called before runApp. |
RiverpodTimeline.instance |
Access the initialized singleton. |
RiverpodTimeline.reset() |
Clear the singleton (useful in tests). |
tl.observer |
The TimelineObserver to pass to ProviderScope. |
tl.controller |
TimelineController for querying and filtering events. |
tl.graphController |
DependencyGraphController for dependency tracking. |
tl.timeTravelController |
TimeTravelController for replay controls. |
tl.metricsCollector |
MetricsCollector for performance data. |
Time Travel Debugging #
final tl = RiverpodTimeline.instance;
tl.replay(); // Start automatic playback of all recorded events
tl.pause(); // Pause playback (maintains position)
tl.resumeReplay(); // Resume from paused position
tl.stepForward(); // Advance to the next event
tl.stepBackward(); // Go back to the previous event
The TimeTravelController also exposes lower-level control:
final ttc = tl.timeTravelController;
ttc.goToEvent(5); // Jump to a specific event index
ttc.goToStart(); // Jump to the first event
ttc.goToEnd(); // Jump to the last event
ttc.replaySpeedMs = 200; // Adjust playback speed (50-5000ms between events)
ttc.state // ReplayState.playing, .paused, .stopped
ttc.currentIndex // Current position in the event list
Export & Import #
// Export as Map
final Map<String, dynamic> data = tl.exportJson();
// Export as formatted JSON string
final String jsonString = tl.exportJsonString();
// Import from Map
tl.importJson(data);
// Import from JSON string
tl.importJsonString(jsonString);
The export includes: all timeline events, provider metadata, dependency graph, performance metrics, and configuration.
Widgets #
| Widget | Controller | Description |
|---|---|---|
ProviderTimelineView(controller:) |
TimelineController |
Full timeline browser with search, filters, expand/collapse |
ProviderDependencyGraph(controller:) |
DependencyGraphController |
Provider relationship viewer with cycle detection |
RiverpodPerformanceDashboard(collector:) |
MetricsCollector |
Performance stats, slowest/most-rebuilt providers |
Developer Overlay #
// Enable — adds a draggable floating debug button
RiverpodTimelineOverlay.enable(
timelineController: tl.controller,
graphController: tl.graphController,
metricsCollector: tl.metricsCollector,
context: context, // Optional, needed if not calling from a widget
);
// Disable — removes the floating button
RiverpodTimelineOverlay.disable();
Tapping the floating button opens a full-screen debug panel with tabbed navigation across Timeline, Dependency Graph, and Performance Metrics.
Production Safety #
The timeline automatically disables itself in release builds using Dart's assert() mechanism. This means zero overhead in production apps with no code changes.
// Timeline silently becomes a no-op in release mode:
// - events are not recorded
// - memory is not allocated
// - observers are still registered but no-op
// To force-enable in release mode (e.g., internal dogfooding builds):
RiverpodTimeline.initialize(
enableReleaseMode: true, // override the release-mode check
// ...
);
Memory Management #
- Configurable
maxEvents(default: 10,000) prevents unbounded memory growth - Oldest events are automatically pruned when the limit is reached
- All data is stored in-memory; no disk I/O during recording
- Export is only performed on-demand
Example App #
A complete example app is in the example/ directory. It demonstrates:
- Multiple provider types (
Provider,NotifierProvider,FutureProvider) - Opening the timeline viewer, dependency graph, and performance dashboard
- Exporting the timeline as JSON
- Using the developer overlay
Run it with:
cd example
flutter run
Testing #
# Run all tests
flutter test
# Run with coverage (requires lcov)
flutter test --coverage
The test suite covers:
- All model serialization round-trips
- Storage engine (add, query, prune, export/import)
- Dependency graph algorithms (cycle detection, add/remove)
- Time-travel controller (step, play/pause/resume, listeners)
- Metrics collector (record, aggregate, slowest/most-rebuilt)
- Export/import full round-trips
- Singleton lifecycle safety
Contributing #
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines #
- Maintain 90%+ test coverage
- Follow existing code style (see
analysis_options.yaml) - Add comments for non-obvious logic
- No new third-party dependencies without discussion
- Ensure zero analyzer warnings
License #
MIT License — see LICENSE
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.