跳到主要内容

数据绑定

数据绑定让你可以把 Rive 编辑器中设置为可绑定的元素连接到运行时代码。通过 View Model(视图模型)和 ViewModelInstance(视图模型实例),你可以读取、写入并监听属性变化,从而让 Flutter 应用中的状态与 Rive 动画保持同步。

数据绑定通常包含以下步骤:

  1. .riv 文件中获取 ViewModel
  2. 创建或获取一个 ViewModelInstance
  3. ViewModelInstance 绑定到状态机或 Artboard。
  4. 读取、写入或监听绑定属性。

如果你使用 RiveWidgetController,很多绑定步骤会自动完成。

视图模型

ViewModel 是在 Rive 编辑器中定义的数据结构。它描述了一组可绑定属性,例如数字、字符串、布尔值、颜色、枚举、图片、列表、Artboard 等。

在 Flutter 运行时中,你可以从 File 获取视图模型:按名称获取、按索引获取,或获取某个 Artboard 的默认视图模型。

信息

如果你正在使用 RiveWidgetController,可以跳过手动创建 ViewModel 的步骤,直接前往 视图模型实例

// Get reference to the File and Artboard
final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);
final artboard = file!.defaultArtboard()!;

// Get reference by name
file.viewModelByName("My View Model");

// Get reference by index
for (var i = 0; i < file.viewModelCount; i++) {
final indexedVM = file.viewModelByIndex(i);
}

// Get reference to the default view model for an artboard
final defaultVM = file.defaultArtboardViewModel(artboard);

// Dispose the view model when you're no longer using it
viewModel.dispose();

视图模型实例

ViewModelInstanceViewModel 的具体实例。你可以把它理解为视图模型的数据对象:ViewModel 定义有哪些属性,而 ViewModelInstance 持有这些属性的实际值。

一个 ViewModel 可以有多个实例。实例可以是空白实例,也可以来自 Rive 编辑器中导出的默认实例、命名实例或索引实例。

使用 RiveWidgetController

如果你使用 RiveWidgetController,可以通过 controller.dataBind(...) 直接创建并绑定视图模型实例。

// Get reference to the File
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);

// Create a controller
controller = RiveWidgetController(file!);

// Data bind by name
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));

// Data bind by index
viewModelInstance = controller.dataBind(DataBind.byIndex(0));

// Auto data bind
viewModelInstance = controller.dataBind(DataBind.auto());

// Bind some existing view model instance to the controller:
viewModelInstance = controller.dataBind(DataBind.byInstance(someViewModelInstance));

// Dispose of objects you created when no longer needed
viewModelInstance.dispose();
controller.dispose();
file.dispose();

手动管理视图模型实例

如果你想自己管理视图模型实例的创建,可以从 ViewModel 创建实例。

final vm = file.viewModelByName("My View Model")!;

// Create blank
final vmiBlank = vm.createInstance();

// Create default
final vmiDefault = vm.createDefaultInstance();

// Create by index
for (int i = 0; i < vm.instanceCount; i++) {
final vmiIndexed = vm.createInstanceByIndex(i);
}

// Create by name
final vmiNamed = vm.createInstanceByName("My Instance");

// Dispose the view model instance
viewModelInstance.dispose();

绑定

绑定是把 ViewModelInstance 连接到状态机或 Artboard 的过程。绑定后,Rive 文件中使用该视图模型的属性会与运行时代码中的实例数据联动。

如果你使用 RiveWidgetController,调用以下任意方法时会自动完成绑定:

viewModelInstance = controller.dataBind(DataBind.auto());
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
viewModelInstance = controller.dataBind(DataBind.byInstance(someViewModelInstance));

否则,你需要确保把视图模型实例绑定到状态机或 Artboard。

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 artboard
artboard.bindViewModelInstance(vmi);

自动绑定

自动绑定会让运行时根据 Rive 文件中的默认设置选择合适的视图模型并创建实例。对于常见场景,这是最简单的方式。

// Get reference to the File
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);

// Create a controller
controller = RiveWidgetController(file!);

// Auto data bind
viewModelInstance = controller.dataBind(DataBind.auto());

// Dispose of objects you created when no longer needed
viewModelInstance.dispose();
controller.dispose();
file.dispose();

属性

视图模型由属性组成。属性可以是数字、字符串、布尔值、颜色、枚举、图片、列表、Artboard 或嵌套的视图模型等。

在运行时,你可以从 ViewModelViewModelInstance 查看可用属性,也可以从 ViewModelInstance 读取和写入具体属性值。

列出属性

可以在 ViewModel 对象或 ViewModelInstance 对象上访问 properties,以查看可用属性。

// Access on a ViewModel object
print("Properties: ${viewModel.properties}");

// Access on a ViewModelInstance object
print("Properties: ${viewModelInstance.properties}");

读取和写入属性

通过属性名称可以从 ViewModelInstance 获取属性对象。属性对象提供 value,可用于读取或设置当前值。

// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;

final numberProperty = vmi.number("My Number Property")!;
// Get
final numberValue = numberProperty.value;

// Set
numberProperty.value = 10;

// Observe
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);

// Remove listener when done
numberProperty.removeListener(onNumberChange);

// Alternatively, clear all listeners
numberProperty.clearListeners();

// Dispose of the property to clear up resources when you're no longer using it
// This will call `clearListeners()` internally.
numberProperty.dispose();

嵌套属性路径

如果视图模型中包含嵌套视图模型,可以通过链式访问逐级获取属性,也可以使用以 / 分隔的路径直接访问嵌套属性。

// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;

final nestedNumberByChain = vmi
.viewModel("My Nested View Model")!
.viewModel("My Second Nested VM")!
.number("My Nested Number");

final nestedNumberByPath = vmi.number("My Nested View Model/My Second Nested VM/My Nested Number");

可观察性

属性支持监听变化。你可以使用 addListener 注册回调,在属性值变化时收到通知;不再需要时使用 removeListenerclearListenersdispose 清理监听器。

// Get reference to the ViewModel instance
final vmi = someExistingViewModelInstance;

final numberProperty = vmi.number("My Number Property")!;
// Get
final numberValue = numberProperty.value;

// Set
numberProperty.value = 10;

// Observe
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);

// Remove listener when done
numberProperty.removeListener(onNumberChange);

// Alternatively, clear all listeners
numberProperty.clearListeners();

// Dispose of the property to clear up resources when you're no longer using it
// This will call `clearListeners()` internally.
numberProperty.dispose();

图片

图片属性允许你在运行时把图片数据设置到 Rive 文件中的绑定图片属性上。

通常流程是:

  1. ViewModelInstance 获取图片属性。
  2. 使用 Factory.rive.decodeImage(bytes)Factory.flutter.decodeImage(bytes) 解码图片。
  3. 将得到的 RenderImage 设置为图片属性的 value
信息

请参阅 Flutter 数据绑定图片示例:databinding_images.dart

// Access the image property by path on a ViewModelInstance object
final imageProperty = viewModelInstance.image('my_image')!; // image property named "my_image"

// Create a RenderImage
final renderImage = await Factory.rive.decodeImage(bytes); // use `Factory.flutter` if you're using the Flutter renderer

// If the image is valid, update the image property value
if (renderImage != null) {
imageProperty.value = renderImage;
}

// You can also set the image property to null to clear it
imageProperty.value = null;

列表

Flutter 中的列表 API 设计为与 Dart 的 List 类相似。它没有包含 List 的完整 API,但提供了最常用的方法。

注意

使用列表时,如果访问越界索引,或执行不允许的列表操作,可能会抛出错误(RangeError)。这与 Dart 的 List API 类似。

ViewModelInstance 对象上通过路径访问列表属性:

final todosProperty = viewModelInstance.list('todos')!; // list property named "todos"
print(todosProperty.length); // print the length of the list

要添加一个条目,需要先创建要添加到列表中的视图模型实例:

final todoItemVM = riveFile.viewModelByName("TodoItem")!;
final todoItemInstance = todoItemVM.createInstance()!;

你也可以使用从 Rive 编辑器导出的现有实例来创建实例:

  • createDefaultInstance()
  • createInstanceByName('exercise')
  • createInstanceByIndex(0)

然后把实例添加到列表中:

todosProperty.add(todoItemInstance);

要从列表中移除某个特定实例,可以使用 remove 方法:

todosProperty.remove(todoItemInstance);

其他操作:

// Remove at index
todosProperty.removeAt(0); // can throw

// Insert at index
todosProperty.insert(0, todoItemInstance); // can throw

// Swap
todosProperty.swap(0, 1); // can throw

// First
ViewModelInstance todo = todosProperty.first(); // can throw

// Last
ViewModelInstance todo = todosProperty.last(); // can throw

// First or null
ViewModelInstance? todo = todosProperty.firstOrNull(); // will return null if the list is empty

// Last or null
ViewModelInstance? todo = todosProperty.lastOrNull(); // will return null if the list is empty

// Access/set directly by index
final instance = todosProperty[0]; // can throw
todosProperty[0] = todoItemInstance; // can throw

// Instance at index
todosProperty.instanceAt(2); // can throw

// Length
todosProperty.length;

Artboard

Artboard 属性使用 BindableArtboard 类,它不同于包中的常规 Artboard 类。

BindableArtboard 是一个运行时包装器,用于通过数据绑定与 Artboard 交互。这些实例引用文件中已有的 Artboard,因此不需要在 Rive 编辑器中进行额外设置。

信息

请参阅 Flutter 数据绑定 Artboard 示例:databinding_artboards.dart

// Artboard property to bind
final artboardProp = viewModelInstance.artboard('artboardPropertyName')!;

// Create a bindable artboard
final bindableArtboard = riveFile.artboardToBind('artboardName')!;
artboardProp.value = bindableArtboard;

枚举

如果 Rive 文件中定义了数据枚举,可以通过 File 对象访问它们。

// Access on a File object
print("Data enums: ${file.enums}");

示例

  • 数据绑定概览:databinding.dart
  • 数据绑定图片:databinding_images.dart
  • 数据绑定 Artboard:databinding_artboards.dart
  • 数据绑定列表:databinding_lists.dart