I was playing around with functions and tried to create a new widget:
newWidget(Function onTap){
return InkWell(
...
onTap: () => onTap
);
}
When tried to use it, I found that giving the onTap as onTap: () => onTap, and using it as onTap: () => onTap(), performed differently. And there's other ways to use the onTap property.
So what's the difference between:
onTap: () => onTap
onTap: () => onTap()
onTap: onTap()
onTap: () => func
This one point the widget to func function and tells it run it when tapped. Like you give it the address of the function and tell it to go there in case someone taps.
onTap: () => func()
onTap: func()
These two basically do the same thing and give onTap property the value returned from the func(). These ones however, takes onTap to the address instead of pointing it to it. These will run immediately after build completes.
You can simply call onTap: func if you don't have to pass it any parameters.
Otherwise call onTap: (param1, param2, ...) => func(param1, param2, ...)
You can also use it like:
onTap: funcationName //with out brackets
Related
In Flutter, in order to navigate to a new page, the typical idiom is:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SomeOtherPage());
If this is, say, the callback of a button, this means a new MaterialPageRoute object will be created every time the button is pressed. Is this how it should be? Could we reuse the same MaterialPageRoute object, as follows?
final route = MaterialPageRoute(builder: (context) => SomeOtherPage());
// ...
TextButton(
onTap: () => Navigator.of(context).push(route),
child: ...,
)
Would there be any difference in behaviour or performance?
TL;DR: Trying to pass a function call to a custom AlertDialog. AlertDialog needs to be popped after the function -> can't make it work.
I've created a custom AlertDialog to use throughout my app. It looks something like this:
customAlertDialog({required context, buttonAction}){
showDialog(context: context, builder: (context) => AlertDialog(
title: Text("example title", style: TextStyle(color: AppTheme.colors.white),),
content: const Text("some content"),
actions: [
TextButton(onPressed: () {Navigator.of(context).pop();}, child: Text(
"Abbrechen",
style: TextStyle(
color: AppTheme.colors.white),
),),
TextButton(
child: Text("do something",
style: TextStyle(
color: AppTheme.colors.lightRed),
),
onPressed: buttonAction)
],
),);
}
The customAlertDialog takes a function call as an argument (here named buttonAction) for the last TextButtons onPressed action. It works fine when I pass:
buttonAction: () => deleteUser(context)
The problem is that this does not work in combination with a pop method. In the following only deleteUser will be called:
buttonAction: () => [deleteUser(context), Navigator.of(context).pop()]
the same if written like this:
buttonAction: () {deleteUser(context), Navigator.of(context).pop()}
I guess that the context of the customAlertDialog itself needs to be popped. So I've tried the following in the customAlertDialog (buttonAction contains () => deleteUser(context):
onPressed: () => [buttonAction, Navigator.of(context).pop()]
Now it only pops the dialog, probably because dart cannot interpret the buttonAction. So I've searched to find a way to pass only the function that should be called but wasn't successful doing so.
So my question is: How can I pass a function and still pop the dialog?
EDIT:
As #SlowDeepCoder mentioned the problem could have been that the Navigator.pop() method throws the context from the stack before deleteUser() has finished and therefore it doesn't work. It was tried to be fixed with the following but it did not work:
buttonAction: () async{ await deleteUser(context); Navigator.of(context).pop(); }
This is because you call pop() on a Navigator from an incorrect BuildContext. So instead of
buttonAction: () {
deleteUser(context);
Navigator.of(context).pop();
}
you have to do something like this:
buttonAction: (innerContext) {
deleteUser(innerContext); // not sure if you need the innerContext here. Depends on your other app code
// deleteUser(context); // use this line if the above does not work
Navigator.of(innerContext).pop();
}
together with
TextButton(
child: Text("do something"),
onPressed: () => buttonAction(context),
)
I also would recommend you to give bot of your BuildContexts (customAlertDialog and showDialog) different names.
You do not always have to use arrow function to pass a custom function.
You can simply pass the function as
buttonAction: (){
deleteUser(context);
Navigator.of(context).pop();
}
I am working on a music player, and on pressing next button I want to perform two functions one is to add song to the queue and another function is used to trigger the plugin method of 'addSong'. On button pressed I wish to execute two functions which are '_pageManager.addSong' and 'addSongToQueue'.
This is the code
ValueListenableBuilder<bool>(
valueListenable: _pageManager.isLastSongNotifier,
builder: (_, isLast, __) {
return IconButton(
icon: Icon(Icons.skip_next),
onPressed: (isLast)
? _pageManager.addSong
: _pageManager.onNextSongButtonPressed;
);
}),
I see your logic
I might suggest to create a function
void onAddSong() {
_pageManager.addSong();
// addSongToQueue()
}
Then call this
onPressed: (isLast)
? this.onAddSong
: _pageManager.onNextSongButtonPressed;
);
I'd suggest you to think in the scalability of this widget with that isLast #param, is it ok?
Don't use a ternary, and instead just use a function body.
onPressed: () {
function1();
function2();
}
You can use if in the oppressed function like this
onPressed: (){
if(isLast){
_pageManager.addSong
}else{
_pageManager.onNextSongButtonPressed;
}
}
or use it like this if you have to check only one condition
onPressed: () =>
isLast ? _pageManager.addSong
:
_pageManager.onNextSongButtonPressed;
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.
Code will explain all:
class ResultOverlay extends StatefulWidget {
final bool _isCorrect;
VoidCallback _onTap;
ResultOverlay(this._isCorrect, this._onTap);
......
......
}
Its state class:
class ResultOverlayState extends State<ResultOverlay>{
#override
Widget build(BuildContext context) {
........
child: new InkWell(
onTap: () => widget._onTap,
.....
.....
}
Passing of callback function:
new ResultOverlay(isCorrect, () {
() => CallAnswerPage();
})
What I am missing here?
This is a more general answer for future viewers.
Callback types
There are a few different types of predefined callbacks:
final VoidCallback myVoidCallback = () {};
final ValueGetter<int> myValueGetter = () => 42;
final ValueSetter<int> myValueSetter = (value) {};
final ValueChanged<int> myValueSetter = (value) {};
Notes:
VoidCallback is an anonymous function that takes no arguments and returns no value.
ValueGetter is an anonymous function that returns a value, which you provide for someone who wants to get it.
ValueSetter is an anonymous function that takes a value as an argument, which you can use to set some other value.
ValueChanged has the same function signature and is used the same way as ValueSetter, but its name emphasizes that the given value actually changed (and was not just set with the same value again).
See this answer for more details.
Ways to write the callback
Good
When you are asked to provide a callback to an API, you can directly write the callback:
onPressed: () {},
Or you can supply the callback variable name (without parentheses):
onPressed: myVoidCallback,
Less good
It would be unnecessarily verbose to use both forms (but you could if you included the parentheses after the variable name):
onPressed: () {
myVoidCallback();
},
This one is equivalent (but also unnecessarily verbose):
onPressed: () => myVoidCallback(),
Just use one of the "Good" forms from above.
Still good
The exception would be if you wanted to do something like call a value setter when the parameter is only asking for a void callback:
onPressed: () => myValueSetter(42),
I was not passing the callback correctly. Here is the correct syntax:
new ResultOverlay(
isCorrect,
() => callAnswerPage()
)
So silly mistake :)
Everything is fine beside how you use it.
Change
onTap: () => widget._onTap,
to
onTap: widget._onTap,
inside your State class