How to add new entry in RxMap - flutter

By using getX, i was trying to modify changes to RxMap. But I couldnot find the way to add value to map. add() is not defined at all for RxMap.
putIfAbsent() is not adding up new entry to RxMap.
class RestaurantController extends GetxController {
RxMap<String, String> reviews = <String, String>{}.obs;
addReview(String name, String review) {
reviews.putIfAbsent(name, () => review);
}
}
I tried calling addReview with the help of TextButton as
TextButton(
style: ButtonStyle(
elevation: MaterialStateProperty.all<double>(10.0),
backgroundColor:
MaterialStateProperty.all<Color>(Colors.orange)),
child: const Text("Submit"),
onPressed: () {
restaurantController.addReview(
nameController.text, reviewController.text);
print("submitted");
}),
On click, the print statement gets executed, still map is not updated. How can I add new key, value pairs to reviews? I'm confused.

Let's say we have the following map in our controller:
RxMap<String, String> map = RxMap();
To add data to this map we can do:
_controller.map.addAll({'hello': 'bye'});
Now, if we print map, the following will be printed as it is added to map:
{hello: bye}
Now, if we do with putIfAbset:
_controller.map.putIfAbsent('hello', () => 'bello');
The console print will still be the same as the key is already present in map so nothing will be added.
But if we change the above code to this:
_controller.map.putIfAbsent('lol', () => 'bello');
Now, if we print to console we will see:
{hello: bye, lol: bello}
To add value to an empty map it's better to use addAll. Use putIfAbsent if you are sure you have same key.
Your code looks right to me as you can also add values to map your way. Just try to print to console everytime you click submit button. Probably it is saving data to your map, without Obx or rebuilds you really cannot see changes in your widget.

Related

Difference between myFunction, myFunction(), and myFunction.call() in Dart/Flutter

I've noticed that when I have a widget/class that takes Functions as arguments, when it comes time to call those functions, it can be done one of three ways (that I know of):
(Consider a Function, myFunction)
myFunction
myFunction()
myFunction.call()
But the weird thing is, I've noticed that when using option 1), it will (ONLY SOMETIMES) not work and require the use of option 2 or 3 in order to work.
Why is that?
Specific example (I've verified the inconsistent calling behaviour with print debugging in the parent):
class SoundPickerTile extends StatelessWidget {
final Sound sound;
final Function() checkboxCallback;
final Function() soundPlayCallback;
SoundPickerTile(
{required this.sound, required this.checkboxCallback, required this.soundPlayCallback});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: checkboxCallback, // <--------------- Function executes in parent
child: Container(
color: sound.isIncluded ? Colors.lightGreen.withAlpha(100) : Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(20, 10, 0, 10),
child: Row(
children: [
Expanded(
child: Text(
sound.shortTitle,
),
),
Expanded(
child: IconButton(
icon: Icon(Icons.play_circle_outline),
onPressed: () {
print("this line of code was reached"); // this works
soundPlayCallback; // <--------------- Function *does not* execute in parent
},
),
),
],
),
),
),
);
}
}
They are all the same object, but with two different behaviours.
myFunction is your function but shaped like an object. So you can pas this around as an argument for onTap (which takes a function like that as argument). That's also why it's run in the parent, that's how it's supposed to work. It gets tossed around as an object and the parent calls () on it.
The reason it's not being executed below is because there you are simple putting the function down as an object. Much like you earlier passed it as an object, right now you're just saying hey here's the function, but I'm not gonna do anything with it.
myFunction; -> no ()
So in order for it to work you need to use number 2. or 3.
myFunction() -> This will call your object (your function) and run it's code, contrary to the previous mentioned example where you just lay the object down. The () is important!
Now the difference between 2. and 3. is.. almost nothing!
() actually does the .call() in the background, however if you have a myFunction that's possibly null, then you can do something like this:
myFunction?.call();
This will only call the function if it's not null, else it will not do anything.
Hope that's clear, also try to define a return value when specifying callbacks, this will make you understand passing around functions quicker. And have a look at typedefs, they are basically signatures for specific functions (VoidCallback for example). Once you grasp that concept passing around functions will become a breeze! Goodluck.
Let's examine these cases and understand what they are:
myFunction:
This one is just a reference to a function. When you write this, the function is not called. Instead, we may be giving the function to somebody else, who may or may not call it in the future.
myFunction() and
myFunction.call():
Call the function. When you write this, the function executes, whatever side effects it has happen and it returns whatever it returns.
So 2 and 3 are exactly the same. So let's focus on the difference between 1 and 2. That is, myFunction and myFunction().
myFunction is a reference to a function. Kind of like the i in int i = 5. You can call it by adding () at the end, or calling its .call() method. Similarly, you can do i.isEven with i.
Example: I'm calling the function. Whatever side effects it has will happen before the next line.
myFunction();
myFunction.call(); // same thing
Alternative to calling myFunction, you can also pass it around as a reference to a function. This is like passing around an int. You are passing the reference to this function to some other place in your code. It may or may not be called in the future. You can call it, you can throw it away, it's your call.
Example: Dear button, here's a function. Call it when you're pressed.
Button(
onPressed: myFunction,
)
Example: Dear button, here's a little function (the () =>) that returns a reference to myFunction. When button calls this, it receives the reference to myFunction. However, the button just calls what you give to onPressed. It does not care about what you return from that. Therefore, you return the reference myFunction but it is never called, it's thrown away.
Button(
onPressed: () => myFunction,
onPressed: () {return myFunction;} // same thing
)
Example: Dear button, here's a little function that calls myFunction() and returns whatever it returns. So myFunction is actually called when the button is pressed.
Button(
onPressed: () => myFunction(),
)
Example: Dear button, here's a little function that does not return anything. One of its lines is myFunction;, which does not do anything, it's like writing 1;, nothing is done with it. Therefore myFunction is not called.
Button(
onPressed: () { myFunction; }
)
Any possible usage of myFunction should fall into one of these categories. The descriptions of the examples should help in understanding what that usage means.
In the GestureDetector we use:
onTap: checkboxCallback,
without the (), since that would call the function immediately, we don't want to call the function, we just want to pass a reference to the function on what should happen when onTap is called.
Then with:
onPressed: () {
print("this line of code was reached"); // this works
soundPlayCallback; // <--------------- Function *does not* execute in parent
},
Since were are not using () it's not being called it's just a reference to the function.
Instead, what you can do is:
onPressed: soundPlayCallback;
Or add the ()
Edit.
What is the difference between calling the function without parentheses and with parentheses

How to pass an Argument as SerializedMessage

I'm quite new to Flutter and to learn "best practice" I loaded up the new skeleton example app.
You get three example items in a list view and when you click on one of them you go to a details view. I want to pass the object so that for every example item there is a custom details view.
So I changed the code to the following:
ListTile(
leading: const FlutterLogo(),
title: Text(myObject.name),
onTap: () {
Navigator.restorablePushNamed(
context, ObjectDetailView.routeName,
arguments: myObject);
},
trailing: const Icon(Icons.arrow_forward_ios_rounded),
But it shows the error: The arguments object must be serializable via the StandardMessageCodec.
How can I do it? This seems quite complicated for an "example app". Does it make sense to use restorablePushNamed() on a details page? Or should I switch to the "normal" push/pop-Method.
It does makes sense to use restorablePushNamed() or any other restorable push method if you want to preserve the current page and the state of the app when it's killed by the operating system while running in the background (on low memory condition, for example).
It's up to you decide if this is necessary in your app, otherwise, you can just use "normal" push methods without needing to serialize the arguments.
But to use State Restoration, you'll have to convert myObject to a Map or List in order to StandardMessageCodec serialize it, as #Apealed said in the comments.
To convert it to a map. you can do something like this in your class:
Map<String, dynamic> toMap() {
return {
"name": this.name,
"property2": this.property2,
...
};
}
You can check more info about it on documentation: flutter.dev/go/state-restoration-design

Adding Multiple List Tile Items in Flutter to List

I have this code that I can't get to work properly. I have a Food class and I have initialized the name, price, and unique ID strings on it. I made the unique ID to be Uuid().v4(), which would give each food item a random string.
FOOD CLASS
class Food {
String name;
String price;
String uniqueID = Uuid().v4();
Food({this.name,
this.price,
this.uniqueID})}
On another page I have a Provider function that would add items in the cart, it is a list of string items (this may not be the best option). Here is the code for it:
class CartItemsModel extends ChangeNotifier {
List<String> _cartItems = [];
List<String> get cartItems => _cartItems;
addCartItem(String item) {
_cartItems.add(item);
notifyListeners();
}
Now, on another page, I am calling that food to be added to the cart, it is an icon with onPressed above function:
return ListTile(
trailing: Container(
padding:
EdgeInsets.only(top: 15.0),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () =>
model.addCartItem(
"${food.name}, Calories: ${food.calories} ${food.price} din\nVegan: ${food.isVegan}, ${Uuid().v4()}")),
Now, you see that I have Uuid on there (without my uniqueID from Food class, because for some reason it doesn't work). I have the Uuid, so that there isn't an error with multiple duplicate items if the button would be clicked twice, here's the error without it:
The issue is that this works and is functional, but I have this ugly ID from the Uuid displayed on the final 'cart' window. Basically I want this ID to be invisible. Here is how it looks:
And here is the code for the 'cart' screen:
class _CartState extends State<Cart> {
#override
Widget build(BuildContext context) {
return Consumer<CartItemsModel>(
builder: (c, model, _) => Scaffold(
body: SingleChildScrollView(
child: Column(
//on trailing i should have icon with clear function that will delete that item from the list
children: model
.cartItems //maybe below can return ListView.separated like on the food list user, we'll see
.map((e) =>
So to keep long story short, my uniqueID isn't used on here because for some reason it doesn't make each list tile item unique with its key, so it doesn't display and give me error when clicked twice, that's why temporatily I am using the Uuid trick.
What I want is for this to work exactly like this, but without the ugly Uuid string to be seen. Is there simple I can do with it, maybe add something to the CartItemsModel code, a conditional, or something else?
If I change the code in onPressed to this:
onPressed: () {
if (!model.cartItems
.contains(food)) {
model.addCartItem(Food);
}
}
I am getting error:
The argument type 'Type' can't be assigned to the parameter type 'String
Is there a simple solution to have items added to the 'cart' screen easily, no matter how many times I click on the same item, just have each as a separate list tile in the cart screen?
UPDATE ERRORS
I am getting these errors in different files when I change these, even if I change the value of everything to Text.
Strings do not have a key property as far as I know.
Try something like this (you could also use UniqueKey()) in order to get a key for your CardItems:
return ListTile(
trailing: Container(
padding: EdgeInsets.only(top: 15.0),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () => model.addCartItem(
Text("${food.name}, Calories: ${food.calories} ${food.price} din\n
Vegan: ${food.isVegan}",
key:ValueKey(Food.uniqueID.toString()),
),
),
),
),
),
Then you need to adjust your CartItems model to List<Text> _cartItems = []; and List<Text> get cartItems => _cartItems;
This way each element of the list has your unique key. Also you need to adjust the map function in your _CartState builder because now you don't have Strings anymore but Text widgets.

Flutter how to retrieve value from button

new FlatButton(
child: new Text("933478476")
)
Here i created one button now i want to store the value of button in string.How to store the values from widget.
The FlatButton dosen't take any value and would not return either. What you could do is, pass a void callback that triggers your function that takes button click values as arguments.
Example:
//Define you function that takes click
void _onClick(String value) {
//do something
}
// Make you `FlatButton` like this
FlatButton(
onPressed: () => _onClick('12345678'), // a lambda void callback that calls your click function with value
child: Text('12345678'),
);
Hope that helped!

IconButton calling setState during onPressed shows no ripple effect

During the onPressed of my IconButton I need to update the database and then the UI so that the user sees feedback of the data change. To do this I call setState, which successfully lets the Widget rebuild. The problem is I'm no longer seeing the touch feedback ripple effect because the Widget rebuilds immediately.
var button = new IconButton(
icon: new Icon(isMyBoolTrue ? Icons.undo : Icons.event_available),
onPressed: () => setState(() => toggleDatabaseBool)
);
The issue was I was creating an ObjectKey with an object that was re-created every time during build. Solved the issue by using ObjectKey and the id field of my object instead of the object itself.
Bad
var card = new Card(
key: new ObjectKey(goal), //the goal object was re-created during `build`
child: button
);
Good
var card = new Card(
key: new ObjectKey(goal.id), // need to key off of the id field as it remains constant
child: button
);
That shouldn't stop the splash. The only reason the splash should stop is if you add or remove one of the widgets between the IconButton and the Material, or change its key, or change the Material's key, or move the Material in the tree. (Some of those are bugs in the framework right now, I hope to fix them in the coming months.)
Can you post a minimal app that shows the problem?
Meanwhile, we're tracking the issue at https://github.com/flutter/flutter/issues/6751 and https://github.com/flutter/flutter/issues/5456