Pass method as parameter to a widget
Asked Answered
A

2

55

I have a custom button widget:

class Button extends StatelessWidget {
  final String text;

  Button(this.text);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      child: SizedBox(
        width: double.infinity,
        child: RaisedButton(
          onPressed: () => {}, // Use the function from parent Widget
          child: Padding(
              padding: EdgeInsets.symmetric(vertical: 13),
              child: Text(
                text,
                style: TextStyle(fontWeight: FontWeight.bold),
              )),
          color: COLOR_BLUE,
          textColor: Colors.white,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
        ),
      ),
    );
  }
}

Then in parent Widget I want to pass a onPressed method to this button widget:

...
myMethod () => {
   // do some stuff
}
...
Padding(
    padding: EdgeInsets.only(bottom: 10),
    child: Button("Log in", myMethod),
),
...

How can I tell a button widget to use the myMethod for onPress?

Applejack answered 2/2, 2019 at 12:18 Comment(1)
see how it is done for a RawMaterialButtonAlyworth
W
77

Use VoidCallback type

Check the line comments on the code as well for more information:

class Button extends StatelessWidget {
  final String text;
  final VoidCallback callback; // Notice the variable type

  Button(this.text, this.callback);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      child: SizedBox(
        width: double.infinity,
        child: RaisedButton(
          onPressed: callback, // Simply put the function name here, DON'T use ()
          child: Padding(
              padding: EdgeInsets.symmetric(vertical: 13),
              child: Text(
                text,
                style: TextStyle(fontWeight: FontWeight.bold),
              )),
          color: COLOR_BLUE,
          textColor: Colors.white,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)),
        ),
      ),
    );
  }
}
  • By omitting the parentheses, you are passing the function itself as a parameter. If you added the parentheses, the function would be executed and you would pass the return of the function to the widget.

Then, initialize your widget:

Button("Log in", myMethod)

or

Button("Log in", (){
    print('something');
})
Wassyngton answered 2/2, 2019 at 12:19 Comment(6)
+1. You can also define your own typedefs for methods if you want one that takes parameters and/or returns a result: dartlang.org/guides/language/language-tour#typedefsTakao
Any reason for not putting () ? I put onPressed:(){ callback() ; }, and it still worked fine!?!Censorship
@Censorship when you add (), you are executing the function and getting the response when the code is run. When you skip the parentheses, you are passing the function as a parameter, as if it was a variable. So onPressed actually takes the function itself, not the result of the function. When you put () { callback(); } , you are passing a function with one line as a parameter, you are not executing it :) If you use callback(), onPressed will receive nothing (void) as value, because that what it would return.Wassyngton
Ahh.. thanks! So correct me if Im wrong... Skipping the parenthesis allows us to accept a returning value, (which I think is an Advantage!)... BUT otherwise () { callback(); } will always have a void return.Censorship
VoidCallback is the answer yes , thank you broHominy
I get The values in a const list literal must be constants. Try removing the keyword 'const' from the list literal. when I do this. Any ideas?Cerebrate
A
69

There are a few predefined types that already exist.

VoidCallback

If you want to create a parameter something like this:

onPressed: () { },

Then you can define it in your class like so:

class MyWidget extends StatelessWidget {

  MyWidget({Key key, this.onPressed}) : super(key: key);

  final VoidCallback onPressed;

  // ...
}

Notes

The typedef is defined in the source code like this:

typedef VoidCallback = void Function();

The asynchronous version is AsyncCallback.

typedef AsyncCallback = Future<void> Function();

ValueSetter

If you want to create a parameter something like this:

onPressed: (value) { },

Then you can define it in your class like so:

class MyWidget extends StatelessWidget {

  MyWidget({Key key, this.onPressed}) : super(key: key);

  final ValueSetter<String> onPressed;

  // ...
}

Notes

The typedef is defined in the source code like this:

typedef ValueSetter<T> = void Function(T value);

If you want to specify that the function only gets called when there is a change then use ValueChanged instead.

typedef ValueChanged<T> = void Function(T value);

The asynchronous version is AsyncValueSetter.

typedef AsyncValueSetter<T> = Future<void> Function(T value);

ValueGetter

If you want to create a parameter something like this:

onPressed: () => value,

Then you can define it in your class like so:

class MyWidget extends StatelessWidget {

  MyWidget({Key key, this.onPressed}) : super(key: key);

  final ValueGetter<String> onPressed;

  // ...
}

Notes

The typedef is defined in the source code like this:

typedef ValueGetter<T> = T Function();

The asynchronous version is AsyncValueGetter.

typedef AsyncValueGetter<T> = Future<T> Function();

Define your own type

As you can see from all of the examples above, everything is just a typedef for a Function. So it is easy enough to make your own.

Say you want to do something like this:

onEvent: (context, child) => value,

Then you would make the typedef like this:

typedef MyEventCallback = int Function(BuildContext context, Widget widget);

And use it like this:

class MyWidget extends StatelessWidget {

  MyWidget({Key key, this.onEvent}) : super(key: key);

  final MyEventCallback onEvent;

  // ...
}

See the documentation for more.

Anthropophagite answered 7/6, 2020 at 11:59 Comment(2)
Say I create a CustomTextField that returns a TextField. How would you make a constructor for the onChanged property and pass it correctly? I'm trying to make a constructor for the CustomTextField that passes the function correctly to the TextField inside. Things would be like onChanged: onChanged.Telemetry
@IvánYoed, You can check out how TextField does it and do the same thing. They have ValueChanged<String>? onChanged where ValueChanged is defined to be void Function(T value).Anthropophagite

© 2022 - 2024 — McMap. All rights reserved.