I have a custom object called item, which contains parameters such as a name, description, price etc. My app's home page is a streambuilder of item objects (stored in Firestore). When I click on an item, it takes me to a detail page, and from there, I can hit another button to edit the item's parameters. When navigating from the home page (item list) to the item detail page, I am using fromMap to easily create a local item from the database and passing that item into the detail page. Then, if the user decides to click the edit button, I am passing that same item into the item edit page (with the item's parameters already filled out). My edit page also contains a button 'save' that will save the updated parameters to Firestore.
The issue I am facing is that this local item is being shared between the detail page and the edit page. So for example if I edit the name of an item and hit the back button, those changes will be reflected in the detail page, when I actually don't want them to. What is the best way of going about handling these changes, more specifically updating the item parameter's in the detail page? Should I add a method in the detail page that every time I enter this page, refresh the page by pulling the most recent data from Firestore? Or, when navigating from the home page (item list) to the detail page, pass in the item's document ID and create the item in the detail page?
You can create a copy of the item object and edit the copy instead so they do not share the same reference.
At the end when you want to propagate the change you can just set the values of the newly instantiated object to the old one.
For example:
class EditItemDetailsPage extends StatefulWidget {
final Item item;
EditItemDetailsPage([this.item]);
#override
_EditItemDetailsPageState createState() => _EditItemDetailsPageState();
}
...
class _EditItemDetailsPageState extends State<EditItemDetailsPage> {
Item _itemCopy;
#override
void initState() {
super.initState();
// Make a copy of the instance you are editing
// An easy way to copy is to convert them into a map and back.
itemCopy = Item().fromMap(widget.item.toMap());
// Now your widgets here will edit _itemCopy instead of widget.item
}
void submit() {
// Send to firestore
bool success = sendToFirestore();
// Change were made to firestore successfully
if (success) {
// Apply change to original account instance.
widget.item.fromMap(_itemCopy.toMap());
}
}
}
Related
I'm using GetX to manage the state of my Flutter app, and the structure of my pages consists of an ItemsListPage that lists some items, and an ItemDetailsPage that shows details about an specific item and lets the user change it or delete it.
I'm managing state using GetX's Reactive state management, which means that, in order to update the state of my ItemsListPage, I need to update an observable variable itemsList that is inside the ItemsListController. This variable is updated on the onInit() method of the controller, which means that whenever the controller is instantiated the list should be updated with the data fetched from the API.
The controller of the ItemsListPage looks like this:
class ItemsListController extends GetxController {
var itemsList = <AppEntity>[].obs;
#override
void onInit() {
super.onInit();
// Fetch items from the API and store them on itemsList:
getApps();
}
//...
}
And the controller of the ItemDetailsPage is like this:
class ItemsDetailsController extends GetxController {
void deleteItem() async {
final response = await deleteItem();
response.fold((failure) {
// Deal with a failure on the request
}, (response) {
if (response.success) {
// I want to update the state of the list from here
Get.toNamed('/main/');
Get.snackbar('Deleted', 'Item deleted successfully!');
}
});
}
The problem is that when the user changes or deletes an item (on the ItemDetails page), he gets redirected to the main page via Get.toNamed('/main'), and sees the old version of the list, because the onInit() method isn't called again.
Is there a way for me to trigger an action everytime the user is redirected to the ItemsList page? This would allow me to fetch the fresh data from the API and update the page. Another solution would be a way of updating the state of the ItemsListController from the ItemDetailsController, which would be even better, as it would not require another call to the back-end.
If you are using get_cli, then you can pretty much generate and use named routes along with your view, controller and bindings. But if in case you are just using the state management without binding, then this problem may occur.
The thing happening in this scenario is, your controller is building one time and then your onInit method is called, after that you do some operation on your list like deleting or adding some data, and then when you go to another screen, your controller doesn't get disposed(Strange).
You can solve this by using these ways,
Whenever you switch to another screen, let's say using Get.toNamed(route) or Get.offNamed(route) or Get.off(route), what you can do is before navigating to other screen, you can remove the controller form the memory like this,
Get.delete<ControllerName>();
In this way, your controller will be again reinstantiated when you open your screen again(Provided that you have put the Get.put(Controller()) In your widget.
If you are using named routes then you can pretty much directly use them, you don't need to do anything in order to work with them. In case you're facing the error even if you have bindings and named routes then you can perform step 1.
If you want to minimize the API calls then whenever the user deleted some item from the list, you can delete it locally and update the UI according to it. This way user won't have to see the loaders again and again.
How can I navigate same page like I am tapping related products in Product details page and it will navigate to Product details page according to that related product? I want to call same page over and over again.
If you make your ProductDetails page take in a Product (or id, depends on your data) you should be able to call it. It depends on how you want to build your routes (and how you want to be able to go back), of course. Say you havce your ProductDetails take in a product to display:
ProductDetails(product: myProduct)
Then it can get the related products, and you can make the related product push another ProductDetails page:
// In your product details page
Widget relatedProductsWidget() {
return Row(
children: relatedProducts.map((product) => ProductCard(
product: product,
onTap: () => Navigator.of(context).push(
// Simply push another product on the stack
MaterialPageRoute(builder: (context) => ProductDetails(product: product))
),
)),
);
}
It depends, of course, on how you wish to go back. This way, a whole stack of ProductDetails pages will be pushed (and popped again when the back button is pressed). Alternatively, if you only want a single product details page to be present at any time, you can use Navigator.of(context).pushReplacement instead, to animate to the new location and then dispose the old route. Now when the user goes back, he is brought back to the main product list without seeing the old product first.
https://api.flutter.dev/flutter/widgets/Navigator/push.html
https://api.flutter.dev/flutter/widgets/Navigator/pushReplacement.html
I hope this gets you on the way to get your navigation sorted. If you have a specific issue, I recommend you to post a minimal version of your code to make it clear what you're trying to accomplish, as it's still a little bit vague from your question right now.
My answer only works if you use firebase.
Just complete the steps:
Create in the database a collection called products
Now add the products and in all products/documents you need to have a field called id. PS: any ids have to be used only one time.
Create in your code a global variable called id
If you tab on one of the products the id-variable need to be set to the id of the product you tabs on
In your Product-Details Page add a variable called productData
Create in your Product-Details class a method that looks like this:
getProductData() async {
await FirebaseFirestore.instance.collection("products").where("id", isEqualTo: id).get().then((value) {
productData = value.docs[0].data();
});}
In your initState() add this method:
asyncTasks() async {
await getProductData();
}
Call the method asyncTasks() in your initState
Now you can get data from the product document like this:
// Now you get the value that stands in the price field
productData["price"];
Hope it helps!
When I insert data from other page and pop this page to go back on the listing page, I have to refresh the listing page to get the updated list of data. I want to go back to the updated version of page without pressing refresh button. I am using pushNamed and also I want to have a back arrow button on the input page.
Using Navigator.push or Navigator.pushNamed returns a Future object to you.
This Future resolves when you use Navigator.pop in the your second screen.
Here is a basic example. Assuming you have function goToNewPage which you are calling to push a new Screen, you can do it like this.
class Screen1State extends State<Screen1> {
void goToNewPage (context) {
Navigator.of(context).pushNamed('screen2').then((_) {
// The screen2 has popped. so you can just call setState here to just rebuild
// You can do anything you want here, that needs to happen after the new page has been popped.
setState(() {});
});
}
... rest of your code.
Note, the actual implementation of what you should do inside the callback depends on how your widget is getting data. But you can do anything you want to do after your new page is popped inside that callback.
I have a home page that displays results based on some user information. From results page user can navigate to profile page and edit information. After edit information, they navigate back to home page. The problem is, the home page not refreshed with new results. It still shows old results. How can i reload this page with the new data.
home.page.ts:
openProfilePage() {
this.router.navigate(['/profile']);
}
profile.page.ts
onSubmit() {
this.router.navigate(['/home'], {replaceUrl: true});
}
You can trigger a call you need to make to update the info on:
ionViewDidEnter() {
console.log('ionViewDidEnter ');
// call update function
}
ionViewWillEnter(){
//here you check data is changed or not and call your function according to that this method call every time when navigate to home page
}
The main interface for my app is dynamically built based upon whether or not the app has been activated. I created a previous route service that uses Angular’s Activated Route to detect if the main interface needs to be updated without doing a complete refresh.
I have implemented two custom IWorkbenchPreferencePage's and they are working as expected. Basically Page1 shows different information based on the selections made in Page2. The problem is that I have to close (explicitly save) the preference dialog to see the changes of Page2 reflect in Page1.
Now I was wondering if there is some kind of mechanism that would allow me to do something (In my case save information on the open preference page) once a IWorkbenchPreferencePage gets left.
You can override the okToLeave method which is called when a different page is selected.
The default implementation of this in PreferencePage is:
#Override
public boolean okToLeave() {
return isValid();
}
You can also use the
public void setVisible(boolean visible)
method which is called when the page is made visible and when it is hidden (be sure to call super.setVisible in your override).