-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Issue
Right now, our usage looks something like this:
const { riveViewRef, setHybridRef } = useRive();
const riveFile: RiveFile = await RiveFileFactory.fromURL('https://cdn.rive.app/animations/vehicles.riv');
<RiveView
autoBind={false}
autoPlay={true}
fit={Fit.Layout}
file={riveFile}
artboardName="someName"
stateMachineName="someName"
hybridRef={setHybridRef}
/>And we allow updating text runs and inputs, and add an event listener, through the view ref. For example
riveViewRef.setTextRunValue('name', 'React Native');This requires the creation of the underlying native view before you can start manipulating Rive data.
This is currently a requirement on the old React Native runtime as well - and has been the cause of a lot of frustration for users to correctly set initial state before the view starts advancing the graphic. This is important as you want to be able to configure the starting state of your graphic, for both visual reasons and to ensure the state machine transitions correctly. In the past, this was solved by adding an onLoad callback that is invoked before the graphic starts advancing.
With this rewrite, we should explore improving this flow. Updating text runs and setting state machine inputs are deprecated in favour of data binding. But we can still improve it.
The important bit is that the same consideration applies to data binding. Right now, this API requires binding the created view model instance on the view ref as well:
riveViewRef.bindViewModelInstance(viewModelInstance);We should instead allow this binding to happen without the requirement of having a view ref. For the same reasons as above:
- Setting initial state
- Not being tied to a view
- Would facilitate writing React Native tests that don't rely on a view
Proposal
Instead of passing the file, artboardName, and stateMachineName, to the view, we allow users to create a controller/configurator that gets passed to the view.
I'll copy what the Flutter runtime does here:
// create the file
var file = (await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
));
// controller requires the file, and optional configurator to allows selection (name, index, default)
var controller = RiveWidgetController(
file,
artboardSelector: ArtboardSelector.byName('Coin'), // optional
stateMachineSelector: StateMachineSelector.byName('State Machine'), // optional
);
// User create a view model instance from the controller and specify some selection
// Auto bind
var viewModelInstance = controller.dataBind(DataBind.auto());
// other binding options
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
viewModelInstance = controller.dataBind(DataBind.byName('someName'));
viewModelInstance = controller.dataBind(DataBind.byInstance(existingInstance));
// later you only pass the controller to the view. The view is still responsible for visual appearance (fit, alignment, etc).
```dart
@override
Widget build(BuildContext context) {
return RiveWidget(
controller: controller,
fit: Fit.layout,
);
}This detaches the need for the view to bind data