rive-app / rive-flutter

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

How can I run animation correctly before dispose ? #102

Closed jossephalvarez closed 3 years ago

jossephalvarez commented 3 years ago

Hello everyone.

I'm using rive with flutter on my app to cover widgets with loading animation while doing any action.

The scenario where I'm using is :

The Parent widget call the loading animation widget while getting response from server with "isBusy"(use to control this flow , I'm using Model View Model).

The animation flow has 2 steps :

  1. Loading spinner
  2. Animation with tick

So , now only appears the step 1.

I have this on the parent :

 body: model.isBusy
                  ? Center(child: LoadingAnimation())
                  // : Container(),

And my loadingWidget is :

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider_architecture_boilerplate/ui/helper_widgets/Styles.dart';
import 'package:rive/rive.dart';

const riveFile = 'assets/images/loading2.riv';

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

  @override
  LoadingAnimationState createState() => LoadingAnimationState();
}

class LoadingAnimationState extends State<LoadingAnimation> {
  bool darkTheme = false;
  Artboard? _artboard;
  RiveAnimationController? _animationController;

  @override
  void initState() {
    _loadRiveFile();
    super.initState();
  }

  void _loadRiveFile() async {
    try {
      final bytes = await rootBundle.load(riveFile);
      RiveFile rFile = RiveFile.import(bytes);

      setState(() => _artboard = rFile.mainArtboard
        ..addController(
          darkTheme
              ? _animationController = SimpleAnimation('dark')
              : _animationController = SimpleAnimation('light'),
        ));
    } catch (e) {
      print(e);
    }
  }

  void _onSucess() {
    _artboard!.artboard.removeController(_animationController!);
    _artboard!.addController(darkTheme
        ? SimpleAnimation('dark_tick')
        : SimpleAnimation('light_tick'));
  }

  @override
  void dispose() {
    _animationController!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: kcPrimaryDark,
      body: Center(
        child: _artboard != null
            ? Rive(
                artboard: _artboard!,
              )
            : Container(),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.check),
        onPressed: _onSucess,
      ),
    );
  }
}

As you see on my LoadingAnimation widget has the method: _onSucess, that generate an animation with tick(if you click or press on the FloatingActionButton) to achieve the correct flow of my animation.

But I would like to do the same without clicking on _onSucess, I want to get the correct flow inmediately after isBusy change. I've tried changing the dispose function like this


  @override
  void dispose() {
     _onSucess(); // ADD IT 
    _animationController!.dispose();
    super.dispose();
  }

But i allways see that step 1 of animation.

I've tried to do that using globalKey to execute the function from parent widget but I think this aproach is not the correct. I think is easier than it.

Could someone tell me how can i solve this problem ?

mjohnsullivan commented 3 years ago

The behavior you would like to see is that when isBusy is changed, the animation is played? Looking at the code, as soon as isBusy changes, the widget will be hidden or shown.

If you want to trigger the animation, you'll need to listen for a change in isBusy inside the stateful widget and trigger appropriately. Depending on how your model is implemented, you could use something like the Provider package to propagate changes in isBusy.

There's a good article on state management in the Flutter docs.

mjohnsullivan commented 3 years ago

Closing this for now; feel free to reopen if you're still having problems.