How to pass an Argument as SerializedMessage - flutter

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

Related

How to add new entry in RxMap

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.

Best practice: Update Model from View flutter

My question is small and is basically a request for advice on how to follow best practice in flutter. I have years of programming experience, but only a few weeks with flutter or dart.
Scenario:
To keep things simple, I have the following:
Model ClassA which has a List of ClassB. ClassB contains some String properties.
A StatefulWidget view that has a ClassA instance with a PageView builder based on the List of ClassB
Every page inside the PageView shows some String properties of ClassB in a TextField
Problem description:
Now, I want to update the properties in ClassB in realtime as the user types (or if the focus drops). There's an event for that and I created a method which accepts the ClassB instance. If I change the attribute in that instance, I see no changes to the original list. If I understand it correctly, dart passed the object by value to the event handler and not a reference to the original object. The original object remains unmodified.
In code this would be roughly the following:
Widget createTextField(ClassB classB) {
return EditableText(
onSubmitted: (value) {
setState(() {
classB.myStringValue = value;
});
}
);
}
PageView.builder(
itemBuilder: (BuildContext context, int index) {
var classB = widget.classA.myList[index];
return createTextField(classB);
},
}
)
My approach:
Instead of passing the instance of ClassB to the event handler, I just pass the index coming from the builder to the event handler instead. The handler will then directly modify the list inside ClassA using the index.
Now, having a strong Java background this somehow feels weird. My first instinct was to pass the instance of the ClassB to the event handler and modify the instance there. My assumption was that this would be a reference to the original Class B instance.
How would some of you approach this problem? It sounds easy enough, but I just want to follow the best practice here.
You should wrap the code that changes the value of a property inside
setState((){
// Put your codes here.
});

Flutter SQFLITE: Please explain what factory and this data type Future<List<Trail>> means

I'm trying to get SQFlite to work in a flutter.
I'm new to flutter and there are some nagging problems with null safety and new types I'm not familiar with.
On top of the problems.. sometimes it says "List is not a type" which it certainly is.
My own code is located here
https://github.com/bksubhuti/places
I would like to connect it to a futurebuilder or listbuilder UI.
This is a learning app but also one for production later on.
Eventually I want to rewrite book reading app (Tipitaka Pali Projector) based on SQFlite and flutter.
follow a tutorial on trails. The code is on github.
https://github.com/nhandrew/sqflite_app/blob/main/lib/main.dart
It does not work with null safety
There is another code for a dog tutorial which is the official tutorial for flutter SQLite I don't understand this code
The code is located at this link here
The dog tutorial also has a similar syntax which is new to me.
https://github.com/flutter/website/blob/master/src/docs/cookbook/persistence/sqlite.md
I get confused about the factory and this datatype Future<List>
I have never seen this stuff before.
It also has to work with null safety.
It is This part here.. that is confusion
factory Trail.fromJson(Map<String,dynamic> json){
return Trail(
name: json['name'],
difficulty: json['difficulty'],
distance: json['distance']
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: FutureBuilder<List<Trail>>(
future: dbService.getTrails(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index){
return ListTile(
title: Text(snapshot.data[index].name),
leading: Text(snapshot.data[index].difficulty),
trailing: Text(snapshot.data[index].distance.toString()),
);
});
}
)
);
}
Future<List<Trail>> getTrails() async {
await initDatabase();
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
return list.map((trail) => Trail.fromJson(trail)).toList();
}
I suggest you read Dart language tour:
https://dart.dev/guides/language/language-tour
You can find details on your questions here:
https://dart.dev/guides/language/language-tour#factory-constructors
https://dart.dev/guides/language/language-tour#generics
https://dart.dev/guides/language/language-tour#asynchrony-support
https://dart.dev/guides/language/language-tour#lists
I'll edit my answer to give answers you asked in a comment.
So, let's unpack this part:
Future<List<Trail>> getTrails() async {
await initDatabase();
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
return list.map((trail) => Trail.fromJson(trail)).toList();
}
First of all - the function is asynchronous (as indicated by async keyword). This means a few things: this function will be able to run in parallel with other things. This is not technically 100% correct, but let's leave it at this level now.
This parallel run is achieved by the function immediately returning a Future object, and later returning the function value into this Future object. (Actually, Future is returned when it first hits await statement but ignore it for now).
Think of it as ordering food in a restaurant: you place the order, and the waiter immediately puts an empty plate in front of you (the plate is the Future object). While waiting for your dinner, you get to do other things - go to the restroom, have some drinks, etc. Eventually, the food will appear in your plate. Food is the object that the Future will eventually hold.
I'm trying here to simply explain a very complex topic - you should learn about this separately, it will really help you a lot. A LOT.
So this kinda explains what this means: Future<List<Trail>>. Your function will immediately return a Future object. But not any Future object, but the one that will hold a specific return value once the function completes. This value will be of List type: but not any List type - it will hold a list that will have only Trail object values. Hence List is defined as List<Trail> (List of Trail objects), and Future is Future<List<Trail>> - a Future that will hold a List of Trail objects.
Next, you are calling await initDatabase();. your initDatabase is async itself, which gives you an option to run some other things while you are waiting for the database to initialise. You could have for example:
var i=initDatabase();
print('Doing some work while the database is being initialized');
await i; // now I'm done, nothing for me to do until the database is initialized, so I'll just wait for that
List<Map> list =...
Again - here you really need to understand your async/await and Future (or Promises as they are called in JavaScript).
The rest should be easy:
List<Map> list = await _db.rawQuery('SELECT * FROM Trails');
Your rawQuery is async - and you decided to wait for it, since there is nothing else for you to do until query is done. The query will return a List - assuming here it will be a List of database Rows. Each Row is presented as a Map object, so List will not be any list - but a List of Map: List<Map>.
Finally - you convert List<Map> that is returned by query into List<Trail> - since this is the value you should return. Remember - function returns Future<List<Trail>>, so your final return statement needs to return List<Trail>. Remember: Future was returned already when you called the function.

List with Custom Class and operations on List

Hi, I have a class called Items.
class Items{
String name;
String picture;
int quantity;
int price;
Items(#required this.name, this.picture,this.quantity,this.price);
}
The same class items are rendered on the screen shown in the screenshot. I have been struggling to add an item to the cart on click of addToCart image button.Im struck at how to search the List and perform add, delete and edit operations on the list. Can somebody help me? thanks in advance.
I don't want to alter your model Items but in order to know which item you must (add, remove) in/from your cart, each item must have a unique Id or similar
class Items{
int id;
// ...
}
Hence, your ListView.builder will look like this
ListView.builder(
itemBuilder: (context, index) {
return ListTile(
// ...
trailing: IconButton(
icon: Icon(Icons.add_shopping_cart,),
onPressed: () {
// Get the item id
final itemId = itemList[index].id;
// Then do your operations
},
),
);
},
),
After some struggle, i'm able to do what I wanted. A brief explanation can help others.
Here we go:
Render your UI from Model data.
Use providers and apply at appropriate level. I declared a 'List of Items class' and add/delete
methods to perform operations on the List. With help of
ListView,identified the index and added that item to the List
of the provider class.
Cart UI can be rendered from the List of the provider class.
My explanation helps in understanding theoretically first because there is so much of practical code available. This is easily doable provided what is needed and what is available in the flutter are clear.

Difference between flutter onTap() and javascript/jquery $(this) in click listener

I'm still in the process of learning dart/flutter. I'm experimenting with ListView/ListTile and specifically onTap() from ListTile.
I'm somewhat familiar with JS/Jquery and am having trouble understanding the differences in approach between JS and flutter.
Please try to ignore any semantic or technical errors in the code below. I'm more concerned with theory/approach than if this is a perfectly formatted and syntactically correct example.
In JS, it's common to do something like make an AJAX call to an API to get a list of items for sale from a database. Assume the API returned 4 fields (primary key ID, isAvailable, item name, and item price). You then process the list and create/insert DOM elements for display.
<div id="234" data-isavailable="y" class=".itemsForSale">TV: $800</div>
<div id="345" data-isavailable="y" class=".itemsForSale">Microwave: $200</div>
<div id="456" data-isavailable="n" class=".itemsForSale">Book: $30</div>
<div id="567" data-isavailable="y" class=".itemsForSale">Sofa: $450</div>
You can then set listeners arbitrarily. For instance, you could do...
$( ".itemsForSale" ).click(function() {
// do something
});
The click handler gets applied in "real-time". The code executed is aware of what was clicked and can analyze/interact with that item in the list. So, you can do something like:
$( ".itemsForSale" ).click(function() {
var isAvailable = $(this).attr('data-isavailable');
if( isAvailable == 'n' )
alert('sorry but that item is out of stock!');
else
addItemToCart( $(this).attr('id') );
});
The point being, that the click handler doesn't necessarily know or care about the underlying data of each item. It becomes "context" aware during runtime and then pulls the relevant values out of the UI/etc.
With flutter, I'm looking to recreate similar functionality using a ListView and ListTile. Specifically, ListTile's onTap(). I'm getting confused because it seems like everything is coded "ahead of time".
Here is a basic example (ignore for now I'm not displaying price/etc)...
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
var mapper = {
'234': 'TV',
'345': 'Microwave',
'456': 'Book',
'567': 'Sofa'
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body:
ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: mapper.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
key: new Key(mapper.keys.elementAt(index).toString()),
title: Text('${mapper.values.elementAt(index)}'),
onTap: () {
print('you clicked: ' + mapper.keys.elementAt(index).toString() );
addToShoppingCart( mapper.keys.elementAt(index).toString() ); // this doesnt exist yet and is just an example
}
);
}
)
);
}
}
First of all, I'm not even sure I need to assign a custom key to each ListView item (based on the database's primary key), but that's irrelevant for now.
What I'm getting stuck on is that the onTap handler doesn't seem to have an equivalent of "$(this)". Does flutter give each ViewTile it's "own" copy of onTap() and that each relevant key info is "hardcoded" into each one (for each itemBuilder loop, the current value of mapper.keys.elementAt(index).toString() is substituted into onTap() )?
I'm probably not describing my confusion properly and I'm not even sure all of the issues I'm going to run into. I'm sure my way of doing this is very "old" and not aligned with react and other newer approaches/frameworks (combining data within the UI elements).
I'm just having trouble seeing how I'm going to be able to add more complex logic (like checking availability of what was clicked).
Does anyone have any references or explanations that help me bridge this gap? I don't even know how to describe the concept of $(this) and how I would search for it in comparison to flutter/etc. I just know that something feels very different.
Thanks!
I understand your confusion probably because I'd a similar question when I started with Flutter just a few months back. And here is what I think -
It doesn't really matter whether the ListTile item has it's own copy of onTap() or not. A. It does not have it's own copy. (Following code snippet will explain) and B. Every programming language / SDK / whatever has its own way of working. What you are trying to say, probably, is that you've a BMW. It has got certain type of breaks. And you are trying to make exact type of break in Audi. It may not be right to do it as the other systems related to the breaks may not work in the optimised way.
Now, look at the following code snippet :
#override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (context, index) => ListViewDivider(),
itemCount: faceRecTransactions.length,
itemBuilder: (BuildContext context, int index) {
return FacerecTransactionListItem(facerecTransaction: faceRecTransactions[index], onTap:()=> _onTap(faceRecTransactions[index],context),);
},
);
}
void _onTap(FacerecTransaction facerecTransaction, BuildContext context) {
print('Status : ${facerecTransaction.userId}');
Navigator.push(context, MaterialPageRoute(builder: (context) => FacerecDetailPage(
facerecTransaction: facerecTransaction,
criminalList: this.criminalList,)));
}
There's no copy of onTap for every list item. It just 'feels' as it has because we write onTap inside ListView. If you look at the example above, when the user taps on certain item, then the onTap receives the information. We don't create array of onTap as such.
Since, there's no copy of onTaps, it's not really ahead of time code. It's pretty much works like in Ajax where the onTap doesn't really care about the payload. It just does the action(s) specified.
Now, in the above example, the Detailed Page can check the availability of the particular item. And if the item is not available then either appropriate message can be shown. Depending on the requirement, we can either write this code within onTap or on the next page. In short, the onTap can handle the complex logic as you need.