2d-inc / Flare-Flutter

Load and get full control of your Rive files in a Flutter project using this library.
https://rive.app/
MIT License
2.55k stars 470 forks source link

Animation State not changing #260

Closed luizwalber closed 4 years ago

luizwalber commented 4 years ago

Hello everyone,

I don't know if this is a bug or I'm just doing something wrong, I'm trying to create an animated background, like the gif bellow:

WhatsApp-Video-2020-06-18-at-11.07.22-AM.md.gif

Gif Link: https://gifyu.com/image/uieR

it Starts nicely, but when I try to go back to 'night_idle' the state changes, but the animation doesn't, I don't know if is showing in the image, but the button is animated correctly, and if I keep changing the animations every one plays nicely except the 'night_idle', does anyone have any ideas?

The code (https://github.com/luizwalber/tests):

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List<String> animations = [
    Constants.ANIMATION_DAY_IDLE,
    Constants.ANIMATION_DAY_TO_NIGHT,
    Constants.ANIMATION_NIGHT_IDLE,
    Constants.ANIMATION_NIGHT_TO_DAY,
  ];

  int animationIndex;
  bool isAnimating;

  @override
  void initState() {
    animationIndex = 0;
    isAnimating = false;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        _backgroundFlare(context),
        _flareButton(),
      ],
    );
  }

  Widget _backgroundFlare(context) {
    return FlareActor(
      Constants.FLARE_BACKGROUND,
      animation: animations[animationIndex],
      fit: BoxFit.fill,
      callback: (name) {
        finishChanging();
      },
    );
  }

  Widget _flareButton() {
    return Center(
      child: Container(
        height: 100,
        width: 100,
        child: GestureDetector(
            onTap: () => changeBackground(),
            child: FlareActor(
              Constants.FLARE_CHANGE_THEME_BUTTON,
              animation: animations[animationIndex],
            )),
      ),
    );
  }

  void changeBackground() {
    if (isAnimating) {
      return;
    } else {
      isAnimating = true;
    }

    _increaseAnimation();
  }

  void finishChanging() {
    setState(() {
      isAnimating = false;
    });
    _increaseAnimation();
  }

  void _increaseAnimation() {
    setState(() {
      animationIndex = (animationIndex + 1) % 4;
    });
  }
}

The Flare files: background: https://rive.app/a/luizwalber/files/flare/background button: https://rive.app/a/luizwalber/files/flare/change-theme

One thing I notice is that if I change the order (starting in day_idle) the night_idle never plays, it only plays when it is the first

But at least some times it is playing, so the animation name is correctly

OBS: If you open the project you can see that I'm warming up the images before running the app too. I don't know if this changes something


const _filesToWarmup = [
  Constants.FLARE_BACKGROUND,
  Constants.FLARE_CHANGE_THEME_BUTTON,
];

Future<void> warmupFlare() async {
  for (final filename in _filesToWarmup) {
    final AssetProvider assetProvider =
        AssetFlare(bundle: rootBundle, name: filename);
    await cachedActor(assetProvider);
  }
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  FlareCache.doesPrune = false;
  warmupFlare().then((value) => runApp(App()));
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Background Flare',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        brightness: Brightness.dark,
      ),
      home: HomePage(),
    );
  }
}
neurowave commented 4 years ago

You probably need to set some keys at the start of whatever animation plays after night_idle to set it back to its original state, if that's what you want. We don't do this automatically when you play a different animation because that would defeat the benefits of animation mixing. You as the animator are in control of what happens when the next animation plays, but it's an additive process. So if you switch state when night idle was half-way through its timeline, it'll still be half-way through.

luizwalber commented 4 years ago

Thanks for answering so fast

the night_idle is in loop, shouldn't reset to initial state when finishing?

neurowave commented 4 years ago

Not unless you tell it to finish the loop before transitioning to the next state. If you want to be able to interrupt the state at any time, then you need to put the same keys from the start of your night_idle loop to the start of the next animation that gets triggered.

This is perhaps something one of our engineers like @umberto-sonnino can help with better than I can, as I'm not entirely sure what your coding is trying to do. :)

umberto-sonnino commented 4 years ago

Thanks for the report, I looked at your repo, and I see that you're swapping animation names in the code. This might work, but it's not ideal, as you'd probably want to mix your animations together instead. We have an example called FlareControls, and taking inspiration from that, you can build your own..!

Here's a gist with how you can do this, and play your animations correctly.

The night_idle animation is having trouble because you're not resetting the initial state of the animation after having played day_idle: in fact the node "Shooting Stars" is being set to 0 opacity in day_idle, and when going back to the night_idle that node is still 0 opacity. If you add a key to that animation at the beginning of night_idle setting the node's opacity to 1, everything should be playing correctly again.

luizwalber commented 4 years ago

Wow, thanks a lot, that resolve the problem totally

Thank you very much