Fabrizio
Fabrizio

Reputation: 1064

Call widget's own function outside it

I have this a stateful widget which could be in two situations, the first one is a Container with a first text in it, for example, "Register", the second one is a different colored container with a different text, for example "confirm". The Problem is that the transition between these two situations is done using an animation and it's not an on-the-fly logic for example:

color: isSituation1 ? Colors.blue : Colors.red.

it's actually something like this:

color: Color.lerp(Colors.blue, Colors.red, _animation1.value)  

and I have a function which runs when the user taps on the container which forwards the animation controller, like so:

_controller1.forward()

and this is a widget called let's say Button1

So in My HomePage stateful widget I have another button which should trigger the inverse process in the Button1 widget, so it would be:

_controller1.reverse()

I tried creating a function in the Button1 widget but then I cannot run it from outside. How could I do it if it's possible?

Upvotes: 1

Views: 1252

Answers (2)

Marcos Boaventura
Marcos Boaventura

Reputation: 4741

So basically you want to call methods of your CustomWidget from another widget. You can define ControllerClass that you will emit an instance when you create a new instance of your CustomWidget. This ControllerClass instance will hold the functions of your CustomWidget and you will be able to call them from outside.

By example a class that is a modal rounded progressbar that can be showed and hided from outise with a controller class. In this example a controller class is called ProgressBarHandler. I don't know if it is a better and the right approach but works.

class ModalRoundedProgressBar extends StatefulWidget {
  final String _textMessage;
  final double _opacity;
  final Color _color;
  final Function _handlerCallback;

  ModalRoundedProgressBar({
    @required Function handleCallback(ProgressBarHandler handler), //callback to get a controller
    String message = "",
    double opacity = 0.7,
    Color color = Colors.black54,
  })  : _textMessage = message,
        _opacity = opacity,
        _color = color,
        _handlerCallback = handleCallback;

  @override
  State createState() => _ModalRoundedProgressBarState();
}

class _ModalRoundedProgressBarState extends State<ModalRoundedProgressBar> {
  bool _isShowing = false;
  @override
  void initState() {
    super.initState();
    // init controller. 
    ProgressBarHandler handler = ProgressBarHandler();
    handler.show = this.show;
    handler.dismiss = this.dismiss;
    widget._handlerCallback(handler); // callback call.
  }

  @override
  Widget build(BuildContext context) {
    if (!_isShowing) return Stack();

    return Material(
      color: Colors.transparent,
      child: Stack(
        children: <Widget>[
          Opacity(
            opacity: widget._opacity,
            child: ModalBarrier(
              dismissible: false,
              color: Colors.black54,
            ),
          ),

          Center(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                CircularProgressIndicator(),
                Text(widget._textMessage),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void show() {
    setState(() => _isShowing = true);
  }

  void dismiss() {
    setState(() => _isShowing = false);
  }
}

  class ProgressBarHandler { 
      Function show; // will point to widget show method
      Function dismiss; // will point to another method.
   }


// ...in another external widget you can do...
// ... code your things and:
var controller;
var progressBar = ModalRoundedProgressBar(
  handleCallback: ((handler){ controller = handler; } ),);

//calling show method with controller
RaisedButton(
    onPressed: () { controller.show(); }
);


//calling dismiss method with controller
RaisedButton(
    onPressed: () { controller.dismiss(); }
);

Upvotes: 2

Miguel Ruivo
Miguel Ruivo

Reputation: 17746

It is possible, but you probably don't want to do that. You should either provide the AnimationController from the parent or structure your app to prevent such behaviors. Anyway, if you still want to go this way, here you have it.

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

class _HomePageState extends State<HomePage> {
  VoidCallback _reverseAnimation;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RaisedButton(
            child: Text('Reverse animation'),
            onPressed: () => _reverseAnimation(),
          ),
          Button1((controller) => _reverseAnimation = controller),
        ],
      ),
    );
  }
}

class Button1 extends StatefulWidget {
  final ValueChanged<VoidCallback> callback;
  Button1(this.callback);
  _Button1State createState() => _Button1State();
}

class _Button1State extends State<Button1> with SingleTickerProviderStateMixin {
  AnimationController _someAnimationController;

  void _reverseAnimation() {
    _someAnimationController?.reverse();
  }

  @override
  void initState() {
    super.initState();
    if (widget.callback != null) {
      widget.callback(_reverseAnimation);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: RaisedButton(
        child: Text('Start animation'),
        onPressed: () => _someAnimationController.forward(),
      ),
    );
  }
}

Upvotes: 0

Related Questions