Flutter DropdownButtonFormField not updating - flutter

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?

Related

how to send object when i pop? (flutter)

Context: I'm creating an app for sales and when I'm on the sales screen I have a button (Add item), this button takes me to the items grid.
my difficulty is turning on the sales screen with the selected item.
onTap: () => isSelecting ? Navigator.of(context).pop() : null,
You can use this
Navigator.pop(context, yourObject);
onPressed: () async {
newProduto = await Navigator.of(context).push(MaterialPageRoute(builder: (context) => ProdutosPage(selecting: true)));
setState(() {
produtos.add(newProduto);
});
}

adding as favorite conditionally in Flutter

I am using the favorite_button Widget to add items to the list of favorites.
For that matter, I have a listview and for each row, I added the option to add it as a favorite. I also have a condition in the backend that if the number of favorites is more than 10, the responsecode equals to 2 and then shows a dialogbox in the flutter and does not add it to the favorite.
Everything works perfectly. The only problem that I have is that in conditions with more than 10 favorites, when I click, it marks as favorite and then shows the dialog box but I could not find a way to undo this action. However, it does not add it to the list (when I refresh the page, it shows as unmarked). How could I unmark it from marked as favorite, for example, when user closes the showDialog?
Any other approach is also appreciated like simulating the onpressed action to undo the action of favoriting.
Thank you in advance.
StarButton(
isStarred: UserFavorite
.Users!
valueChanged: (_isStarred) {
if (_isStarred == true)
setState(() async {
var resp =
await add(
widget.oauth,
widget.Id,);
if (resp.responseCode ==
2) {
await showDialog(
context: context,
builder:
(alertDialogContext) {
return AlertDialog(
title: Text(
'Limit Reached'),
content: Text(
'You are allowed to add up to 10 sensors as your favorite.'),
actions: [
TextButton(
child: Text(
'Ok'),
onPressed:
() {
Navigator.pop(
alertDialogContext);
},
),
],
);
},
);
}
});
else
setState(() {
delete(
widget.oauth,
widget.Id,
);
});
},
)

How to Navigator.push() from a DropdownMenuItem's onTap and await the returned value?

Following on from Unhandled Exception: type '_DropdownRouteResult<int>' is not a subtype of type 'int?' when returning value from Navigator.pop() as I still haven't resolved the issue.
I have a DropdownFormField which I am dynamically populating from a db via a Provider. I would like to add a DropdownMenuItem which, when selected, pushes a new route (for inserting a new record into the db).
The route returns the id of the newly-inserted db record when popped, and I would like to set the new value as the value of the DropdownFormField.
Implementing the new item with a TextButton child and pushing in the buttons' onPressed results in expected push/pop behaviour, but is styled inconsistently from the "normal" items, and does not close the dropdown (which makes sense as the button is pressed, but the DropdownMenuItem is not tapped). Tapping outside the dropdown after popping reveals that the dropdown's value is updated correctly.
DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
Implementing the new item with a Text child and pushing in the DropdownMenuItem's onTap (which seems like the correct approach) results in an immediate attempt to return the value, disrespecting the asynchronous nature of the onTap and resulting in the exception from my previous question. Breakpoint debugging without specifying the type of newValue shows that it is immediately assigned the Future/_DropdownRouteResult<int>, rather than awaiting its returned int.
DropdownMenuItem<int>(
onTap: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: const Text('Add New Manufacturer'));
I have no idea why await is being respected in TextButton.onPressed but not in DropdownMenuItem.onTap
I don't know if it's the right way, since it relies on null as a placeholder value and I can't see how you'd easily scale it beyond a single DropdownMenuItem with special behaviour (as unlikely as it seems that you'd want to) but after reading this for the third time I finally grokked a solution - return null as the value, and perform navigation/assignment in the DropdownButtonFormField's onChanged
final brokenAddNewTeaProducerButton = DropdownMenuItem<int>(
value: null,
child: const Text('Add New Manufacturer'));
return DropdownButtonFormField<int?>(
value: _selectedValue,
items: [brokenAddNewTeaProducerButton] + teaProducerListItems,
onChanged: (value) async {
if (value == null) {
final newTeaProducerId = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newTeaProducerId;
});
} else {
setState(() {
_selectedValue = value;
});
}
},
hint: Text('Select a manufacturer'),
);
}
**You can try this statfulBuilder**
StatefulBuilder(builder: (context,state){
return DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
var newValue = await Navigator.push(context,
AddNewTeaProducerRoute());
state(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
}),

How stop SetState flutter

I work on flutter project . when i click to modify icon to edit name for example ==> the screen is roaleded automatically . How i can stop refresh screen after click on edit button ?
this piece of my Form code :
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Adresse email :',
style: TextStyle(
color: Color(0xFF4053FCF),
fontSize: 16,
fontWeight: FontWeight.w600
),
),
IconButton(
icon: Icon(CommunityMaterialIcons.pencil,
color: Colors.grey,
),
onPressed: () {
emailNode.requestFocus();
setState(() {
enableemail = true;
});
})
],
),
void editUserProfile() async {
setState(() {});
// if (_formKey.currentState.validate()) {
String name = _nameController.text;
String email = _emailController.text;
String adress = _adressController.text;
userApi.editUserProfile(name, email, adress).then((data) {
print(data);
if (data != null) {
// Navigator.pop(context);
/* Navigator.push(
context, MaterialPageRoute(builder: (context) => Profile()));*/
}
// setState(() {});
/* Navigator.push(
context, MaterialPageRoute(builder: (context) => BoxSettings()));*/
setState(() {
enableup = false;
enableadress = false;
enableemail = false;
});
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(data)));
// ScaffoldMessenger.of(context).showSnackBar(snackBar3);
}).catchError((error) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.toString())));
});
setState(() {});
}
and this my screen for more information :
How i can press on edit button without reload screen ?
There some workarounds to achieve this (i.e. update the state of one widget after tapping a completely different widget) like passing the callback function as a parameter etc.
But The best and neat solution here which will solve the above problem and keep your code neat is using Provider pattern.
If you are not aware of how a Provider pattern works, you can easily google search for articles regarding it. Here is one of them :
https://www.raywenderlich.com/6373413-state-management-with-provider
Read the above article before moving below.
Basically what we do is :
Create a ChangeNotifier class.
Wrap the parent of both widgets by a ChangeNotifierProvider widget.
Wrap the widget you want to update with Consumer widget.
Then in your onTap/onPressed function of Edit button you can call a function which will call the notifyListener() function. What this will do is it will notify the above ChangeNotifierProvider widget that some change has neen occured in it's widget tree. Then it will traverse the child whole widget tree below and will update the widget wrapped with Consumer widget.
So this way, you wont need to refresh your whole screen and you can easily update one widget by doing some action on a competely different widget.
Wrap the widgets you want to refresh inside stateful builder and make the whole screen a stateless widget and then call stateful builder

element data not refreshing

//calling a different class for dialog on submit click it's not updating list data
It's refreshing data but not going to the last opened tab on the second tab I perform this action but on tap, it's going on the first tab.
onTap: () {
setState(() {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) =>
EmployeePersonalDetailsUpdateActivity(_token),));
widget.listener.onEmployeeFamilyClick(_day, _month, _year,
_working);
});
Navigator.pop(context,true);
}
Your are calling your widget.listener after you navigate, call it before you navigate the route & if you want to pass the api response to the EmployeePersonalDetailsUpdateActivity widget, you must handle it, and it's your choice when you want to refresh your screen.
setState(() {
widget.listener.onEmployeeFamilyClick(_day,_month,_year,_working);
Navigator.pushReplacement(context, MaterialPageRoute(builder:
(context) => EmployeePersonalDetailsUpdateActivity(_token),));
});
There's another way:
widget.listener.onEmployeeFamilyClick(_day,_month,_year,_working).then((value) {
Navigator.pushReplacement(context, MaterialPageRoute(builder:
(context) => EmployeePersonalDetailsUpdateActivity(_token), value));
});
you can navigate your widget and pass the value of the response and show the updated value on the widget.