0

I've noticed that when I have a widget/class that takes Functions as arguments, when it comes time to call those functions, it can be done one of three ways (that I know of):

(Consider a Function, myFunction)

  1. myFunction

  2. myFunction()

  3. myFunction.call()

But the weird thing is, I've noticed that when using option 1), it will (ONLY SOMETIMES) not work and require the use of option 2 or 3 in order to work.

Why is that?

Specific example (I've verified the inconsistent calling behaviour with print debugging in the parent):

class SoundPickerTile extends StatelessWidget {
  final Sound sound;
  final Function() checkboxCallback;
  final Function() soundPlayCallback;

  SoundPickerTile(
      {required this.sound, required this.checkboxCallback, required this.soundPlayCallback});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: checkboxCallback, // <--------------- Function executes in parent
      child: Container(
        color: sound.isIncluded ? Colors.lightGreen.withAlpha(100) : Colors.white,
        child: Padding(
          padding: EdgeInsets.fromLTRB(20, 10, 0, 10),
          child: Row(
            children: [
              Expanded(
                child: Text(
                  sound.shortTitle,
                ),
              ),
              Expanded(
                child: IconButton(
                  icon: Icon(Icons.play_circle_outline),
                  onPressed: () {
                    print("this line of code was reached"); // this works
                    soundPlayCallback; // <--------------- Function *does not* execute in parent
                  },
                ),
              ),

            ],
          ),
        ),
      ),
    );
  }
}
Nerdy Bunz
  • 6,040
  • 10
  • 41
  • 100

3 Answers3

2

They are all the same object, but with two different behaviours.

myFunction is your function but shaped like an object. So you can pas this around as an argument for onTap (which takes a function like that as argument). That's also why it's run in the parent, that's how it's supposed to work. It gets tossed around as an object and the parent calls () on it.

The reason it's not being executed below is because there you are simple putting the function down as an object. Much like you earlier passed it as an object, right now you're just saying hey here's the function, but I'm not gonna do anything with it.

myFunction; -> no ()

So in order for it to work you need to use number 2. or 3.

myFunction() -> This will call your object (your function) and run it's code, contrary to the previous mentioned example where you just lay the object down. The () is important!

Now the difference between 2. and 3. is.. almost nothing!

() actually does the .call() in the background, however if you have a myFunction that's possibly null, then you can do something like this:

myFunction?.call();

This will only call the function if it's not null, else it will not do anything.

Hope that's clear, also try to define a return value when specifying callbacks, this will make you understand passing around functions quicker. And have a look at typedefs, they are basically signatures for specific functions (VoidCallback for example). Once you grasp that concept passing around functions will become a breeze! Goodluck.

Coda Veto
  • 896
  • 1
  • 8
  • 13
2

Let's examine these cases and understand what they are:

  1. myFunction:

This one is just a reference to a function. When you write this, the function is not called. Instead, we may be giving the function to somebody else, who may or may not call it in the future.

  1. myFunction() and
  2. myFunction.call():

Call the function. When you write this, the function executes, whatever side effects it has happen and it returns whatever it returns.

So 2 and 3 are exactly the same. So let's focus on the difference between 1 and 2. That is, myFunction and myFunction().

myFunction is a reference to a function. Kind of like the i in int i = 5. You can call it by adding () at the end, or calling its .call() method. Similarly, you can do i.isEven with i.

Example: I'm calling the function. Whatever side effects it has will happen before the next line.

myFunction(); 
myFunction.call(); // same thing

Alternative to calling myFunction, you can also pass it around as a reference to a function. This is like passing around an int. You are passing the reference to this function to some other place in your code. It may or may not be called in the future. You can call it, you can throw it away, it's your call.

Example: Dear button, here's a function. Call it when you're pressed.

Button(
  onPressed: myFunction, 
)

Example: Dear button, here's a little function (the () =>) that returns a reference to myFunction. When button calls this, it receives the reference to myFunction. However, the button just calls what you give to onPressed. It does not care about what you return from that. Therefore, you return the reference myFunction but it is never called, it's thrown away.

Button(
  onPressed: () => myFunction,
  onPressed: () {return myFunction;} // same thing
)

Example: Dear button, here's a little function that calls myFunction() and returns whatever it returns. So myFunction is actually called when the button is pressed.

Button(
  onPressed: () => myFunction(),
)

Example: Dear button, here's a little function that does not return anything. One of its lines is myFunction;, which does not do anything, it's like writing 1;, nothing is done with it. Therefore myFunction is not called.

Button(
  onPressed: () { myFunction; }
)

Any possible usage of myFunction should fall into one of these categories. The descriptions of the examples should help in understanding what that usage means.

Gazihan Alankus
  • 11,256
  • 7
  • 46
  • 57
1

In the GestureDetector we use:

onTap: checkboxCallback, 

without the (), since that would call the function immediately, we don't want to call the function, we just want to pass a reference to the function on what should happen when onTap is called.

Then with:

onPressed: () {
                    print("this line of code was reached"); // this works
                    soundPlayCallback; // <--------------- Function *does not* execute in parent
                  },

Since were are not using () it's not being called it's just a reference to the function. Instead, what you can do is: onPressed: soundPlayCallback; Or add the ()

Edit.

What is the difference between calling the function without parentheses and with parentheses

MendelG
  • 14,885
  • 4
  • 25
  • 52