Files
rive-flutter/example/lib/examples/databinding_lists.dart
HayesGordon 16b484c81c chore(flutter): release with rive_native bump and update examples (#10190) eec2f66855
* chore(flutter): bump rive_native

* chore(flutter): add examples

* docs: update changelog

fix: recursively check whether an artboard is its ancestor before usi… (#10184) 53fb2577bc
* fix: recursively check whether an artboard is its ancestor before using it as the source of a nested artboard

fix(wagyu): proper init of Wagyu Render Pass Inputs (#10175) ff8fc66bc4
* proper init of Wagyu Render Pass Inputs

* fixed build issues

* clang-format

* proper black clear color

chore: update thumbnailer for new rive building set-up 9fd4961e9b

Co-authored-by: Gordon <pggordonhayes@gmail.com>
2025-07-16 17:53:55 +00:00

255 lines
7.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
import 'package:rive_example/main.dart' show RiveExampleApp;
/// Example using Rive data binding lists at runtime.
///
/// See: https://rive.app/docs/runtimes/data-binding
class ExampleDataBindingLists extends StatefulWidget {
const ExampleDataBindingLists({super.key});
@override
State<ExampleDataBindingLists> createState() =>
_ExampleDataBindingListsState();
}
class _ExampleDataBindingListsState extends State<ExampleDataBindingLists> {
late ViewModelInstance viewModelInstance;
late ViewModelInstanceList menuList;
late ViewModel todoItemVM;
// Text controllers for input fields
final TextEditingController _textController = TextEditingController();
final TextEditingController _num1Controller = TextEditingController();
final TextEditingController _num2Controller = TextEditingController();
final TextEditingController _deleteController = TextEditingController();
FileLoader fileLoader = FileLoader.fromAsset(
'assets/lists_demo.riv',
riveFactory: RiveExampleApp.getCurrentFactory,
);
@override
void initState() {
super.initState();
}
@override
void dispose() {
fileLoader.dispose();
_textController.dispose();
_num1Controller.dispose();
_num2Controller.dispose();
_deleteController.dispose();
super.dispose();
}
void _onLoaded(RiveLoaded state) {
viewModelInstance = state.viewModelInstance!;
// Get the menu list
menuList = viewModelInstance.list('menu')!;
// Get the TodoItem View Model
todoItemVM = state.file.viewModelByName('listItem')!;
debugPrint('Rive file loaded!');
debugPrint('Menu list: $menuList');
debugPrint('TodoItem VM: $todoItemVM');
}
void _onSubmit() {
final inputText = _textController.text.trim();
if (inputText.isEmpty) return;
debugPrint('Submitted text: $inputText');
var myTodo = todoItemVM.createInstance()!;
myTodo.string('label')!.value = inputText;
myTodo.string('fontIcon')!.value = "";
myTodo.color('hoverColor')!.value = const Color(0xffefefef);
menuList.add(myTodo);
myTodo.dispose();
// Clear the input
_textController.clear();
}
void _onSwap() {
final num1Text = _num1Controller.text.trim();
final num2Text = _num2Controller.text.trim();
if (num1Text.isEmpty || num2Text.isEmpty) return;
final num1 = int.tryParse(num1Text);
final num2 = int.tryParse(num2Text);
if (num1 != null && num2 != null) {
try {
menuList.swap(num1, num2);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error swapping items: $e')),
);
}
}
}
void _onDelete() {
final deleteText = _deleteController.text.trim();
if (deleteText.isEmpty) return;
final deleteIndex = int.tryParse(deleteText);
if (deleteIndex != null) {
try {
menuList.removeAt(deleteIndex);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error deleting item: $e')),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Data Binding Lists'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
// Rive Widget
Expanded(
child: RiveWidgetBuilder(
fileLoader: fileLoader,
dataBind: DataBind.auto(),
onLoaded: _onLoaded,
builder: (context, state) => switch (state) {
RiveLoading() =>
const Center(child: CircularProgressIndicator()),
RiveFailed() => ErrorWidget.withDetails(
message: state.error.toString(),
error: FlutterError(state.error.toString()),
),
RiveLoaded() => RiveWidget(controller: state.controller),
},
),
),
// Controls Panel
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey[300]!),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Add Item Section
const Text(
'Add New Item',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _textController,
decoration: const InputDecoration(
hintText: 'Enter item text',
border: OutlineInputBorder(),
),
onSubmitted: (_) => _onSubmit(),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _onSubmit,
child: const Text('Add'),
),
],
),
const SizedBox(height: 16),
// Swap Items Section
const Text(
'Swap Items',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _num1Controller,
decoration: const InputDecoration(
hintText: 'Index 1',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 8),
Expanded(
child: TextField(
controller: _num2Controller,
decoration: const InputDecoration(
hintText: 'Index 2',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _onSwap,
child: const Text('Swap'),
),
],
),
const SizedBox(height: 16),
// Delete Item Section
const Text(
'Delete Item',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _deleteController,
decoration: const InputDecoration(
hintText: 'Index to delete',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _onDelete,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Delete'),
),
],
),
],
),
),
],
),
);
}
}