Difference between myFunction, myFunction(), and myFunction.call() in Dart/Flutter - flutter

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)
myFunction
myFunction()
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
},
),
),
],
),
),
),
);
}
}

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.

Let's examine these cases and understand what they are:
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.
myFunction() and
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.

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

Related

The argument Future can not be assigned to the parameter type void Function()

I have a statefull widged where a button has
onPressed: _exportData,
where _exportData defined this way
Future<void> _exportData() async {
...
setState(() {
_message = 'A message'; // I want to use localization string here
});
}
I need to pass localization string into the above setState() and I do it this way
Lang.key(ctx, 'about')
i.e. I need to pass the context there, but when I try to make it like this
onPressed: _exportData(ctx), // the error here
and
Future<void> _exportData(BuildContext ctx) async {...
there is an error on onPressed
The argument Future can not be assigned to the parameter type
'void Function()'
How cat I pass context inside _exportData or how can I use localization string from inside the _exportData?
update: actualy there was no need to pass context into _exportData, because context is awailable inside state class
onPressed: () async { await _exportData(context) }
Make this change and let us know.
EDIT:
onPressed: _exportData(ctx), // the error here
You cannot do this because you can only pass a reference to a function in onPressed. If you want to pass arguments to a function, you'll have to do it the way I have shown above (although, it needs not to be asynchronous necessarily). Function name without the opening and a closing parenthesis is the reference while the one with the parenthesis is the actual function call.
The reason for this is that onPressed takes a reference/address to the function/pointer and not the actual function call. So if we had to pass the argument (context in your case) by your way, we will need to put the parenthesis around the argument making it a function call and not a reference hence executing it as soon as the build method runs and not when onPressed triggers it.
The way I showed you assigns an anonymous function to onPressed. See, there is no parenthesis in the end, so not executing it straight away and acting as a reference to a function. I put an async and await there because your _exportData function returns a future.
Hope that clears your doubt!

How can you call a void function in Flutter without passing a parameter?

I'm new to Flutter and am working through the intro course on Udacity. In one of the tasks, I was trying to follow the code and I can't make much sense of it. Here's the code from the solution of the project (I've cut and paste the parts that matter, and also legal disclaimer I do not own any of this code, it's from the sample Flutter Udacity project):
Widget build(BuildContext context) {
final input = Padding(
padding: _padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(...),
_createDropdown(_fromValue.name, _updateFromConversion),
],
),
);
}
void _updateFromConversion(dynamic unitName) {
setState(() {
_fromValue = _getUnit(unitName);
});
if (_inputValue != null) {
_updateConversion();
}
}
Widget _createDropdown(String currentValue, ValueChanged<dynamic> onChanged) {
return Container(
margin: EdgeInsets.only(top: 16.0),
decoration: BoxDecoration(...),
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Theme(...),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton(
value: currentValue,
items: _unitMenuItems,
onChanged: onChanged,
style: Theme.of(context).textTheme.title,
),
),
),
),
);
}
Here's where I'm stuck. _updateFromConversion requires an input parameter unitName. But when they call it, in _createDropdown, they don't pass any. So how does _updateFromConversion know what unitName is? Also, is _updateFromConversion executed before _createDropdown, or is it executed when the "onChanged" property of DropdownButton is set?
Second question: they're passing that function with return type void into _createDropdown, which is expecting ValueChanged. Shouldn't this throw an error?
If someone can explain the flow of this code and what I am missing I would greatly appreciate it.
Thank you!
You seem to be misunderstanding the assignment of a variable to a function for a function call.
Let me try to show it with example code.
void _updateFromConversion(dynamic unitName) {
print("Unit name: $unitName");
}
class SomeClass {
void Function(dynamic arg) myFunction;
}
void main() {
final c = SomeClass()..myFunction = _updateFromConversion;
print("Created c. Calling its function");
c.myFunction("foo");
print("Done");
}
When you run this code, you will see this printed:
Created c. Calling its function
Unit name: foo
Done
This shows that the _updateFromConversion function is not called when you create the SomeClass instance in SomeClass()..myFunction = _updateFromConversion;. This is only an assignment (it assigns to the field myFunction the value _updateFromConversion... yes, in Dart a function itself can be a value)!
You should know that because there's no () after the function name, and function invocation in Dart always must contain the list of arguments between () even if it's empty.
So, here's where the function is invoked:
c.myFunction("foo");
See? There's a list of arguments containing a single value, "foo". That's why the function then prints Unit name: foo, because the argument unitName takes the value "foo".
TL;DR
This is a function invocation:
c.myFunction("foo");
This is NOT:
c.myFunction;
Yashvin,
In dart function can be passed as parameter to other functions. This is usually used to pass callbacks for instance.
In example you provided, function _updateFromConversion is passed as parameter onChanged to another function _createDropdown.
In that function it will be assigned to onChanged listener of DropdownButton button.
Every time value of DropdownButton changes, this function will be invoked and will be passed selected value of DropdownButton.

Why do these two ways of specifying a callback for onPressed work?

I would like to learn about why these two ways to call the same method work (and why a third does not).
In the included sample app, the _MyHomePageState widget has these defined:
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
I notice that in the onPressed within the button, either of these will work:
onPressed: _incrementCounter,
OR
onPressed: ()=> _incrementCounter(),
But this will not work:
onPressed: _incrementCounter(),
Why is that?
It is because both working examples are function references, whereas the option that doesn't work is immediately calling the function. The first is a named function, and the second is an anonymous function that calls the named function. Since onPressed is expecting a function reference, you need to pass one of your references. If you call the function (which is what happens when you put the parenthesis after the reference name) you'll just pass the result of the function, which in your example is void.
For example, let's say we have the following:
int counter = 0;
void doThing() { ++counter; }
var doAnotherThing = () => doThing();
In this example, doThing and doAnotherThing both reference functions. The difference between them is that doAnotherThing references an anonymous function, whereas doThing is a named function. The anonymous function calls the named function. So, if we executed the following, how many times would doThing be called? What would be the value of counter?
doThing();
doAnotherThing();
The value would be 2. Note that since doAnotherThing is just a reference to a function, in our example you could do something like this:
var doAnotherThing = doThing;
However, if you call the function (instead of creating a reference to it) you would end up with a void reference:
var doAnotherThing = doThing(); // doThing returns void, so we get a void reference
Hope that clarifies things.
onPressed property should be passed as VoidCallback or void Function(). The void _incrementCounter() {} type is already a void Function() so you don't need to give extra braces to use it as property.
The extra braces will execute the function instead passing it on the Widget.

How to define onPressed property for button

What is the difference for these two variants? If the function _getCsvDocunent is just void type then what does it mean if we define onPressed like onPressed: () => _getCsvDocunent(), ?
FlatButton(
child: Text('Provide .csv data file'),
onPressed: _getCsvDocunent,
),
vs
FlatButton(
child: Text('Provide .csv data file'),
onPressed: () => _getCsvDocunent(),
),
In onPressed:_getCsvDocunent: onPressed gets an reference to _getCsvDocument passed to it. This way of passing a reference is only possible when the function to be passed is already defined and is compatible with onPressed.
In onPressed: () => _getCsvDocunent(): onPressed gets the value returned by
_getCsvDocunent after it is done executing.
In onPressed: () => _getCsvDocunent(): onPressed gets a reference to _getCsvDocument exactly similar to the case in the first bullet point, difference being, in this case a function reference (_getCsvDocument) is directly passed to onPressed instead of using an inline function to return a reference to _getCsvDocument.

Flutter how to retrieve value from button

new FlatButton(
child: new Text("933478476")
)
Here i created one button now i want to store the value of button in string.How to store the values from widget.
The FlatButton dosen't take any value and would not return either. What you could do is, pass a void callback that triggers your function that takes button click values as arguments.
Example:
//Define you function that takes click
void _onClick(String value) {
//do something
}
// Make you `FlatButton` like this
FlatButton(
onPressed: () => _onClick('12345678'), // a lambda void callback that calls your click function with value
child: Text('12345678'),
);
Hope that helped!