in Flutter I have made a custom button that does a small animation when pressing it. The issue is that when I add a VoidCallBack function, which is a parameter that I give to the button widget, to the same onTap, then the function does not get executed whilst the animation does.
I did find this question ( how do we execute two function in single button pressed in flutter) that seems to be similar to mine but I have tried the suggestions and they do not work for me.
Here is a code snippet of when Im trying to use the button widget:
MyButton (
onTap = () {
print(_isSelected);
setState(() {
_isSelected[0] = !_isSelected[0];
});
},
)
Also I dont know if it makes a difference but Ive tried it both with and without the setState part.
Then in the button itself I do:
onTap: () {
widget.onTap;
setState(() {
_clicked = !_clicked;
});
},
I also tried to do this, like in the other stackOverflow question which had the same result:
onTap: () => [
widget.onTap,
{
setState(() {
_clicked = !_clicked;
})
}
],
Though it does work if I only use the parameter: onTap: widget.onTap,
This is usually how I do it.
onTap: () {
widget.onTap();
your_function_here();
},
Declare function variable:
final Function onTap;
and call widget.onTap with round brackets as below:
onTap: (){
widget.onTap();
setState(() {
_clicked = !_clicked;
});
}
You need to declare a constructor which takes onTap function as a parameter.
class MyButton extends StatelessWidget {
final Function onTap;
const MyButton ({Key? key, this.onTap,}) : super(key: key);
}
Since the onTap parameter needs a function as argument, you simply write a function that runs your functions.
MyButton(
onTap: () {
your_function1();
your_function2();
},
)
Related
I have a customized flutter stepper widget and I want to having conditional back button when I press the backbutton in appbar, it will be back on previous step, just like this
onPressed: () {
if (currentStep != 0) {
onStepCancel;
} else {
Navigator.pop(context);
}
},
and somehow onStepCancel is can't be call because it has a value of final VoidCallback? onStepCancel, I put another function inside another function A.K.A nested, I want to use this widget in another class, so it can be simplify by only putting the void function inside onStepCancel
CustomStepper(
. . .
currentStep: controller.currentStep.value,
onStepContinue: controller.increment,
onStepCancel: controller.decrement,
);
the void function that fill with decrement function of currentStep will be proceed inside onStepCancel and when user click on back button with condition currentStep != 0 it will show the previous step and when user is reaching currentStep == 0 it will back to previous page Navigator.pop(context);, but the problem is onStepCancel can't be VoidCallback? because the conditional function is already the void function itself and it can't be returned VoidCallBack? inside it, so how can I call onStepCancel function inside conditional case with onPressed, this is the widget function where I put it:
Widget _buttonBack(int stepIndex, BuildContext context) {
return IconButton(
onPressed: () {
if (currentStep != 0) {
onStepCancel;
} else {
Navigator.pop(context);
}
},
icon: SvgPicture.asset(
Images.backArrowButton,
color: ColorResources.brandHeavy,
),
);
}
and I want to put Widget _buttonBack inside:
Scaffold(
appBar: AppBar(
. . .
leading: _buttonBack(currentStep, context),
. . .
);
edit:
onStepCancel is fullfil with void function that I call from controller:
void decrement() => currentStep--;
void decrement() is the function for getting back to previous Step and I call decrement() function in onStepCancel just like this:
onStepCancel: controller.decrement
Inside my CustomStepper class, onStepCancel will pass decrement to onPressed(), I have try few way to put conditional onPressed(), with this:
onPressed: () => currentStep != 0 ? onStepCancel : Navigator.pop(context),
and this
onPressed: () {
if (currentStep != 0) {
onStepCancel;
} else {
Navigator.pop(context);
}
},
both conditional doesn't work, but the funny thing is when I call onStepCancel without conditional case, like this:
onPressed: onStepCancel
it show no problem and it works well, so the point is, on step cancel will only work well without nested function, I only could call it with onStepCancel without any conditional function, how to call nested function? is it needed to be in any other form instead of function or else?
if onStepCancel is a callback, you should call it instead of returning it:
if (currentStep != 0) {
onStepCancel();
} else {
Navigator.pop(context);
}
When a widget icon button is pressed, I want to change the button's icon, have setState rebuild the widget so the changed button icon is visible, then run a function:
bool _showPauseIcon = false;
void doSomething() {
print("doSomething()");
}
.
.
.
IconButton(
icon: _showPauseIcon ? Icon(Icons.pause) : Icon(Icons.play),
onPressed: () {
_showPauseIcon = true;
setState (() { });
doSomething();
},
)
doSomething() appears to be called before setState rebuilds the widget, so the modified icon only appears after doSomething() has been called - I need it to happen before doSomething() is called. I looking for the simplest possible solution.
SetState is only for the updating the widget on Current page.. So no need to call function inside the setstate.
IconButton(
icon: _showPauseIcon ? Icon(Icons.pause) : Icon(Icons.play),
onPressed: () {
setState (() {
_showPauseIcon = true;
});
doSomething();
},
)
Solved:
onPressed: () {
setState(() {
_showPauseIcon = true;
});
SchedulerBinding.instance.addPostFrameCallback((_) {
doSomething();
});
}
Example: a widget, that accepts a Function() function as a parameter. Now i want to execute code inside this widget, when function is called somewhere above in hierarchy, before or after it. Is this possible?
Here is the button widget
class AppButton extends StatelessWidget {
final Function()? onPressed;
const AppButton(
{Key? key,required this.onPressed}): super(key: key);
#override
Widget build(BuildContext context) {
return ElevatedButton(onPressed: onPressed);
}
Here is how I call it
AppButton(
onPressed: () {
[some code]
},
),
Now the question is: is it possible to call a code, like setState inside the AppBar, before or after [some code] gets executed on tap? Like to inject code into onPressed inside AppButton?
Yes, it's possible, You should take the Function() function as an argument and in the Widget(e.g ElevatedButton in the property onPressed: function)
and in the Constructor get the function.
so, when you calling the widget it will ask for the function as well their you will give the function like this
YouWidgetName(function: () {
// here will be your code for that particular area.
}),
I have a Flutter stateful widget inside a Stepper widget, and it looks like this:
The first step of the stepper widget has another widget as its content, and that widget is a stateful widget as below:
import 'package:flutter/material.dart';
class ConductorStart extends StatefulWidget {
const ConductorStart({
Key? key,
required this.continued,
}) : super(key: key);
final VoidCallback continued;
#override
ConductorStartState createState() => ConductorStartState();
}
class ConductorStartState extends State<ConductorStart> {
int _currentStep = 0;
bool _pressedButton = false;
void tapped() {
setState(() => _pressedButton = true);
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
if (!_pressedButton)
ElevatedButton(
onPressed: () {
tapped;
widget.continued;
},
child: const Text('Continue'),
)
else
const SizedBox.shrink(),
],
);
}
}
The ElevatedButton has two functions when called: tapped, and widget.continued. tapped should make the button disappear. and widget.continued is a function from the parent widget that is supposed to make the parent widget to continue to step 2 when clicked. Currently the two functions have a warning of Avoid using unnecessary statements, and they do not get executed properly. When I click the continue button. Nothing happens. What did I do wrong here?
I'll totally go with Pat9RB. Just posting it here as an answer for others to quickly find the relevant issue.
Adding just definitions helps for adding a callback parameter in the constructor.
For Eg.: onPressed: myOnPressed;
where, onPressed needs a VoidCallback, and myOnPressed is a VoidCallback argument.
But, you need to call the function in order to execute it from any other function body.
Either call using .call() method, like, tapped!.call(); or append with callback syntax (), like, tapped();
I have an InkWell which uses onTap to perform some actions. When the button is tapped, I like an indicator to be shown (in case the action is long-running). However, the setState in the InkWell does not trigger its children to be re-rendered. The code is as follows:
class PrimaryButtonState extends State<PrimaryButton> {
bool _apiCall;
Widget getWidget() {
if(_apiCall) {
return new CircularProgressIndicator();
} else {
return Text(
widget.label,
);
}
}
#override
Widget build(BuildContext context) {
final List<Color> colors = //omitted
return InkWell(
child: Container(
decoration: // omitted
child: getWidget(), // not updated when _apiCall changes !!!!!
),
onTap: () {
setState(() {
_apiCall = true;
});
widget.onTab(context);
setState(() {
_apiCall = false;
});
}
);
}
}
How can I solve this that getWidget returns the correct widget dependent on _apiCall?
EDIT:
The widget.onTap contains the following:
void performLogin(BuildContext context) {
final String userName = _userName.text.trim();
final String password = _password.text.trim();
UserService.get().loginUser(userName, password).then((val) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => MainLayout()));
}).catchError((e) {
// omitted
});
}
it is passed with the widget:
class PrimaryButton extends StatefulWidget {
final bool isPrimary;
final String label;
final Function(BuildContext context) onTab;
PrimaryButton(this.label, this.isPrimary, this.onTab);
#override
State<StatefulWidget> createState() => PrimaryButtonState();
}
My main concern is, that the given onTap method should not know it is "bound" to a UI widget and therefore should not setState. Also, as this is a general button implementation I like it to be interchangeable (therefore, onTap is not hardcoded)
It looks like your problem is because you are calling setState() twice in your onTap() function. Since onTap() is not an async function it will set _apiCall = true in the first setState, then immediately run widget.onTab(context) and then immediately perform the second setState() to set _apiCall = false so you never see the loading widget.
To fix this you will need to make your onTab function an async function and await for a value in your onTap function for your InkWell:
onTap: () async {
setState(() {
_apiCall = true;
});
await widget.onTab(context);
setState(() {
_apiCall = false;
});
}
This will also let you use the results of your onTab function to show errors or other functionality if needed.
If you are unsure how to use async functions and futures here is a good guide on it that goes over this exact kind of use case.