- Notifications
You must be signed in to change notification settings - Fork 229
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