Rive Animation Manager
A comprehensive Flutter package for managing Rive animations with data binding, image replacement, and global state management capabilities.
Features
- Global Animation Controller: Centralized singleton for managing all Rive animations across your app
- State Machine Management: Handle inputs (triggers, booleans, numbers) and state transitions
- Data Binding Support: Full support for ViewModels with automatic property discovery
- Flexible Color Support: 8 color formats with automatic detection (hex, RGB, Maps, Lists, named colors)
- Image Replacement: Dynamically update images from assets, URLs, or raw bytes
- Image Caching: Preload and cache images for instant switching without decode overhead
- Text Run Management: Update and retrieve text values from animations
- Input Callbacks: Real-time callbacks for input changes, triggers, and hover actions
- Event Handling: Listen to Rive events with state context
- Property Caching: Optimized nested property path caching for performance
- Comprehensive Logging: Debug logging with configurable log manager
Installation
Add to your pubspec.yaml:
dependencies:
rive_animation_manager: ^1.0.12
Then run:
flutter pub get
Quick Start
Basic Usage
import 'package:rive_animation_manager/rive_animation_manager.dart';
RiveManager(
animationId: 'myAnimation',
riveFilePath: 'assets/animations/my_animation.riv',
animationType: RiveAnimationType.stateMachine,
onInit: (artboard) {
print('Animation loaded: $artboard');
},
onInputChange: (index, name, value) {
print('Input changed: $name = $value');
},
)
Controlling Animations
Use the global controller to manipulate animations:
RiveAnimationController controller = RiveAnimationController.instance;
// Update boolean input
controller.updateBool('myAnimation', 'isHovered', true);
// Update number input
controller.updateNumber('myAnimation', 'scrollPosition', 0.5);
// Trigger an input
controller.triggerInput('myAnimation', 'playAnimation');
// Update text
controller.setTextRunValue('myAnimation', 'myText', 'Hello World');
// Get current values
final value = controller.getDataBindingPropertyValue('myAnimation', 'propertyName');
Color Property Updates (v1.0.11+)
Update colors with 8 different formats - all automatically detected!
final controller = RiveAnimationController.instance;
// Hex format
await controller.updateDataBindingProperty('myAnimation', 'color', '#3EC293');
// RGB/RGBA strings
await controller.updateDataBindingProperty('myAnimation', 'color', 'rgb(62, 194, 147)');
await controller.updateDataBindingProperty('myAnimation', 'color', 'rgba(62, 194, 147, 0.8)');
// Flutter Color objects
await controller.updateDataBindingProperty('myAnimation', 'color', Colors.teal);
await controller.updateDataBindingProperty('myAnimation', 'color', Color(0xFF3EC293));
// Maps (standard 0-255)
await controller.updateDataBindingProperty('myAnimation', 'color', {'r': 62, 'g': 194, 'b': 147});
// Maps (Rive normalized 0.0-1.0) - Auto-detected!
await controller.updateDataBindingProperty('myAnimation', 'color', {'r': 0.2431, 'g': 0.7608, 'b': 0.5764});
// Lists (standard 0-255)
await controller.updateDataBindingProperty('myAnimation', 'color', [62, 194, 147]);
// Lists (Rive normalized 0.0-1.0) - Auto-detected!
await controller.updateDataBindingProperty('myAnimation', 'color', [0.2431, 0.7608, 0.5764]);
// Named colors
await controller.updateDataBindingProperty('myAnimation', 'color', 'teal');
Data Binding
Discover and update data binding properties:
RiveManager(
animationId: 'myAnimation',
riveFilePath: 'assets/animations/my_animation.riv',
onViewModelPropertiesDiscovered: (properties) {
for (var prop in properties) {
print('Property: ${prop['name']} (${prop['type']})');
}
},
onDataBindingChange: (propertyName, propertyType, value) {
print('Property changed: $propertyName = $value');
},
)
Update data binding properties:
final controller = RiveAnimationController.instance;
// Update various property types
await controller.updateDataBindingProperty('myAnimation', 'text', 'New Text');
await controller.updateDataBindingProperty('myAnimation', 'count', 42);
await controller.updateDataBindingProperty('myAnimation', 'isVisible', true);
await controller.updateDataBindingProperty('myAnimation', 'color', Color(0xFF00FF00));
Image Replacement
Enable image replacement for dynamic image updates:
RiveManager(
animationId: 'myAnimation',
riveFilePath: 'assets/animations/my_animation.riv',
enableImageReplacement: true,
)
Update images programmatically:
final controller = RiveAnimationController.instance;
final state = controller.getAnimationState('myAnimation');
if (state != null) {
// Update from asset
await state.updateImageFromAsset('assets/images/new_image.png');
// Update from URL
await state.updateImageFromUrl('https://example.com/image.png');
// Update from bytes
await state.updateImageFromBytes(imageBytes);
// Update from pre-decoded RenderImage (fastest)
state.updateImageFromRenderedImage(renderImage);
}
Image Caching
Preload images for instant switching:
final controller = RiveAnimationController.instance;
await controller.preloadImagesForAnimation(
'myAnimation',
[
'https://example.com/image1.png',
'https://example.com/image2.png',
'https://example.com/image3.png',
],
Factory.rive,
);
// Switch to cached image instantly
controller.updateImageFromCache('myAnimation', 0);
Input Handling
Handle different input types:
RiveManager(
animationId: 'myAnimation',
riveFilePath: 'assets/animations/my_animation.riv',
onInputChange: (index, inputName, value) {
print('Input changed: $inputName = $value');
},
onTriggerAction: (triggerName, value) {
print('Trigger fired: $triggerName');
},
onHoverAction: (hoverName, value) {
print('Hover state: $hoverName = $value');
},
)
Event Handling
Listen to Rive events:
RiveManager(
animationId: 'myAnimation',
riveFilePath: 'assets/animations/my_animation.riv',
onEventChange: (eventName, event, currentState) {
print('Event: $eventName in state: $currentState');
},
)
Advanced Features
Nested Property Updates
Update nested properties using path notation:
final controller = RiveAnimationController.instance;
// Using '/' separator
await controller.updateNestedProperty(
'myAnimation',
'parent/child',
'newValue',
);
// Using '.' separator
await controller.updateNestedProperty(
'myAnimation',
'parent.child',
'newValue',
);
Cache Statistics
Monitor animation manager cache usage:
final controller = RiveAnimationController.instance;
final stats = controller.getCacheStats();
print('Active animations: ${stats['animations']}');
print('Cached images: ${stats['totalCachedImages']}');
print('Cached property paths: ${stats['totalCachedPropertyPaths']}');
Logging - Corrected Section for QUICK_REFERENCE.md
Logging (Corrected)
Add Logs
import 'package:rive_animation_manager/rive_animation_manager.dart';
// Add a single log
LogManager.addLog('Animation loaded successfully');
// Add a log with error flag
LogManager.addLog('Failed to load animation', isExpected: false);
// Add multiple logs at once
LogManager.addMultipleLogs([
'Log 1',
'Log 2',
'Log 3',
]);
Retrieve Logs
// Get all logs as strings
List<String> allLogs = LogManager.logs;
// Get last N logs
final recentLogs = LogManager.getLastLogsAsStrings(10);
for (var log in recentLogs) {
print(log);
}
// Get log counts
int totalLogs = LogManager.logCount;
int errorLogs = LogManager.errorCount;
int infoLogs = LogManager.infoCount;
Filter & Search Logs
// Get only error logs
List<Map<String, dynamic>> errors = LogManager.getLogsByType(false);
// Get only info logs
List<Map<String, dynamic>> infos = LogManager.getLogsByType(true);
// Search logs by keyword
List<Map<String, dynamic>> results = LogManager.searchLogs('animation');
for (var log in results) {
print('${log['timestamp']}: ${log['message']}');
}
Export Logs
// Export all logs as formatted string
String formatted = LogManager.exportAsString();
print(formatted);
// Export all logs as JSON
String json = LogManager.exportAsJSON();
print(json);
Clear Logs
// Clear all logs
LogManager.clearLogs();
Reactive UI Updates
// Listen to log changes
LogManager.logMessages.addListener(() {
print('Logs updated!');
});
// Use in ValueListenableBuilder for real-time UI updates
ValueListenableBuilder<List<Map<String, dynamic>>>(
valueListenable: LogManager.logMessages,
builder: (context, logs, _) {
return ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) {
final log = logs[index];
final isError = log['type'] == 'error';
return ListTile(
leading: Icon(
isError ? Icons.error : Icons.info,
color: isError ? Colors.red : Colors.blue,
),
title: Text(log['message']),
subtitle: Text(log['timestamp']),
trailing: Text(log['type']),
);
},
);
},
);
Log Entry Structure
Each log is stored as a Map<String, dynamic> with the following structure:
{
'message': 'The log message', // String
'text': 'The log message', // String (same as message)
'timestamp': '14:32:45', // String in HH:MM:SS format
'type': 'info', // String: 'info' or 'error'
'isExpected': true, // bool: true = info, false = error
}
Complete LogManager API
Methods
| Method | Purpose | Example |
|---|---|---|
addLog() |
Add single log | LogManager.addLog('message') |
addMultipleLogs() |
Add multiple logs | LogManager.addMultipleLogs(['log1', 'log2']) |
clearLogs() |
Clear all logs | LogManager.clearLogs() |
getLastLogsAsStrings() |
Get last N logs | LogManager.getLastLogsAsStrings(10) |
getLogsByType() |
Filter by type | LogManager.getLogsByType(true) |
searchLogs() |
Search logs | LogManager.searchLogs('keyword') |
exportAsString() |
Export formatted | LogManager.exportAsString() |
exportAsJSON() |
Export as JSON | LogManager.exportAsJSON() |
dispose() |
Cleanup | LogManager.dispose() |
Properties
| Property | Type | Purpose |
|---|---|---|
logs |
List<String> |
All logs as strings |
logCount |
int |
Total number of logs |
errorCount |
int |
Number of error logs |
infoCount |
int |
Number of info logs |
logMessages |
ValueNotifier<List<Map>> |
For reactive UI updates |
mounted |
bool |
Check if widget binding available |
Logging Levels
LogManager uses a simple two-level logging system:
// Info level (default)
LogManager.addLog('Animation loaded', isExpected: true); // ✅ Info
// Error level
LogManager.addLog('Failed to load', isExpected: false); // ❌ Error
API Reference
RiveAnimationController
Global singleton for managing all Rive animations.
Key Methods:
register(String id, RiveManagerState state)- Register an animationupdateBool(String id, String name, bool value)- Update boolean inputupdateNumber(String id, String name, double value)- Update number inputtriggerInput(String id, String name)- Fire a triggersetTextRunValue(String id, String textRunName, String value)- Update textupdateDataBindingProperty(String id, String name, dynamic value)- Update data binding propertyupdateNestedProperty(String id, String path, dynamic value)- Update nested propertypreloadImagesForAnimation(String id, List<String> urls, Factory factory)- Cache imagesupdateImageFromCache(String id, int index)- Use cached imagegetCacheStats()- Get cache statistics
RiveManager Widget
Flutter widget for displaying Rive animations.
Constructor Parameters:
animationId- Unique identifier for this animation instanceriveFilePath- Path to .riv file in assetsexternalFile- External Rive file (alternative to riveFilePath)fileLoader- Custom file loaderenableImageReplacement- Enable dynamic image updates- Various display properties:
fit,alignment,hitTestBehavior, etc.
Callbacks:
onInit- Called when animation is loadedonInputChange- Called when input value changesonTriggerAction- Called when trigger firesonViewModelPropertiesDiscovered- Called when data binding properties foundonDataBindingChange- Called when data binding property changes
Best Practices
- Use Unique Animation IDs: Always provide unique identifiers for each animation instance
- Cache Images: For animations with many image updates, preload and cache images
- Handle Errors: Check return values and use logging to debug issues
- Dispose Properly: The package handles cleanup automatically in dispose()
- Nested Properties: Use path caching for frequently updated nested properties
- Enable Logging in Debug: Use LogManager to debug issues during development
Troubleshooting
Animation not loading?
- Check file path is correct
- Enable logging:
LogManager.enabled = true - Check logs for specific error messages
Image replacement not working?
- Ensure
enableImageReplacement: trueis set - Check that animation actually has image assets
- Verify image format is supported (PNG, JPEG, etc.)
Performance issues?
- Use image caching for frequent updates
- Enable property path caching (automatic)
- Monitor cache stats with
getCacheStats()
License
This package is licensed under the MIT License. See LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
For issues, feature requests, or questions:
- GitHub Repository: https://github.com/unspokenlanguage/RiveAnimation-Manager
- GitHub Issues: https://github.com/unspokenlanguage/RiveAnimation-Manager/issues
- pub.flutter-io.cn: https://pub.flutter-io.cn/packages/rive_animation_manager
Getting Help
- Check existing issues: Search GitHub issues first
- Review documentation: See README.md and EXAMPLES.md in the repository
- Create new issue: If not found, create a detailed issue with:
- Flutter version (
flutter --version) - Package version
- Error logs or stack trace
- Minimal reproducible example (MRE)
- Flutter version (
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with clear commit messages
- Add tests for new functionality
- Update documentation as needed
- Push to your branch (
git push origin feature/amazing-feature) - Open a Pull Request with detailed description
License
This package is licensed under the MIT License. See LICENSE file for details.
Changelog
See CHANGELOG.md for version history and updates.
Made with ❤️ for the Flutter community
Libraries
- rive
- rive_animation_manager
- Rive Animation Manager - A comprehensive Flutter package for managing Rive animations
- test