-
Notifications
You must be signed in to change notification settings - Fork 230
Overlapping artboards when using databinding in pushed routes #605
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Summary
When using the same Rive widget with data binding in multiple routes, artboards display correctly on the first-level route but show overlapping/incorrect artboards on pushed routes.
Environment
- rive: 0.14.1
- Flutter: 3.35.4 (Dart 3.9.2)
- Platform: iOS / Android
Expected Behavior
The WuzuAvatar widget should display the correct single artboard with data-bound properties (outfit, hat, background, etc.) regardless of which route it's rendered on.
Actual Behavior
- On the first-level route (e.g., home screen): Widget displays correctly with the specified artboard and bound properties.
- On pushed routes (e.g., diary detail, weekly report): Widget displays overlapping artboards that are not the ones specified. Multiple artboards appear to be rendered simultaneously.
Key observation: If I remove all data binding (ViewModelInstance properties), the widget renders correctly without overlapping issues.
Steps to Reproduce
- Create a Rive file with multiple artboards (e.g.,
cat,bear,dog) - Set up a ViewModel with artboard-type properties (e.g.,
clothes_back,clothes_front,hat_front) - Create a Flutter widget that uses
RiveWidgetControllerwithdataBind() - Use the widget on a root-level screen → works correctly
- Navigate to a pushed route and use the same widget → overlapping artboards appear
Code
Widget Implementation
class WuzuAvatar extends ConsumerStatefulWidget {
const WuzuAvatar({
super.key,
this.controller,
this.artboard, // Which artboard to use (cat, bear, dog)
this.collarColor,
this.eyeColor,
this.outfit, // Nested artboard variant
this.hat, // Nested artboard variant
this.background, // Nested artboard variant
this.foreground, // Nested artboard variant
// ... more properties
this.stateMachine,
this.size = 48.0,
this.onTap,
});
// ... properties
@override
ConsumerState<WuzuAvatar> createState() => _WuzuAvatarState();
}
class _WuzuAvatarState extends ConsumerState<WuzuAvatar> {
rive.File? _file;
rive.RiveWidgetController? _riveController;
rive.ViewModelInstance? _viewModelInstance;
rive.ViewModelInstanceArtboard? _clothesBackProperty;
rive.ViewModelInstanceArtboard? _clothesFrontProperty;
rive.ViewModelInstanceArtboard? _hatFrontProperty;
// ... more properties
@override
void initState() {
super.initState();
_loadRiveFile();
}
Future<void> _loadRiveFile() async {
final data = await rootBundle.load('assets/rive/wuzu.riv');
final bytes = data.buffer.asUint8List(
data.offsetInBytes,
data.lengthInBytes,
);
final file = await rive.File.decode(
bytes,
riveFactory: rive.Factory.rive,
);
if (!mounted) return;
setState(() {
_file = file;
_riveController = rive.RiveWidgetController(
file,
artboardSelector: rive.ArtboardSelector.byName(effectiveArtboard.value),
stateMachineSelector: widget.stateMachine != null
? rive.StateMachineSelector.byName(widget.stateMachine!.value)
: const rive.StateMachineDefault(),
);
});
_initDataBinding(effectiveArtboard);
}
void _initDataBinding(WuzuArtboard effectiveArtboard) {
if (_riveController == null || _file == null) return;
// Bind by index (each artboard has its own ViewModel)
_viewModelInstance = _riveController!.dataBind(
rive.DataBind.byIndex(effectiveArtboard.index),
);
if (_viewModelInstance == null) return;
// Bind nested artboard properties
_clothesBackProperty = _viewModelInstance!.artboard('clothes_back');
_clothesFrontProperty = _viewModelInstance!.artboard('clothes_front');
_hatFrontProperty = _viewModelInstance!.artboard('hat_front');
// ... more bindings
// Set initial values
_updateOutfit();
_updateHat();
// ... more updates
}
void _updateArtboard(
rive.ViewModelInstanceArtboard? property,
String prefix,
WuzuVariant? variant,
) {
if (property == null || variant == null || _file == null) return;
final artboardName = '${prefix}_${variant.value}';
final bindableArtboard = _file!.artboardToBind(artboardName);
if (bindableArtboard != null) {
property.value = bindableArtboard;
}
}
void _disposeDataBindingProperties() {
_clothesBackProperty?.dispose();
_clothesFrontProperty?.dispose();
_hatFrontProperty?.dispose();
// ... dispose all properties
_viewModelInstance?.dispose();
_riveController?.dispose();
}
@override
void dispose() {
_disposeDataBindingProperties();
_file?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.size * 1.378,
height: widget.size,
child: rive.RiveWidget(
controller: _riveController!,
fit: rive.Fit.contain,
),
);
}
}Usage on First-Level Route (Works)
// stats_page.dart - Root level route
WuzuAvatar(
controller: _avatarController,
stateMachine: stateMachine,
collarColor: profile.getCollarColor(Theme.of(context).brightness),
eyeColor: profile.getEyeColor(),
outfit: profile.equippedOutfitEnum,
hat: profile.equippedHatEnum,
size: _avatarSize,
)Usage on Pushed Route (Shows Overlapping)
// diary_detail_screen.dart - Pushed via Navigator.push
WuzuAvatar(
collarColor: profile?.getCollarColor(brightness),
eyeColor: profile?.getEyeColor(),
outfit: profile?.equippedOutfitEnum,
hat: profile?.equippedHatEnum,
background: WuzuVariant.hide,
foreground: WuzuVariant.hide,
inner: WuzuVariant.hide,
size: 48,
)Rive File Structure
- Main artboards:
cat,bear,dog(index 0, 1, 2) - Nested artboard slots:
clothes_back,clothes_front,hat_front,hat_back,background,foreground,inner,facial,blush - Variant artboards:
clothes_back_default,clothes_back_rain_coat,hat_front_witch, etc. - ViewModel per main artboard with artboard-type properties for each slot
Workaround
Removing all data binding code makes the artboards render correctly (but without dynamic property binding).
Screenshots
- Correct display on first-level route
- Overlapping artboards on pushed route (down right bottom)

Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working