rive-app / rive-flutter

Flutter runtime for Rive
https://rive.app
MIT License
1.16k stars 180 forks source link

Rive animation freezes with Flutter Web in Chrome #345

Closed PawlikMichal25 closed 9 months ago

PawlikMichal25 commented 9 months ago

Description

The rive animation which we created is consistently freezing on this weird frame (see screenshot). What's interesting is that we don't even have such purple circles in our animation, so I don't understand where they are coming from (blue background is set in app, not part of animation). When I run our app in Chrome Incognito then it always works, but when I run it in normal Chrome window then it always freezes.

Source .riv/.rev file

https://drive.google.com/file/d/14eeedCjplGN7iVATbxmbQfMNPQaeXUYs/view?usp=sharing

Screenshots

rive

Device & Versions

Code

Code ```dart class LaunchLoading extends StatefulWidget { const LaunchLoading(); @override State createState() => _LaunchLoadingState(); } class _LaunchLoadingState extends State { late final RiveAnimationController _startLoadingController; late final RiveAnimationController _finishLoadingController; _LaunchAnimation _currentAnimation = _LaunchAnimation.startLoading; @override void initState() { super.initState(); _startLoadingController = OneShotAnimation( _LaunchAnimation.startLoading.name, mix: 0, autoplay: true, onStop: () => setState(() => _currentAnimation = _LaunchAnimation.loading), ); _finishLoadingController = OneShotAnimation( _LaunchAnimation.finishLoading.name, mix: 0, autoplay: true, onStop: () => _onAnimationFinished(), ); } @override void dispose() { _startLoadingController.dispose(); _finishLoadingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return RiveAnimation.asset( Assets.animationLaunchMoments, animations: [_currentAnimation.name], controllers: [_startLoadingController, _finishLoadingController], fit: BoxFit.cover, ); } } ```
HayesGordon commented 9 months ago

Hi @PawlikMichal25 , thanks for the detailed info.

I'm not able to reproduce this on my side with the latest version of the runtime (v0.11.16)

This is the code I used:

class SimplePlayer extends StatelessWidget {
  const SimplePlayer({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Colors.blue,
      body: RiveAnimation.asset(
        'assets/launch_moments.riv',
        artboard: 'FinalCanvas',
        stateMachines: ['State Machine'],
        fit: BoxFit.cover,
      ),
    );
  }
}

Are you experiencing this in your debug builds or only after releasing? It might be that your browser is caching an older version of the file that is not the one you're trying to show. Can you try clearing your cache?

PawlikMichal25 commented 9 months ago

Thanks for trying. It only happens after release and I actually cleared cache multiple times.

Do you have any ideas what else might be causing it? Or if we should try splitting our animation into smaller animations?

HayesGordon commented 9 months ago

The fact that it's working in a private browser makes me think it must be something cache related.

Apologies I also missed that you shared a code snippet. If you can share a complete reproducible example that will be helpful for debugging, as the code above is missing implementations for me to run. It could be that something is wrong with they way you're mixing the animations and playing multiple at the same time potentially.

That said though, I see your animation file is set up with a StateMachine, but you're coding in the animation mixing yourself.

I strongly recommend rather making use of the state machine for blending and configuring your animation playback. It's much simpler. What I originally used to playback your animation is:

class SimplePlayer extends StatelessWidget {
  const SimplePlayer({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Colors.blue,
      body: RiveAnimation.asset(
        'assets/launch_moments.riv',
        artboard: 'FinalCanvas',
        stateMachines: ['State Machine'],
        fit: BoxFit.cover,
      ),
    );
  }
}

The in the RiveEditor you can configure the transitions and blends as needed on the StateMachine

PawlikMichal25 commented 9 months ago

I actually don't want to mix any animations. I just want to trigger the last state in our animation when we finish loading data. For this use case, do you think I should be doing things differently than the RiveAnimationControllers?

HayesGordon commented 9 months ago

Your State Machine seems to be setup exactly to handle this:

https://github.com/rive-app/rive-flutter/assets/13705472/beec846b-1cce-4202-b0c8-f6b8cd9cd970

It is a bit confusing why you would not use the state machine to drive this behaviour from code.

See our state machine documentation for instructions on inputs: https://help.rive.app/runtimes/state-machines

You can just play the state machine as I showed above, and then eventually call the LoadComplete trigger when needed.

HayesGordon commented 9 months ago

Sorry I see that it's not a trigger, you can achieve what I described above with this code:

class SimplePlayer extends StatefulWidget {
  const SimplePlayer({super.key});

  @override
  State<SimplePlayer> createState() => _SimplePlayerState();
}

class _SimplePlayerState extends State<SimplePlayer> {
  late final StateMachineController _controller;
  late final SMIBool _triggerComplete;

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blue,
      body: Column(
        children: [
          Expanded(
            child: RiveAnimation.asset(
              'assets/launch_moments.riv',
              artboard: 'FinalCanvas',
              onInit: (artboard) {
                _controller = artboard.stateMachineByName('State Machine')!;
                artboard.addController(_controller);
                _triggerComplete =
                    _controller.findInput<bool>('LoadComplete') as SMIBool;
              },
              fit: BoxFit.cover,
            ),
          ),
          Center(
            child: ElevatedButton(
              onPressed: () {
                _triggerComplete.value = true;
              },
              child: const Text('complete'),
            ),
          )
        ],
      ),
    );
  }
}
PawlikMichal25 commented 9 months ago

Thanks for all the help. The code is cleaner, but it didn't resolve my issue. So following your advice I completely wiped all data in Chrome and it helped šŸ™ƒ Before I was Hard Reloading / Clearing cache for my particular website and that apparently wasn't enough