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?
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);
}
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)})
I'm pushing a new route like so in my app
Navigator.of(context)
.push<void>(
FilterTypesPage.routeFullScreen(context),
).then(
(value) {
log('PAGGGE POPPED');
},
),
static Route routeFullScreen(BuildContext context) {
return MaterialPageRoute<void>(
settings: const RouteSettings(name: routeName),
builder: (_) => BlocProvider.value(
value: BlocProvider.of<FeatureBloc>(context),
child: const FilterTypesPage(),
),
fullscreenDialog: true);
}
for some reason log('PAGGGE POPPED'); doesn't get called on page close
I'd like to trigger a bloc event or a function when I close this page
You should just call
Navigator.pop(context, someData);
from your RouteSettings where someData is the data you want to pass from the RouteSettings to the former page.
Then from your former page, you can perform your event handling inside the then block. The value inside the then block is the data that was passed from the RouteSettings page.
Alternatively, you can also use async-await instead of then in your former page.
onPressed: () async {
final someData = await Navigator.of(cotext).push(.....);
// Now perform your event handling which will be invoked after you pop `RouteSettings` page.
}
Here I'm rendering an AlertDialog inside showDialog to display when error thrown from provider file. But that didn't work first time( Seems like stuck in catchError block, didn't execute future of catchError), Then I told to add <Null> after showDialog. That worked But How, What is changed, what it means?
Here is the code
if (_editedProduct.id == null) {
Provider.of<Products>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog<Null>( //Here is that Null I didn't understand
context: context,
builder: (ctx) {
return AlertDialog(
title: Text('ERROR'),
content: Text('Error Occured'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('I GOT IT'),
)
],
);
});
}).then((_) {
print('THEN');
setState(() {
isLoading = false;
});
Navigator.of(context).pop();
});
} else {
Provider.of<Products>(context, listen: false)
.updateProduct(_editedProduct.id, _editedProduct);
Navigator.of(context).pop();
}
}
Nb: isLoading true shows a circluarProgressIndicator()
From the Official Docs
Returns a Future that resolves to the value (if any) that was passed to Navigator.pop when the dialog was closed.
The place where you sent Null indicates the type of data the Future will return.
By using Null you are indicating that the Future won't return any data, this tell your program not to wait for the possible value that might be returned.
Suppose in your dialog user has to pick 2 numbers and you want the picked number to be returned to the place where you called the showDialog() function then you'll use int instead of Null.
Something like
showDialog<int>(...)
This is my students list screen, which has an add student button and upon taping save on the form it pops back to the students list.
class _StudentListState extends State<StudentList> {
var students = new List<Student>();
_getStudents() {
APIServices.fetchStudents().then((response) {
setState(() {
Iterable list = json.decode(response);
students = list.map((model) => Student.fromJson(model)).toList();
});
});
}
#override
void initState() {
super.initState();
_getStudents();
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton:_buidFloatingButton(),
appBar: _buildAppBar(context),
bottomNavigationBar: BottomAppBar(
// ================== REFRESH BUTTON ============================
child: FlatButton(
child:Icon(Icons.refresh),
onPressed: () {
_getStudents();
},
),
// =======================================================
),
// ================== ADD STUDENT BUTTON =======================
Widget _buidFloatingButton() {
return FloatingActionButton(
child:Icon(Icons.person_add),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => AddStudent())).then((value) {
_getStudents();
});
},
);
}
// =======================================================
}
}
I'm trying to refresh the student list after the form pop with this code, as seen in above code:
Navigator.push(context, MaterialPageRoute(builder: (context) => AddStudent())).then((value) => () {
_getStudents();
});
It's not refreshing the student list, but it will refresh if I tap the refresh button, both attempts are executing the same _getStudentes() function.
At the end of the save button I just do a:
Navigator.pop(context);
What am I missing?
Thanks.
You are returning a function with (value) => () { ... }.
The shorthand syntax in JavaScript and Dart differs a little in that regard, let me explain:
// Expression that returns a function.
() {
...
}
// You could also assign it to a variable:
final foo = () { return 3; };
// Now, you can call foo:
final bar = foo();
Thus, you are returning a function with (value) => () { ... }.
What you want to do instead is either of the following:
(value) => _getStudents()
// or
(value) {
_getStudents();
}
Learn more about functions in Dart.