rive-app / rive-flutter

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

[BUG] Rive StateMatchine with multiple layers does not work properly #342

Closed Sh1d0w closed 1 month ago

Sh1d0w commented 1 year ago

Description

I have a Rive StateMachine with 3 layers in it. When an input is fired I have different conditions that trigger animations on all 3 of the layers.

It works perfectly with Rive for React, but when I import and use the Rive animation in flutter it only triggers the animation from the last layer and then exits, without animating the rest at the same time. Subsequent triggers also does not seem to trigger any animations.

Steps To Reproduce

Use the animation from the attached archive.

var ctrl = StateMachineController.fromArtboard(art, 'DivisionMachine',
        onStateChange: (
      String stateMachineName,
      String stateName,
    ) {
      print(stateMachineName);
      print(stateName);
    }) as StateMachineController;
    art.addController(ctrl);

    final divisionInput = ctrl.findInput<double>('division');

    if (divisionInput != null) {
      divisionInput.value = 7;
    }

    final suitInput = ctrl.findInput<double>('rank');
    if (suitInput != null) {
      suitInput.value = 3;
    }

    SMITrigger promote = ctrl.findSMI('promote');
    SMITrigger demote = ctrl.findSMI('demote');
    SMITrigger toClub = ctrl.findSMI('toClub');
    SMITrigger toDiamond = ctrl.findSMI('toDiamond');
    SMITrigger toHeart = ctrl.findSMI('toHeart');
    SMITrigger toSpade = ctrl.findSMI('toSpade');

     promote.fire();
     toHeart.fire();

Source .riv/.rev file

poc.zip

Expected behavior

When I hit the trigger all layers of animations are executed simultaneously.

Device & Versions (please complete the following information)

Sh1d0w commented 1 year ago

@luigi-rosso any chance you can look into this, please? Thanks 🙏

luigi-rosso commented 1 year ago

Hi @Sh1d0w, I don't think I'm reproducing the problem correctly. This seems to behave properly for how the layers are setup:

https://github.com/rive-app/rive-flutter/assets/454182/089f634e-f2ca-4315-b1db-c6b520a886f6

Does that look as expected or is something missing that I'm not catching?

Code I used:

import 'package:flutter/material.dart';
import 'package:rive/rive.dart';

class SimpleStateMachine extends StatefulWidget {
  const SimpleStateMachine({Key? key}) : super(key: key);

  @override
  State<SimpleStateMachine> createState() => _SimpleStateMachineState();
}

class _SimpleStateMachineState extends State<SimpleStateMachine> {
  SMITrigger? _promote;
  SMITrigger? _heart;

  void _onRiveInit(Artboard artboard) {
    final controller =
        StateMachineController.fromArtboard(artboard, 'DivisionMachine');
    artboard.addController(controller!);
    _promote = controller.findInput<bool>('promote') as SMITrigger;
    _heart = controller.findInput<bool>('toHeart') as SMITrigger;
    final divisionInput = controller.findInput<double>('division');

    if (divisionInput != null) {
      divisionInput.value = 7;
    }

    final suitInput = controller.findInput<double>('rank');
    if (suitInput != null) {
      suitInput.value = 3;
    }
  }

  void _onTap() {
    _promote?.fire();
    _heart?.fire();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Simple State Machine'),
      ),
      body: Stack(
        children: [
          Center(
            child: GestureDetector(
              onTap: _onTap,
              child: RiveAnimation.asset(
                'assets/rank_change.riv',
                fit: BoxFit.cover,
                onInit: _onRiveInit,
              ),
            ),
          ),
        ],
      ),
    );
  }
}
Sh1d0w commented 1 year ago

@luigi-rosso Thanks for looking into this. Yes that's the correct behaviour. But in my app it does not do this with the latest version of the plugin. It skips the whole layer of background change, and only triggers the stars animation.

https://github.com/rive-app/rive-flutter/assets/5074917/692a0190-1674-4f27-821c-c75b6e893fe2

Weird. Let me setup a bare flutter repo and I will try to reproduce it there, as our app is closed source.

Thanks again for your time, will get back to you. 🙏

Sh1d0w commented 1 year ago

@luigi-rosso OK I've found out what the issue is. From the difference in your code and mine it seems that it works when it is triggered from onTap event, all good.

But if I do it like my original post, e.g. in the rive's onInit callback, it does not work.

Is that expected, a limitation, or a bug? As I would expect to setup and trigger the animation right inside the init, not via gesture? What would be the correct way to do that in such case?

The use case is as you can see I have a very complex animation, so I have some Flutter logic which trigger should be fired, so auto play does not suit my need. I need to setup the input first and then based on some conditions to trigger the correct animation right after the initialisation without user interference.

Sh1d0w commented 1 year ago

I've workaround it by creating an async function _animate that I call from onInit and before the triggers I put this

await Future.delayed(Duration(milliseconds: 100));

It works, but not sure if this is intended or not. Feel free to close if you don't think this is bug. Thanks for your help mate!

HayesGordon commented 1 month ago

Closing this but please reopen if needed.