数据绑定
使用视图模型将代码连接到编辑器中绑定的元素。
在加载 Rive 文件后,在运行时使用视图模型来连接数据。
如果你使用的是 RiveWidgetController,可以跳过创建 ViewModel 的步骤。直接转到视图模型实例。
// 获取 File 和 Artboard 的引用
final file = await File.asset(
'assets/my_file.riv',
riveFactory: Factory.rive,
);
final artboard = file!.defaultArtboard()!;
// 按名称获取引用
file.viewModelByName("My View Model");
// 按索引获取引用
for (var i = 0; i < file.viewModelCount; i++) {
final indexedVM = file.viewModelByIndex(i);
}
// 获取画板的默认视图模型引用
final defaultVM = file.defaultArtboardViewModel(artboard);
// 不再使用时释放视图模型
viewModel.dispose();
视图模型实例
创建和管理视图模型的实例,用于在运行时与 Rive 图形交换数据。
如果你使用 RiveWidgetController:
// 获取 File 的引用
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);
// 创建控制器
controller = RiveWidgetController(file!);
// 按名称进行数据绑定
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
// 按索引进行数据绑定
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
// 自动数据绑定
viewModelInstance = controller.dataBind(DataBind.auto());
// 将已有的视图模型实例绑定到控制器:
viewModelInstance = controller.dataBind(DataBind.byInstance(someViewModelInstance));
// 不再需要时释放你创建的对象
viewModelInstance.dispose();
controller.dispose();
file.dispose();
如果你想自行管理视图模型实例的创建:
final vm = file.viewModelByName("My View Model")!;
// 创建空白实例
final vmiBlank = vm.createInstance();
// 创建默认实例
final vmiDefault = vm.createDefaultInstance();
// 按索引创建
for (int i = 0; i < vm.instanceCount; i++) {
final vmiIndexed = vm.createInstanceByIndex(i);
}
// 按名称创建
final vmiNamed = vm.createInstanceByName("My Instance");
// 释放视图模型实例
viewModelInstance.dispose();
绑定
将视图模型实例绑定到画板或状态机,以建立数据连接。
如果你使用 RiveWidgetController,调用以下任一方法时会自动完成绑定:
viewModelInstance = controller.dataBind(DataBind.auto());
viewModelInstance = controller.dataBind(DataBind.byName('My View Model'));
viewModelInstance = controller.dataBind(DataBind.byIndex(0));
viewModelInstnace = controller.dataBind(someViewModelInstance);
否则,你需要手动将视图模型实例绑定到状态机或画板。
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()!;
// 绑定到状态机。这会自动同时绑定到画板。
stateMachine.bindViewModelInstance(vmi);
// 如果不使用状态机,则绑定到画板
artboard.bindViewModelInstance(vmi);
自动绑定
自动绑定可简化运行时数据绑定,无需手动绑定到画板或状态机。当文件包含视图模型时,它通过索引或类型自动选择视图模型和实例。
// 获取 File 的引用
file = await File.asset(
'assets/rewards.riv',
riveFactory: Factory.rive,
);
// 创建控制器
controller = RiveWidgetController(file!);
// 自动数据绑定
viewModelInstance = controller.dataBind(DataBind.auto());
// 不再需要时释放你创建的对象
viewModelInstance.dispose();
controller.dispose();
file.dispose();
属性
属性是视图模型中可读写的数据点,用于连接到编辑器元素或在运行时从代码中更新。
列出属性
// 在 ViewModel 对象上访问
print("Properties: ${viewModel.properties}");
// 在 ViewModelInstance 对象上访问
print("Properties: ${viewModelInstance.properties}");
读写属性
// 获取 ViewModel 实例的引用
final vmi = someExistingViewModelInstance;
final numberProperty = vmi.number("My Number Property")!;
// 获取
final numberValue = numberProperty.value;
// 设置
numberProperty.value = 10;
// 监听
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);
// 完成后移除监听器
numberProperty.removeListener(onNumberChange);
// 或者清除所有监听器
numberProperty.clearListeners();
// 释放属性以清理资源,当你不再使用它时
// 这将在内部调用 `clearListeners()`。
numberProperty.dispose();
嵌套属性路径
使用点分隔路径或链式属性访问来访问嵌套属性。
// 获取 ViewModel 实例的引用
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");
可观察性
设置属性后,任何监听器都会收到通知,从而无需手动轮询即可支持响应式更新。
// 获取 ViewModel 实例的引用
final vmi = someExistingViewModelInstance;
final numberProperty = vmi.number("My Number Property")!;
// 获取
final numberValue = numberProperty.value;
// 设置
numberProperty.value = 10;
// 监听
void onNumberChange(double value) {
print("Number changed to: $value");
}
numberProperty.addListener(onNumberChange);
// 完成后移除监听器
numberProperty.removeListener(onNumberChange);
// 或者清除所有监听器
numberProperty.clearListeners();
// 释放属性以清理资源,当你不再使用它时
// 这将在内部调用 `clearListeners()`。
numberProperty.dispose();
图片
// 在 ViewModelInstance 对象上按路径访问图片属性
final imageProperty = viewModelInstance.image('my_image')!; // 名为 "my_image" 的图片属性
// 创建 RenderImage
final renderImage = await Factory.rive.decodeImage(bytes); // 如果使用 Flutter 渲染器,使用 `Factory.flutter`
// 如果图片有效,更新图片属性值
if (renderImage != null) {
imageProperty.value = renderImage;
}
// 你也可以将图片属性设置为 null 来清除它
imageProperty.value = null;
列表
Flutter 中的列表 API 设计类似于 Dart 中的 List 类。它不包含该类的完整 API 规范,但提供了最常用的方法。
⚠️ 警告:使用列表时,如果尝试访问越 界索引或执行其他不允许的列表操作,可能会导致错误(
RangeError)被抛出。与 Dart List API 类似。
在 ViewModelInstance 对象上按路径访问列表属性:
final todosProperty = viewModelInstance.list('todos')!; // 名为 "todos" 的列表属性
print(todosProperty.length); // 打印列表长度
要添加项目,首先需要创建要添加到列表中的视图模型实例:
final todoItemVM = riveFile.viewModelByName("TodoItem")!;
final todoItemInstance = todoItemVM.createInstance()!;
你也可以从已有实例(如 Rive 编辑器中导出的)创建实例,使用:
createDefaultInstance()createInstanceByName('exercise')createInstanceByIndex(0)
然后将实例添加到列表中:
todosProperty.add(todoItemInstance);
要从列表中移除特定实例,可以使用 remove 方法:
todosProperty.remove(todoItemInstance);
其他操作:
// 按索引移除
todosProperty.removeAt(0); // 可能抛出异常
// 按索引插入
todosProperty.insert(0, todoItemInstance); // 可能抛出异常
// 交换
todosProperty.swap(0, 1); // 可能抛出异常
// 第一个
ViewModelInstance todo = todosProperty.first(); // 可能抛出异常
// 最后一个
ViewModelInstance todo = todosProperty.last(); // 可能抛出异常
// 第一个或 null
ViewModelInstance? todo todosProperty.firstOrNull(); // 列表为空时返回 null
// 最后一个或 null
ViewModelInstance? todosProperty.lastOrNull(); // 列表为空时返回 null
// 直接按索引访问/设置
final instance = todosProperty[0]; // 可能抛出异常
todosProperty[0] = todoItemInstance; // 可能抛出异常
// 按索引获取实例
todosProperty.instanceAt(2); // 可能抛出异常
// 长度
todosProperty.length;
画板
画板属性使用 BindableArtboard 类,这与包中的常规 Artboard 类不同。
BindableArtboard 是一个运行时包装器,用于通过数据绑定与画板交互。这些实例引用文件中已有的画板,因此无需在 Rive 编辑器中额外设置。
// 要绑定的画板属性
final artboardProp = viewModelInstance.artboard('artboardPropertyName')!;
// 创建可绑定的画板
final bindableArtboard = riveFile.artboardToBind('artboardName')!;
artboardProp.value = bindableArtboard;
枚举
// 在 File 对象上访问
print("Data enums: ${file.enums}");