Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functionality of the advance method in Flare controller #217

Closed
jcairo opened this issue Jan 2, 2020 · 6 comments
Closed

Functionality of the advance method in Flare controller #217

jcairo opened this issue Jan 2, 2020 · 6 comments

Comments

@jcairo
Copy link

jcairo commented Jan 2, 2020

Hi guys. Thanks for the great tool.

I'm working on a Flutter animation that requires a portion of the animation to be replayed while a network request is made. From my understanding this requires a custom controller.

I'm trying to create a basic custom controller to get started but I'm having issues with the advance method.

It seems as though although I always return true from the method, its only called for the first render of the widget its contained in. Any idea why this would be?

class SubUnsubButton extends StatefulWidget {
  final Fight fight;
  SubUnsubButton({
    Key key,
    @required this.fight,
  }) : super(key: key);

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

class _SubUnsubButtonState extends State<SubUnsubButton>
    with TickerProviderStateMixin
    implements FlareController {
  // Add a state for the button
  FlareController flareController = FlareControls();
  String _buttonState;
  bool _waitingOnNetwork;
  @override
  initState() {
    super.initState();
    _buttonState =
        widget.fight.userIsSubscribed ? 'Subscribed' : 'Unsubscribed';
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          Container(
            margin: EdgeInsets.only(
                top: kBaseUnitSize * 2, bottom: kBaseUnitSize * 2),
            height: 30,
            child: FlatButton(
              splashColor: Colors.transparent,
              highlightColor: Colors.transparent,
              padding: EdgeInsets.all(0.0),
              child: FlareActor(
                "assets/animations/FightBell.flr",
                artboard: "RoundButtonV2",
                controller: this,
                fit: BoxFit.contain,
                sizeFromArtboard: true,
              ),
              onPressed: () async {
                // Check we have a user
                FirebaseUser user = Provider.of<FirebaseUser>(context);
                // If no user send to login screen
                if (user == null) {
                  Navigator.of(context).push(
                      MaterialPageRoute(builder: (context) => LoginScreen()));
                }
                if (!widget.fight.userIsSubscribed) {
                  attemptSubscribe(user.uid);
                } else {
                  attemptUnsubscribe(user.uid);
                }
              },
            ),
          ),
        ],
      ),
    );
  }
// Try to subscribe the user
  void attemptSubscribe(String userUID) {
    this.play('Sub');
    _waitingOnNetwork = true;
    Provider.of<FirestoreService>(context)
        .addSubscriptionForFight(
            widget.fight.eventUID, widget.fight.fightUID, userUID)
        .then((onValue) {
      widget.fight.incrementSubscriptions();
      // setState(() {
      //   //TODO: play animation to end
      // });
      Vibration.vibrate();
      _waitingOnNetwork = false;
    }).catchError((e) {
      //TODO: reset animation to beginning
      print(e);
      Provider.of<FlushBarService>(context).showBar(
          "You are not connected to the internet",
          "Connect to the internet to subscribe to this fight");

      _waitingOnNetwork = false;
    });
  }
  
// Try to unsubscribe the user
  void attemptUnsubscribe(String userUID) {
    this.play('Unsub');
    _waitingOnNetwork = true;
    Provider.of<FirestoreService>(context)
        .removeSubscriptionForFight(
            widget.fight.eventUID, widget.fight.fightUID, userUID)
        .then((onValue) {
      widget.fight.decrementSubscriptions();
      // setState(() {
      //   //TODO: play animation to end
      // });
      Vibration.vibrate();
      _waitingOnNetwork = false;
    }).catchError((e) {
      //TODO: reset animation to beginning
      _waitingOnNetwork = false;
      print(e);
      Provider.of<FlushBarService>(context).showBar(
          "You are not connected to the internet",
          "Connect to the internet to unsubscribe from this fight");
    });
  }

  // FLARE CONTROLLER 
  FlutterActorArtboard _artboard;
  double _animationTime = 0.0;
  bool _completed = false;
  bool _hasActiveAnimation;
  ActorAnimation _activeAnimation;

  @override
  bool advance(FlutterActorArtboard artboard, double elapsed) {
    // debugger(when: _hasActiveAnimation == false);
    print('Advance called');
    if (_hasActiveAnimation) {
      print(_hasActiveAnimation);
    }
    if (_hasActiveAnimation) {
      if (_animationTime > _activeAnimation.duration) {
        _hasActiveAnimation = false;
        // _activeAnimation = null;
        _animationTime = 0;
        _completed = true;
      } else {
        _animationTime += elapsed;
        _activeAnimation.apply(_animationTime, _artboard, 1);
      }

    }
    return true;
  }

  // Snaps an animation to the end
  // Used to setup the button on first render so the buttons aren't animated in when the screen loads
  void snapToEnd(String animationName) {
    var snapAnimation = _artboard.getAnimation(animationName);
    var animationDuration = snapAnimation.duration;
    snapAnimation.apply(animationDuration, _artboard, 1.0);
  }
  /// Add the [FlareAnimationLayer] of the animation named [name],
  /// to the end of the list of currently playing animation layers.
  void play(String animationName) {
    print("Play called");
    _activeAnimation = _artboard.getAnimation(animationName);
    _hasActiveAnimation = true;
    _completed = false;
  }

  @override
  void initialize(FlutterActorArtboard artboard) {
    _artboard = artboard;
    widget.fight.userIsSubscribed ? this.snapToEnd('Sub') : this.snapToEnd('Unsub');
    _hasActiveAnimation = false;
  }

  @override
  void setViewTransform(Mat2D viewTransform) {}

  @override
  ValueNotifier<bool> isActive;
}
@luigi-rosso
Copy link
Contributor

Hi @jcairo! That does seem strange. Can you post the matching .flr file? If it's private, could you email it to me at luigi@rive.app? I'll follow up here with findings!

@jcairo
Copy link
Author

jcairo commented Jan 2, 2020

Hey @luigi-rosso! Thanks for the quick reply.

File and link attached.

FightBell.flr2d.zip

https://rive.app/a/jonnyc/files/flare/fight-bell

@luigi-rosso
Copy link
Contributor

I see what happened!

You're inheriting from FlareController with the implements keyword which requires you to implement everything yourself (including the ValueNotifier<bool> isActive = ValueNotifier<bool>(true); which isn't initialized in your code).

Easy fix is to change it to a mixin and remove the ValueNotifier in your code:

Change definition to mixin:

class _SubUnsubButtonState extends State<SubUnsubButton>
    with TickerProviderStateMixin, FlareController {

Delete this:

  @override
  ValueNotifier<bool> isActive;

@jcairo
Copy link
Author

jcairo commented Jan 3, 2020

Hey @luigi-rosso!

That was it! Works perfect.

When I return false from the advance function it appears its never called again.

Is there a way to get the advance function to rerun again after false is returned? Is it bad practise to always return true?

Thanks again for the help. Much appreciated.

@luigi-rosso
Copy link
Contributor

Great! Yes, you can reactivate the controller by setting isActive.value = true.

@jcairo
Copy link
Author

jcairo commented Jan 3, 2020

Perfect!

@jcairo jcairo closed this as completed Jan 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants