onPressed ElevatedButton call automatically in Flutter - flutter

setState() or markNeedsBuild() called during build
Initially I want to know why onPress call automatically while rendering. After getting above issue I tried solving it in a number of ways like
WidgetsBinding.instance.addPostFrameCallback and Future.delayed . Code
class ShadowBtn {
static Widget drawBtn(double width, double height, String text, myFunc) {
return Container(
width: width,
height: height,
child: ElevatedButton(
///onPressed: () => myFunc,
onPressed: () {
myFunc();
},
child: Text(text),
),
),
);
}
}
Some solutions said to update onPress like
onPressed: () => myFunc
I simply call it like
ShadowBtn.drawBtn(321.w, 61.h, "SEND OTP", openScreen(context))
Where openScreen
openScreen(context) {
Future.delayed(Duration.zero, () async {
Navigator.of(context)
.pushNamedAndRemoveUntil('/dashboard', (Route<dynamic> route) => false);
});
// WidgetsBinding.instance.addPostFrameCallback((_) {
// Navigator.of(context).pushNamedAndRemoveUntil(
// '/dashboard', (Route<dynamic> route) => false);
// });
}
After trying all the solution. I didn't find any proper way to handle it.

openScreen function is called when passed as a parameter.
ShadowBtn.drawBtn(321.w, 61.h, "SEND OTP", openScreen(context))
Something like this should work:
ShadowBtn.drawBtn(321.w, 61.h, "SEND OTP", () => openScreen(context));
class ShadowBtn {
static Widget drawBtn(double width, double height, String text, void Function() myFunc) {
...
onPressed: () {
myFunc();
},
...
}
}

While passing the parameter you are actually not passing , instead making a function call, but function definition needs to be passed in the parameter.
so try using (){function()}, so now the definition is passed, and function is not called.
Change
ShadowBtn.drawBtn(321.w, 61.h, "SEND OTP", openScreen(context))
to
ShadowBtn.drawBtn(321.w, 61.h, "SEND OTP", (){openScreen(context)})

Related

How to call nested conditional void function with onPressed [flutter]

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);
}

Flutter DropdownButtonFormField not updating

I have an 'Add' link in my dropdown, which navigates to a form to do an add, and then sets the state afterwards, adding the new item to the dropdown. At least that's the way it was working; I tried to refactor to reuse this dropdown (it has some logic attached to it), and it no longer works...
Before, working:
The dropdown was added in a single stateful widget, and this code set the state:
TextButton(
child: Text(linkText),
onPressed: () {
Navigator.pop(aidingDropdownKey.currentContext!);
Navigator.push(context,
MaterialPageRoute(builder: (context) => linkDest))
.then((_) => setState(() {}));
},
)
Now, the dropdown is in its own StatefulWidget, and the DropdownMenuItem with the 'Add' link is in its own class. The code that tries to set the state looks like this:
TextButton(
child: Text(text),
onPressed: () {
if (poppee != null) {
Navigator.pop(poppee);
}
Navigator.push(context,
MaterialPageRoute(builder: (context) => dest))
.then((_) {
var afterClosed = afterLinkClosed;
if (afterClosed != null) {
afterClosed();
}
});
},
)
and
DropdownLabel(
"NTRIP Services",
linkText: "Add",
linkDest: const NtripForm(),
linkPoppee: widget.aidingKey?.currentContext,
afterLinkClosed: () {
_logger.d("after link callback called");
setState(() {});
},
)
Through logging, I can see my dropdown's build method is getting called, and the new menu item is created, but the UI isn't updating; the new item isn't showing.
Why wouldn't the UI update in this case?

Flutter Create a method including setState

I am trying to create a custom snackbar. It includes a "setState" executed after the snackbar dismisses. But the setStat function doesn't work (i.e., snackBarIsOn = false;). The snackbar pops up and dismisses after 2 seconds. It is also supposed to change "snackBarIsOn" to "true" but it doesn't happen. I tested it in the main code without the method. It works as it is supposed to. I am suspicious about the type of "actionAfterClosed".
Please help me.
customSnackBar(
context: context,
content: 'Enter a Title.',
label: 'Close',
textColor: Colors.white,
snackBarAction: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
setState(() {
snackBarIsOn = false;
});
},
actionAfterClosed:
setState(() {
snackBarIsOn = false;
})
);
void customSnackBar(
{required BuildContext context,
required String content,
required String label,
required Color textColor,
required VoidCallback snackBarAction,
required **VoidCallback** actionAfterClosed}) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(content),
duration: const Duration(seconds: 2),
action: SnackBarAction(
label: label,
textColor: textColor,
onPressed: snackBarAction,
),
))
.closed
.then((_) =>
actionAfterClosed
);
}
I believe this is the problem:
actionAfterClosed:
setState(() {
snackBarIsOn = false;
})
Here, you are telling flutter to run setState, and then use whatever it returns as a callback, so it expects setState to return a function to call after the snackbar closes, instead of calling setState itself.
To fix this, you must use a function literal like you did for snackbarAction:
actionAfterClosed:
() => setState(() {
snackBarIsOn = false;
})
The second problem I can find is here:
.closed
.then((_) =>
actionAfterClosed
);
You forgot the parenthesis! this way the function isn't running!
.closed
.then((_) =>
actionAfterClosed()
);

flutter:: Can I use conditional statements inside a button or widget?

This is my code. I want to get a variable called "favorite" from the Video0_02 file and change the color to yellow if true and to blue if false. I don't know how to write the code because the if statement doesn't work.
How do I solve this?
LearnLevelButton(
color: Colors.yellow,
onTap: () async {
await Navigator.push(
context, MaterialPageRoute(builder: (context) {
return Video0_02();
//Level newLevel = await gameBloc.setLevel(index + 1);
//Navigator.of(context).push(GamePage.route(newLevel));
}));
},
),
Try with this
LearnLevelButton(
color: favorite? Colors.yellow:Colors.blue,
onTap: () async {
await Navigator.push(
context, MaterialPageRoute(builder: (context) {
return Video0_02();
//Level newLevel = await gameBloc.setLevel(index + 1);
//Navigator.of(context).push(GamePage.route(newLevel));
}));
},
),
There are several ways you can use to handle the logic.
Use Dart's ternary operator. Something like this:
...
// Remember to check in case your `favorite` variable is null, set it to false
LearnLevelButton(
color: (favorite ?? false) ? Colors.yellow : Colors.blue,
...
),
Use a function to handle the logic:
LearnLevelButton(
color: getColor(favorite),
...
),
// The function
Color getColor(bool favorite) {
if (favorite ?? false) {
return Colors.yellow;
}
return Colors.blue;
}
Other way is immediate anonymous function or use switch/case in your separate function when there are more than 2 values to consider. But these 2 methods should be enough to get you started! ;)

Need to execute task after setState finish

I need to execute a task, after setState() method complete its whole job. I will describe my problem at below with some code.
I have a login screen and it has a widgets as below:
...
child: TextField(
errorText: getErrorStringOfEmail(),
...
),
...
child: MaterialButton(
onPressed: () => onClickLoginButton(),
...
),
"getErrorStringOfEmail" method is as below: (This method is called when the Textfield is updated by calling "setState()".)
String getErrorStringOfEmail(
if(loginEmailTextEditingController.text.toString() == "a") {
isValidLogin = false;
return 'Wrong password or email';
}
isValidLogin = true;
return null;
}
"onClickLoginButton" is as below:
void onClickLoginButton() {
setState(() {
//in here a boolean is changed to update Textfield.
});
if (isValidLogin) {
Navigator.pushReplacement (
context,
MaterialPageRoute(builder: (context) => HomeWidget()),
);
}
}
As you can see, "isValidLogin" boolean is assigned when Textfield widget is updated. However, getErrorStringOfEmail method is called after onClickLoginButton. I need to execute the following part,
if (isValidLogin) {
Navigator.pushReplacement (
context,
MaterialPageRoute(builder: (context) => HomeWidget()),
);
}
after getErrorStringOfEmail method is called. To achieve this, i need to call this part after setState update widgets.
How can i do this?