5

Passing state down to widgets is easy. I have a StatefulWidget that contains an animation with its controller. I need to be able to trigger the animation from another widget higher in my widget tree.

app

My MainApp should trigger the animation using a button.

enter image description here

As I understand AnimationController only has an imperative API. I can call controller.forward() or controller.reverse(). But to do this I need to expose the controller to my MainApp.

What I currently do is to keep a global variable of my state around.

class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      ...
      body: new LogoWidget(),
    );
  }

  _startAnimation() {
    _state.restartAnimation();
  }
}

_LogoWidgetState _state; // yuk!

class LogoWidget extends StatefulWidget {
  _LogoWidgetState createState() {
    _state = _LogoWidgetState();
    return _state;
  }
}

class _LogoWidgetState extends State<LogoWidget>
    with SingleTickerProviderStateMixin {
  Animation<double> animation;
  AnimationController controller;

  restartAnimation() {
    controller.value == 1.0 ? controller.reverse() : controller.forward();
  }
  ...
}

(full sourcecode here)

What is a better way to deal with this?

jkdev
  • 11,360
  • 15
  • 54
  • 77
oliver
  • 9,235
  • 4
  • 34
  • 39
  • Why does `LogoWidget` holds animation stuff if it's supposed to be controlled somewhere else ? – Rémi Rousselet May 11 '18 at 14:51
  • well...the animation-widget itself should just react to the app state...otherwise I'd have to pull out the `AnimationController` but the `MainApp` should not care about that at all. – oliver May 11 '18 at 15:24

1 Answers1

1

You don't need _LogoWidgetState _state; // yuk! out in the middle of nowhere, but you can try:

  1. create LogoWidget _myBody = LogoWidget(), and use that for your body:
  2. similarily, apply with final _LogoWidgetState _state = _LogoWidgetState()
  3. then call it as _myBody._state.restartAnimation()

Your sample, modified:

class MainApp extends StatelessWidget {

  LogoWidget _myBody = LogoWidget();  //<---

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      ...
      body: _myBody,  //<---
    );
  }

  _startAnimation() {
    _myBody._state.restartAnimation();  //<---
  }
}

class LogoWidget extends StatefulWidget {

  final _LogoWidgetState _state = _LogoWidgetState();  //<---

  _LogoWidgetState createState() {
    return _state;
  }
}

But if you think _myBody._state.restartAnimation() is too long, you can shorten it with:

class LogoWidget extends StatefulWidget {

  final _LogoWidgetState _state = _LogoWidgetState();  //<---

  void restartAnimation() {  //<---
    _state.restartAnimation();
  }

  _LogoWidgetState createState() {
    return _state;
  }
}

Then just use _myBody.restartAnimation()

Here's some relevant posts:

TWL
  • 6,228
  • 29
  • 65