View models describe a set of properties, but cannot themselves be used to get or set values - that is the role of view model instances.
To begin, we need to get a reference to a particular view model. This can be done either by index, by name, or the default for a given artboard, and is done from the Rive file. The default option refers to the view model assigned to an artboard by the dropdown in the editor.
Access a View Model from the created Rive object in the onLoad callback:
const rive = new rive.Rive({ onLoad: () => { // The Rive object is now loaded and ready to use. }});
Once Rive is loaded, you can access the view models using the following methods:
// Get reference by nameconst namedVM = rive.viewModelByName("My View Model");// Get reference by indexfor (let i = 0; i < rive.viewModelCount; i++) { const indexedVM = rive.viewModelByIndex(i);}// Get reference to the default view modelconst defaultVM = rive.defaultViewModel();
Alternatively, if you have access to the underlying Rive File object you can access the above methods on the file.
Access a View Model from the created Rive object in the onLoad callback:
const rive = new rive.Rive({ onLoad: () => { // The Rive object is now loaded and ready to use. }});
Once Rive is loaded, you can access the view models using the following methods:
// Get reference by nameconst namedVM = rive.viewModelByName("My View Model");// Get reference by indexfor (let i = 0; i < rive.viewModelCount; i++) { const indexedVM = rive.viewModelByIndex(i);}// Get reference to the default view modelconst defaultVM = rive.defaultViewModel();
Alternatively, if you have access to the underlying Rive File object you can access the above methods on the file.
Use the useViewModel hook to get a reference to a view model. You need to pass the rive object obtained from useRive.
import { useRive, useViewModel } from '@rive-app/react-webgl2';const { rive, RiveComponent } = useRive({ src: 'your_file.riv', // ... other options});// Option 1: Get the default ViewModel for the artboardconst defaultViewModel = useViewModel(rive); // Option 2: Get the default ViewModel explicitlyconst defaultViewModelExplicit = useViewModel(rive, { useDefault: true }); // Option 3: Get a ViewModel by its nameconst namedViewModel = useViewModel(rive, { name: 'MyViewModelName' });
let riveViewModel = RiveViewModel(...)let file = riveViewModel.riveModel!.riveFile// Data binding view model by namelet viewModelByName = file.viewModelNamed("...")// Data binding view model by indexfor index in 0..<file.viewModelCount { let viewModelByIndex = file.viewModel(at: index)}// Default data binding view model for an artboardlet artboard = riveViewModel.riveModel!.artboardlet viewModelForArtboard = file.viewModel(for: artboard)
// `view` of type RiveAnimationViewview.setRiveResource(R.raw.my_rive_file)val file = view.controller.file!!// Get reference by nameval vm = file.getViewModelByName("My View Model")// Get reference by indexfor (i in 0 until file.viewModelCount) { val indexedVM = file.getViewModelByIndex(i)}// Get reference to the default view modelval defaultVM = file.defaultViewModelForArtboard(view.controller.activeArtboard!!)
// Get reference to the File and Artboardfinal file = await File.asset( 'assets/my_file.riv', riveFactory: Factory.rive,);final artboard = file!.defaultArtboard()!;// Get reference by namefile.viewModelByName("My View Model");// Get reference by indexfor (var i = 0; i < file.viewModelCount; i++) { final indexedVM = file.viewModelByIndex(i);}// Get reference to the default view model for an artboardfinal defaultVM = file.defaultArtboardViewModel(artboard);// Dispose the view model when you're no longer using itviewModel.dispose();
These APIs are only needed when the Data Binding Mode on the RiveWidget is set to Manual.
Otherwise, you can configure view model binding directly in the Unity Inspector under the Data section.
private void OnEnable(){ riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;}private void OnDisable(){ riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;}private void HandleWidgetStatusChanged(){ if (riveWidget.Status == WidgetStatus.Loaded) { File file = riveWidget.File; // Get reference by name ViewModel viewModel = file.GetViewModelByName("My View Model"); // Get reference by index for (int i = 0; i < file.ViewModelCount; i++) { ViewModel indexedVM = file.GetViewModelAtIndex(i); } // Get reference to the default view model for an artboard ViewModel defaultVM = riveWidget.Artboard.DefaultViewModel; }}
In React Native you cannot directly instantiate a view model and pass it to Rive. Instead, React Native will always use the view model
that is bound to the artboard - as set in the editor.
This is because in React Native, you do not have direct control over the Rive file, the artboard, or the state machine.
What you can control is the instance of the view model. See below.
A future version of Rive for React Native may allow for similar control as the other runtimes.
Once we have a reference to a view model, it can be used to create an instance. When creating an instance, you have four options:
Create a blank instance - Fill the properties of the created instance with default values as follows:
Type
Value
Number
0
String
Empty string
Boolean
False
Color
#000000FF
Trigger
Untriggered
Enum
The first value
Nested view model
Null
Create the default instance - Use the instance labelled “Default” in the editor. Usually this is the one a designer intends as the primary one to be used at runtime.
Create by index - Using the order returned when iterating over all available instances. Useful when creating multiple instances by iteration.
Create by name - Use the editor’s instance name. Useful when creating a specific instance.
// Create a blank instance from a view model (ViewModel)const vmiBlank = viewModel.instance();// Create a default instance from a view model (ViewModel)const vmiDefault = viewModel.defaultInstance();// Create an instance by index from a view model (ViewModel)for (let i = 0; i < viewModel.instanceCount; i++) { const vmiIndexed = viewModel.instanceByIndex(i);}// Create an instace by name from a view model (ViewModel)const vmiNamed = viewModel.instanceByName("My Instance");
// Create a blank instance from a view model (ViewModel)const vmiBlank = viewModel.instance();// Create a default instance from a view model (ViewModel)const vmiDefault = viewModel.defaultInstance();// Create an instance by index from a view model (ViewModel)for (let i = 0; i < viewModel.instanceCount; i++) { const vmiIndexed = viewModel.instanceByIndex(i);}// Create an instace by name from a view model (ViewModel)const vmiNamed = viewModel.instanceByName("My Instance");
Use the useViewModelInstance hook to create a view model instance from a view model returned by the useViewModel hook.
import { useRive, useViewModel, useViewModelInstance } from '@rive-app/react-webgl2';const { rive, RiveComponent } = useRive({ src: 'your_file.riv', artboard: 'MyArtboard', stateMachine: 'MyStateMachine', // ... other options});const viewModel = useViewModel(rive, { name: 'MyViewModelName' }); // Or: const viewModel = useViewModel(rive); // Default VM// Get default instance without bindingconst defaultUnbound = useViewModelInstance(viewModel, { useDefault: true }); // Get named instance without bindingconst namedUnbound = useViewModelInstance(viewModel, { name: 'MyInstanceName' });// Create new blank instance without bindingconst newUnbound = useViewModelInstance(viewModel, { useNew: true });
You can also bind the view model instance directly to the Rive instance by passing the rive object to the useViewModelInstance hook.
import { useRive, useViewModel, useViewModelInstance } from '@rive-app/react-webgl2';const { rive, RiveComponent } = useRive({ src: 'your_file.riv', artboard: 'MyArtboard', stateMachine: 'MyStateMachine', autoBind: false, // Disable auto binding so we can manually bind later // ... other options});const viewModel = useViewModel(rive, { name: 'MyViewModelName' }); // Get default instance (implicit) and bind itconst defaultBound = useViewModelInstance(viewModel, { rive });// Get named instance and bind itconst namedBound = useViewModelInstance(viewModel, { name: 'MyInstanceName', rive });// Create a new blank instance and bind itconst newBound = useViewModelInstance(viewModel, { useNew: true, rive });
If you set autoBind: true in useRive, you can access the automatically bound default instance directly via rive.viewModelInstance once Rive is loaded, without needing useViewModel or useViewModelInstance.
const { rive, RiveComponent } = useRive({ src: 'your_file.riv', artboard: 'MyArtboard', stateMachine: 'MyStateMachine', autoBind: true,});// Once loaded, the instance is available:const boundInstance = rive?.viewModelInstance;
let riveViewModel = RiveViewModel(...) let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")! // Create blank let blankInstance = viewModel.createInstance() // Create default let defaultInstance = viewModel.createDefaultInstance() // Create by index for index in 0..<viewModel.instanceCount { let instanceByIndex = viewModel.createInstance(fromIndex: index) } // Create by name for name in viewModel.instanceNames { let instanceByName = viewModel.createInstance(fromName: name) }
val vm = view.controller.file?.getViewModelByName("My View Model")!!// Create blankval vmiBlank = vm.createBlankInstance()// Create defaultval vmiDefault = vm.createDefaultInstance()// Create by indexfor (i in 0 until vm.instanceCount) { val vmiIndexed = vm.createInstanceFromIndex(i)}// Create by nameval vmiNamed = vm.createInstanceFromName("My Instance")
final vm = file.viewModelByName("My View Model")!;// Create blankfinal vmiBlank = vm.createInstance();// Create defaultfinal vmiDefault = vm.createDefaultInstance();// Create by indexfor (int i = 0; i < vm.instanceCount; i++) {final vmiIndexed = vm.createInstanceByIndex(i);}// Create by namefinal vmiNamed = vm.createInstanceByName("My Instance");// Dispose the view model instanceviewModelInstance.dispose();
These APIs are only needed when the Data Binding Mode on the RiveWidget is set to Manual.
Otherwise, you can configure view model binding directly in the Unity Inspector under the Data section.
private void OnEnable(){ riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;}private void OnDisable(){ riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;}private void HandleWidgetStatusChanged(){ if (riveWidget.Status == WidgetStatus.Loaded) { // From a ViewModel reference ViewModel vm = riveWidget.File.GetViewModelByName("My View Model"); // Create blank ViewModelInstance vmiBlank = vm.CreateInstance(); // Create default ViewModelInstance vmiDefault = vm.CreateDefaultInstance(); // Create by index for (int i = 0; i < vm.InstanceCount; i++) { ViewModelInstance vmiIndexed = vm.CreateInstanceAt(i); } // Create by name ViewModelInstance vmiNamed = vm.CreateInstanceByName("My Instance"); }}
You can bind a view model instance to a Rive component by passing in a dataBinding prop to the Rive component.
The dataBinding prop accepts a DataBindBy type, which can be one of the following:
You can listen to errors by passing in the onError={(riveError: RNRiveError) prop to the Rive component.
The riveError object contains the error type and message, and you can filter out for RNRiveErrorType.DataBindingError:
The created instance can then be assigned to a state machine or artboard. This establishes the bindings set up at edit time.
It is preferred to assign to a state machine, as this will automatically apply the instance to the artboard as well. Only assign to an artboard if you are not using a state machine, i.e. your file is static or uses linear animations.
The initial values of the instance are not applied to their bound elements until the state machine or artboard advances.
const rive = new rive.Rive({ autoBind: false, // This should be set to false (default) onLoad: () => { const vm = rive.viewModelByName("My View Model"); const vmi = vm.instanceByName("My Instance"); // Manually bind by applying the instance to the state machine and artboard rive.bindViewModelInstance(vmi); } });
const rive = new rive.Rive({ autoBind: false, // This should be set to false (default) onLoad: () => { const vm = rive.viewModelByName("My View Model"); const vmi = vm.instanceByName("My Instance"); // Manually bind by applying the instance to the state machine and artboard rive.bindViewModelInstance(vmi); } });
For React, no additional steps are needed to bind the view model instance to the Rive component. Passing the rive object to useViewModelInstance handles this automatically.
let riveViewModel = RiveViewModel(...)let artboard = riveViewModel.riveModel!.artboard,let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()!// Apply the instance to the state machine (preferred)// Applying to a state machine will automatically bind to its artboardriveViewModel.riveModel!.stateMachine.bind(instance) // Alternatively, apply the instance to the artboardartboard.bind(viewModelInstance: instance)
view.setRiveResource( R.raw.my_rive_file, artboardName = "My Artboard",)val vm = view.controller.file?.getViewModelByName("My View Model")!!val vmi = vm.createInstanceFromName("My Instance")// Apply the instance to the state machine (preferred)view.controller.stateMachines.first().viewModelInstance = vmi// Alternatively, apply the instance to the artboardview.controller.activeArtboard?.viewModelInstance = vmi
final file = await File.asset('assets/my_file.riv',riveFactory: Factory.rive,);final artboard = file!.defaultArtboard();final stateMachine = artboard!.defaultStateMachine()!;final vm = file.defaultArtboardViewModel(artboard)!;final vmi = vm.createDefaultInstance()!;// Bind to the state machine. This automatically binds to the artboard as well.stateMachine.bindViewModelInstance(vmi);// If you're not using a state machine, bind to the artboardartboard.bindViewModelInstance(vmi);
// Access the RiveWidget component// Using the Unity Inspector// 1. Select your RiveWidget in the Inspector// 2. In the "Data" section, set the Data Binding Mode:// - Auto Bind Default: Automatically binds the default view model instance// - Auto Bind Selected: Uses a specific instance you select in the dropdown// - Manual: Requires you to manually set up binding in code// Or programmatically if set to Manual or if using the low-level APIprivate void OnEnable(){ riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;}private void OnDisable(){ riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;}private void HandleWidgetStatusChanged(){ if (riveWidget.Status == WidgetStatus.Loaded) { ViewModel vm = riveWidget.Artboard.DefaultViewModel; ViewModelInstance vmi = vm.CreateDefaultInstance(); // Applying to a state machine will automatically bind to its artboard riveWidget.StateMachine.BindViewModelInstance(vmi); }}
For React Native, no additional steps are needed to bind the view model instance to the Rive component. The dataBinding prop handles this automatically.
Alternatively, you may prefer to use auto-binding. This will automatically bind the default view model of the artboard using the default instance to both the state machine and the artboard. The default view model is the one selected on the artboard in the editor dropdown. The default instance is the one marked “Default” in the editor.
const rive = new rive.Rive({ src: "my_rive_file.riv", canvas: document.getElementById("canvas"), autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let boundInstance = rive.viewModelInstance; }});
const rive = new rive.Rive({ src: "my_rive_file.riv", canvas: document.getElementById("canvas"), autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let boundInstance = rive.viewModelInstance; }});
const { rive, RiveComponent } = useRive({ src: 'your_file.riv', artboard: 'MyArtboard', stateMachine: 'MyStateMachine', autoBind: true, // Enable auto-binding // ... other options});// Once loaded, the instance is available:const boundInstance = rive?.viewModelInstance;
let riveViewModel = RiveViewModel(...)riveViewModel.riveModel.enableAutoBind { instance in // Store a reference to `instance` to later access properties // The instance may change as state machines and artboards change}// If you'd like to disable autoBind after enabling…riveViewModel.riveModel!.disableAutoBind()
Flutter (rive_native) does not support auto-binding at this time. Higher level widgets will expose this functionality in the future.
See the RivePlayer.dart file in the example app for a demo of how this will be presented.
Rive Widget provides both visual and programmatic ways to configure auto-binding. In the Inspector, you can easily set up binding through the Data Binding Mode dropdown:
To enable auto-binding programmatically, use the following APIs:
// Before the widget is loaded:// Option 1: Auto bind the default instanceriveWidget.BindingMode = DataBindingMode.AutoBindDefault;// Option 2: Auto bind a specific instance by nameriveWidget.BindingMode = DataBindingMode.AutoBindSelected;riveWidget.ViewModelInstanceName = "My Instance";// Load the Rive file after setting the binding moderiveWidget.Load(riveFile, artboardName, stateMachineName);...// Access the current instance that was auto-boundViewModelInstance boundInstance = riveWidget.StateMachine.ViewModelInstance;
The default value for the dataBinding prop is AutoBind(false), which means auto-binding is disabled by default.
To enable auto-binding, set the dataBinding prop to AutoBind(true).
A property is a value that can be read, set, or observed on a view model instance. Properties can be of the following types:
Type
Supported
Floating point numbers
✅
Booleans
✅
Triggers
✅
Strings
✅
Enumerations
✅
Colors
✅
Nesting
✅
Lists
🚧 Coming soon
Images
🚧 Coming soon
Property descriptors can be inspected on a view model to discover at runtime which are available. These are not the mutable properties themselves though - once again those are on instances. These descriptors have a type and name.
// A list of properties on a view model (ViewModel)const properties = viewModel.properties;console.log(properties);
// A list of properties on a view model (ViewModel)const properties = viewModel.properties;console.log(properties);
// Access properties from the view model returned by useViewModelconst viewModel = useViewModel(rive);console.log(viewModel?.properties);
let riveViewModel = RiveViewModel(...)let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)!for property in viewModel.properties { print(property.type) // String, number, boolean, etc print(property.name) // The name of the property within the view model}
val vm = view.controller.file?.getViewModelByName("My View Model")!!// A list of propertiesval properties = vm.propertiesassertContains( properties, ViewModel.Property(ViewModel.PropertyDataType.NUMBER, "My Number Property"))
// Accesss on a ViewModel objectprint("Properties: ${viewModel.properties}");// Access on a ViewModelInstance objectprint("Properties: ${viewModelInstance.properties}");
var vm = riveWidget.File.GetViewModelByName("My View Model");// A list of propertiesvar properties = vm.Properties;foreach (var prop in properties){ Debug.Log($"Property: {prop.Name}, Type: {prop.Type}");}
The properties API is not yet available in React Native.
View model instances have mutable properties. References to these properties can be retrieved by name. They have get, set, and observe operations. Getting or observing the value will retrieve the latest value set on that properties binding, as of the last state machine or artboard advance. Setting the value will update the value and all of its bound elements.
Trigger properties do not have a get operation - only set and observe.
After setting a property’s value, the changes will not apply to their bound elements until the state machine or artboard advances.
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const numberProperty = vmi.number("My Number Property"); // Get const numberValue = numberProperty.value; // Set numberProperty.value = 10; } });
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const numberProperty = vmi.number("My Number Property"); // Get const numberValue = numberProperty.value; // Set numberProperty.value = 10; } });
Use the specific hook for a given property type to get and set property values.
useViewModelInstanceNumber: Read/write number properties
useViewModelInstanceColor: Read/write color properties with additional RGB/alpha methods
useViewModelInstanceEnum: Read/write enum properties with available values
useViewModelInstanceTrigger: Fire trigger events with optional callbacks
These hooks return the current value and a function to update it (setValue, setRgb, trigger). The value will be null if the property is not found or if the hook is provided with an invalid viewModelInstance.
import { useViewModelInstanceBoolean, useViewModelInstanceString, useViewModelInstanceNumber, useViewModelInstanceEnum, useViewModelInstanceColor, useViewModelInstanceTrigger} from '@rive-app/react-webgl2';// Assuming viewModelInstance is obtained via useViewModelInstance or rive.viewModelInstance// Booleanconst { value: isActive, setValue: setIsActive } = useViewModelInstanceBoolean( 'isToggleOn', // Property path viewModelInstance);// Set: setIsActive(true);// Stringconst { value: userName, setValue: setUserName } = useViewModelInstanceString( 'user/name', // Property path viewModelInstance);// Set: setUserName('Rive');// Numberconst { value: score, setValue: setScore } = useViewModelInstanceNumber( 'levelScore', // Property path viewModelInstance);// Set: setScore(100);// Enumconst { value: status, setValue: setStatus, values: statusOptions } = useViewModelInstanceEnum( 'appStatus', // Property path viewModelInstance);// Set: setStatus('loading'); // Get available options: statusOptions is an array like ['idle', 'loading', 'error']// Colorconst { value: themeColor, // Raw number value like -3267805 setRgb: setThemeColorRgb, // Set RGB components (0-255 values) setAlpha: setThemeColorAlpha, // Set alpha component (0-255) setOpacity: setThemeColorOpacity, // Set opacity (0.0-1.0) setRgba: setThemeColorRgba, // Set all components at once setValue: setThemeColorValue // Set raw color value} = useViewModelInstanceColor( 'ui/themeColor', // Property path viewModelInstance);// Set RGB: setThemeColorRgb(0, 128, 255); // Set to a blue color// Set Alpha: setThemeColorAlpha(128); // Set to 50% opacity// Set Opacity: setThemeColorOpacity(0.5); // Set to 50% opacity// Set RGBA: setThemeColorRgba(0, 128, 255, 255); // Blue with full opacity// Set Value: setThemeColorValue(-3267805); // Set using raw color value// Trigger (No value, just a trigger function)const { trigger: playEffect } = useViewModelInstanceTrigger( 'playButtonEffect', // Property path viewModelInstance, { // Optional callback to be called when the trigger is fired onTrigger: () => { console.log('Trigger Fired!'); } });// Trigger: playEffect();
The value returned by each hook will update automatically when the property changes in the Rive graphic.
let riveViewModel = RiveViewModel(...)let instance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!// Stringslet stringProperty = instance.stringProperty(fromPath: "...")!// Updating its valuestringProperty.value = "Hello, Rive"// Get its valueprint(stringProperty.value)// You can also set and get values without storing a strong referenceinstance.stringProperty(fromPath: "...").value = "Hello again, Rive"// Numberslet numberProperty = instance.numberProperty(fromPath: "...")!// Updating its valuenumberProperty.value = 1337// Get its valueprint(numberProperty.value)// You can also set and get values without storing a strong referenceinstance.numberProperty(fromPath: "...").value = 1337// Booleanslet booleanProperty = instance.booleanProperty(fromPath: "...")!// Updating its valuebooleanProperty.value = true// Get its valueprint(booleanProperty.value)// You can also set and get values without storing a strong referenceinstance.booleanProperty(fromPath: "...").value = true// Colorslet colorProperty = instance.colorProperty(fromPath: "...")!// Updating its value, which is a UIColor/NSColor, so all static helpers apply.colorProperty.value = .red// Get its valueprint(colorProperty.value)// You can also set and get values without storing a strong referenceinstance.colorProperty(fromPath: "...").value = .red// Enumslet enumProperty = instance.enumProperty(fromPath: "...")!// Updating its valueenumProperty.value = "Foo"// Get its valueprint(enumProperty.value)// Print all possible valuesprint(enumProperty.values)// You can also set and get values without storing a strong referenceinstance.enumProperty(fromPath: "...").value = "Foo"// Triggerlet triggerProperty = instance.triggerProperty(fromPath: "...")!// Fire the triggertriggerProperty.trigger()
val vm = view.controller.file?.getViewModelByName("My View Model")!!val vmi = vm.createInstanceFromName("My Instance")val numberProperty = vmi.getNumberProperty("My Number Property")// Getval numberValue = numberProperty.value// SetnumberProperty.value = 10f
final vm = file.defaultArtboardViewModel(artboard)!;final vmi = vm.createDefaultInstance()!;final numberProperty = vmi.number("My Number Property")!;// Getfinal numberValue = numberProperty.value;// SetnumberProperty.value = 10;// Observevoid onNumberChange(double value) { print("Number changed to: $value");}numberProperty.addListener(onNumberChange);// Remove listener when donenumberProperty.removeListener(onNumberChange);// Alternatively, clear all listenersnumberProperty.clearListeners();// Dispose of the property to clear up resources when you're no longer using it// This will call `clearListeners()` internally.numberProperty.dispose();
private void OnEnable(){ riveWidget.OnWidgetStatusChanged += HandleWidgetStatusChanged;}private void OnDisable(){ riveWidget.OnWidgetStatusChanged -= HandleWidgetStatusChanged;}private void HandleWidgetStatusChanged(){ // Check if the widget is loaded before accessing the view model instance if (riveWidget.Status == WidgetStatus.Loaded) { ViewModelInstance viewModelInstance = riveWidget.StateMachine.ViewModelInstance; //========================================================================== // STRING PROPERTIES //========================================================================== ViewModelInstanceStringProperty stringProperty = viewModelInstance.GetStringProperty("title"); Debug.Log($"String value: {stringProperty.Value}"); stringProperty.Value = "New Text"; //========================================================================== // NUMBER PROPERTIES //========================================================================== ViewModelInstanceNumberProperty numberProperty = viewModelInstance.GetNumberProperty("count"); Debug.Log($"Number value: {numberProperty.Value}"); numberProperty.Value = 42.5f; //========================================================================== // BOOLEAN PROPERTIES //========================================================================== ViewModelInstanceBooleanProperty boolProperty = viewModelInstance.GetBooleanProperty("isActive"); Debug.Log($"Boolean value: {boolProperty.Value}"); boolProperty.Value = true; //========================================================================== // COLOR PROPERTIES //========================================================================== ViewModelInstanceColorProperty colorProperty = viewModelInstance.GetColorProperty("backgroundColor"); // Using Unity Color (float values 0-1) Color currentColor = colorProperty.Value; colorProperty.Value = new UnityEngine.Color(1, 0, 0, 1); // Red color // Or using Color32 (byte values 0-255) Color32 currentColor32 = colorProperty.Value32; colorProperty.Value32 = new Color32(0, 255, 0, 255); // Green color //========================================================================== // ENUM PROPERTIES //========================================================================== ViewModelInstanceEnumProperty enumProperty = viewModelInstance.GetEnumProperty("category"); Debug.Log($"Enum current value: {enumProperty.Value}"); Debug.Log($"Enum available values: {string.Join(", ", enumProperty.EnumValues)}"); enumProperty.Value = "option_name"; //========================================================================== // TRIGGER PROPERTIES //========================================================================== ViewModelInstanceTriggerProperty triggerProperty = viewModelInstance.GetTriggerProperty("onSubmit"); triggerProperty.Trigger(); // Fire the trigger }}
There following data binding methods are exposed on the RiveRef object.
The color property can be set using either a RiveRGBA object or a hex string. The hex string should be in the format
#RRGGBBAA, where RR, GG, BB, and AA are two-digit hexadecimal values representing the red, green, blue, and
alpha channels, respectively.
type RiveRGBA = { r: number; g: number; b: number; a: number };
Example usage:
const [setRiveRef, riveRef] = useRive();const setBoolean = () => { if (riveRef) { riveRef.setBoolean('My Boolean Property', true); }};const setString = () => { if (riveRef) { riveRef.current.setString('My String Property', 'Hello, Rive'); }};const setNumber = () => { if (riveRef) { riveRef.current.setNumber('My Number Property', 10); }};const setColor = () => { if (riveRef) { riveRef.setColor('My Color Property', { r: 255, g: 0, b: 0, a: 1 }); // or riveRef.setColor('My Color Property', '#00FF00FF'); }};const setEnum = () => { if (riveRef) { riveRef.setEnum('My Enum Property', 'Option 1'); }};const trigger = () => { if (riveRef) { riveRef.trigger('My Trigger Property'); }};
You can observe changes over time to property values, either by using listeners or a platform equivalent method. Once observed, you will be notified when the property changes are applied by a state machine advance, whether that is a new value that has been explicitly set or if the value was updated as a result of a binding. Observing trigger properties is an alternative method to receive events from the editor, as compared to Rive Events.
Adding an observer to a property is done by calling the on method on the property.
public on(callback: EventCallback)
The observer can be removed by calling the off method on the property and passing the callback function. Alternatively, you can call off() without any arguments to remove all observers.
public off(callback?: EventCallback)
Example:
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const numberProperty = vmi.number("My Number Property"); // Observe numberProperty.on((event) => { console.log(event.data); }); // Remove all listener when done numberProperty.off(); } });
Adding an observer to a property is done by calling the on method on the property.
public on(callback: EventCallback)
The observer can be removed by calling the off method on the property and passing the callback function. Alternatively, you can call off() without any arguments to remove all observers.
public off(callback?: EventCallback)
Example:
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const numberProperty = vmi.number("My Number Property"); // Observe numberProperty.on((event) => { console.log(event.data); }); // Remove all listener when done numberProperty.off(); } });
The React hooks handle observability automatically. When a property’s value changes within the Rive instance (either because you set it via a hook or due to an internal binding), the value returned by the corresponding hook (e.g., useViewModelInstanceString) updates. This state change triggers a re-render of your React component, allowing you to react to the new value.
For Triggers, you can provide an onTrigger callback directly to the useViewModelInstanceTrigger hook, which fires when the trigger is activated in the Rive instance.
import { useViewModelInstanceTrigger } from '@rive-app/react-webgl2';// Assuming viewModelInstance is availableconst { trigger } = useViewModelInstanceTrigger( 'showPopup', viewModelInstance, { onTrigger: () => { console.log('Show Popup Trigger Fired!'); // Show your popup UI } });
let riveViewModel = RiveViewModel(...)let instance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()!// Get the string propertylet stringProperty = instance.stringProperty(fromPath: "...")!// Add a listenerlet listener = stringProperty.addListener { newValue in print(newValue)}// Remove a listener, where listener is the return value of addListenerstringProperty.removeListener(listener)// Trigger properties can also be listened to for when they are triggeredinstance.triggerProperty(fromPath: "...")!.addListener { print("Triggered!")}
val vm = view.controller.file?.getViewModelByName("My View Model")!!val vmi = vm.createInstanceFromName("My Instance")val numberProperty = vmi.getNumberProperty("My Number Property")// ObservelifecycleScope.launch { numberProperty.collect { value -> Log.i("MyActivity", "Value: $value") }}// Or collect in Composeval numberValue by numberProperty.collectAsState()
final vm = file.defaultArtboardViewModel(artboard)!;final vmi = vm.createDefaultInstance()!;final numberProperty = vmi.number("My Number Property")!;// Getfinal numberValue = numberProperty.value;// SetnumberProperty.value = 10;// Observevoid onNumberChange(double value) { print("Number changed to: $value");}numberProperty.addListener(onNumberChange);// Remove listener when donenumberProperty.removeListener(onNumberChange);// Alternatively, clear all listenersnumberProperty.clearListeners();// Dispose of the property to clear up resources when you're no longer using it// This will call `clearListeners()` internally.numberProperty.dispose();
The useRiveTrigger hook does not return a value, but instead takes a callback function as its third argument.
This callback will be executed when the trigger is fired.
Enums properties come in two flavors: system and user-defined. In practice, you will not need to worry about the distinction, but just be aware that system enums are available in any Rive file that binds to an editor-defined enum set, representing options from the editor’s dropdowns, where user-defined enums are those defined by a designer in the editor.
Enums are string typed. The Rive file contains a list of enums. Each enum in turn has a name and a list of strings.
const rive = new rive.Rive({ onLoad: () => { const enums = rive.enums(); console.log(enums); } });
const rive = new rive.Rive({ onLoad: () => { const enums = rive.enums(); console.log(enums); } });
val enums = view.controller.file?.enums!!val firstEnumName = enums[0].nameval firstEnumFirstValue = enums[0].values[0]
// Accesss on a File objectprint("Data enums: ${file.enums}");
var viewModelInstance = riveWidget.StateMachine.ViewModelInstance; // Accessing enums from the file var enums = riveWidget.File.ViewModelEnums; foreach (var enumType in enums) { Debug.Log($"Enum: {enumType.Name}"); foreach (var value in enumType.Values) { Debug.Log($" - Value: {value}"); } } ... // Using enum properties var enumProperty = viewModelInstance.GetEnumProperty("category"); Debug.Log($"Current value: {enumProperty.Value}"); Debug.Log($"Available values: {string.Join(", ", enumProperty.EnumValues)}"); enumProperty.Value = enumProperty.EnumValues[0]; // Set to first value
Retrieving the list of enums on the file is not yet available in React Native.
View models can have properties of type view model, allowing for arbitrary nesting. We could chain property calls on each instance starting from the root until we get to the property of interest. Alternatively we can do this through a path parameter, which is similar to a URI in that it is a forward slash delimited list of property names ending in the name of the property of interest.
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const nestedNumberByChain = vmi .viewModel("My Nested View Model") .viewModel("My Second Nested VM") .number("My Nested Number"); const nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number"); } });
const rive = new rive.Rive({ autoBind: true, onLoad: () => { // Access the current instance that was auto-bound let vmi = rive.viewModelInstance; const nestedNumberByChain = vmi .viewModel("My Nested View Model") .viewModel("My Second Nested VM") .number("My Nested Number"); const nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number"); } });
Access nested properties by providing the full path (separated by /) as the first argument to the property hooks.
See the examples/data_binding.dart file in the example app on Pub for a demo.
dart pub unpack rive_native # Unpack the package source code and example appcd rive_native/example # Navigate to the example folderflutter create . # Create the platform foldersflutter pub get # Fetch dependenciesflutter run # Run the example app