Flutter : How put received data on model as nested List - list

I am trying to create shopping app by flutter and Laravel for backend ,I created the backend at tested it on postman and worked correctly ,now I want to design the UI by flutter depend on data's come from API ,First of all I want to view all categories and all products of each category ,I receive data from API as this :
[
{
"id": 1,
"name": "cars",
"created_at": "-000001-11-30T00:00:00.000000Z",
"updated_at": null,
"product": [
{
"id": 1,
"product_name": "mercides",
"category_id": 1,
"price": "120000",
"sale": "0",
"created_at": null,
"updated_at": null
},
]
},
]
I made a class in model folder to put data on it :
import 'dart:convert';
List<Categories> categoriesFromMap(String str) =>
List<Categories>.from(json.decode(str).map((x) => Categories.fromMap(x)));
String categoriesToMap(List<Categories> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toMap())));
class Categories {
Categories({
this.id,
this.name,
this.product,
});
final int id;
final String name;
final List<Product> product;
factory Categories.fromMap(Map<String, dynamic> json) => Categories(
id: json["id"],
name: json["name"],
product:
List<Product>.from(json["product"].map((x) => Product.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"id": id,
"name": name,
"product": List<dynamic>.from(product.map((x) => x.toMap())),
};
}
class Product {
Product({
this.id,
this.productName,
this.categoryId,
this.price,
this.sale,
});
final int id;
final String productName;
final int categoryId;
final String price;
final String sale;
factory Product.fromMap(Map<String, dynamic> json) => Product(
id: json["id"],
productName: json["product_name"],
categoryId: json["category_id"],
price: json["price"],
sale: json["sale"],
);
Map<String, dynamic> toMap() => {
"id": id,
"product_name": productName,
"category_id": categoryId,
"price": price,
"sale": sale,
};
}
Now I want to receive data from URL and convert it as Future List in this function :
Future<List> get_data() async {
var url = 'http://10.0.2.2:8000/api/user/cats_view';
var response = await http.get(url);
var data = jsonDecode(response.body);
}
How can I do it , How can I use categoriesToMap() function on class or any other way?

You could use the categoriesFromMap function to convert the response body into a list of Categoriess. I have included a minimum working example of the get_data function:
Future<List<Categories>> get_data() async {
var url = 'http://10.0.2.2:8000/api/user/cats_view';
var response = await http.get(url);
var data = categoriesFromMap(response.body);
}
See the documentation here for more information regarding deserializing responses.

Related

Ember Data Serializer & Adapter with Supabase returns empty (Proxy { })

Struggling to create my customised adapter & serializer to integrate Supabase, how I'm stuck why no data in Ember Data.
Trying out with a simple findAll() method. See below:
Service ⬇️:
export default class SupabaseService extends Service {
client;
constructor() {
super(...arguments);
const { url, key } = ENV.supabase;
const supabase = createClient(url, key);
this.client = supabase;
}
}
Model ⬇️:
export default class CourseModel extends Model {
#attr('string') name;
#attr('date') date_added;
}
Adapter ⬇️:
export default class ApplicationAdapter extends RESTAdapter {
#service supabase;
async findAll(store, type, neverSet, snapshotRecordArray) {
return new Promise(async (resolve, reject) => {
try {
const { data, error, status } = await this.supabase.client
.from(pluralize(type.modelName))
.select('*');
if (error) {
reject(error);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
});
}
}
Serializer ⬇️:
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// parse the response data from the server and return it in the format that Ember Data expects
let newPayload = {
data: payload.map(item => {
let attributes = JSON.parse(JSON.stringify(item));
delete attributes.id;
return {
id: item.id,
type: primaryModelClass.modelName,
attributes: attributes
}
})
}
return super.normalizeResponse(store, primaryModelClass, newPayload, id, requestType);
}
✅ The service works fine. The adapter manage to get data and returns as follows:
[
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"date_added": "2022-12-31T00:03:14.618585+00:00",
"name": "Science"
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"date_added": "2022-12-31T00:03:30.010963+00:00",
"name": "Physics"
}
]
The serializer newPayload to follow JSON API schema, returns:
{
"data": [
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"type": "course",
"attributes": {
"name": "Science",
"date_added": "2022-12-31T00:03:14.618585+00:00"
}
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"type": "course",
"attributes": {
"name": "Physics",
"date_added": "2022-12-31T00:03:30.010963+00:00"
}
}
]
}
But the problem is no data in store. Logging model in template shows empty Proxy {}.
I have no idea why. Ember Inspector shows no model in Data.
Any suggestions?

_TypeError (type 'List<dynamic>' is not a subtype of type 'List<Category>')

I want to do a travel app so I am trying to create a smal demo data to specify my boilerplate code in my flutter app. But it gives me an type error. I have two model class which are Place and Category. But I can't relate this class. I am taking typError that I can't solve.
_TypeError (type 'List' is not a subtype of type 'List')
Here is the debug panel:
════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building MainScreen(dirty):
type 'List<dynamic>' is not a subtype of type 'List<Category>'
The relevant error-causing widget was
MainScreen
When the exception was thrown, this was the stack
#0 places.<anonymous closure>
#1 MappedListIterable.elementAt (dart:_internal/iterable.dart:413:31)
#2 ListIterator.moveNext (dart:_internal/iterable.dart:342:26)
#3 new _GrowableList._ofEfficientLengthIterable (dart:core-patch/growable_array.dart:206:27)
#4 new _GrowableList.of (dart:core-patch/growable_array.dart:153:28)
#5 new List.of (dart:core-patch/array_patch.dart:51:28)
#6 ListIterable.toList (dart:_internal/iterable.dart:213:44)
#7 places
#8 places (package:seyahat_app/test.dart)
#9 MainScreen.build
#10 StatelessElement.build
#11 ComponentElement.performRebuild
#12 Element.rebuild
#13 ComponentElement._firstBuild
#14 ComponentElement.mount
... Normal element mounting (171 frames)
#185 Element.inflateWidget
#186 MultiChildRenderObjectElement.inflateWidget
#187 MultiChildRenderObjectElement.mount
... Normal element mounting (362 frames)
#549 Element.inflateWidget
#550 Element.updateChild
#551 RenderObjectToWidgetElement._rebuild
#552 RenderObjectToWidgetElement.mount
#553 RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure>
#554 BuildOwner.buildScope
#555 RenderObjectToWidgetAdapter.attachToRenderTree
#556 WidgetsBinding.attachRootWidget
#557 WidgetsBinding.scheduleAttachRootWidget.<anonymous closure>
(elided 11 frames from class _RawReceivePortImpl, class _Timer, dart:async, and dart:async-patch)
════════════════════════════════════════════════════════════════════════════════
Here is my models and data list:
class Place {
final String name;
final String image;
final List<Category> category;
Place({
required this.name,
required this.image,
required this.category,
});
}
class Category {
final String name;
Category({required this.name});
}
List<Place> places = placesData
.map(
(place) => Place(
name: place["name"],
image: place["image"],
category: place["category"]
.map(
(category) => Category(
name: category,
),
)
.toList(),
),
)
.toList();
List placesData = [
{
"name": "Dolmabahçe Sarayı",
"image": "assets/images/dolmabahçe.jpg",
"category": [
"Museum",
],
},
{
"name": "Ayasofya",
"image": "assets/images/ayasofya.webp",
"category": [
"Museum",
]
},
{
"name": "Yerebatan Sarnıcı",
"image": "assets/images/yerebatan.webp",
"category": [
"Museum",
]
}
];
var categoriesData = [
{"name": "Müze"},
{"name": "Restorant"},
{"name": "Eğlence"},
{"name": "Alışveriş"},
];
If you help me I will be very happy...
Bad typecasting. Look at the example.
Example:
void main() {
places.forEach(print);
}
class Place {
final String name;
final String image;
final List<Category> category;
Place({
required this.name,
required this.image,
required this.category,
});
#override
String toString() => 'Place(name: $name, image: $image, category: $category)';
}
class Category {
final String name;
Category({required this.name});
#override
String toString() => 'Category(name: $name)';
}
List<Place> places = [
for (final place in placesData)
Place(
name: place["name"],
image: place["image"],
category: [
// Here you need cast as Type
// Or `List<String>.from(place["category"])`
for (final category in place["category"] as List<String>)
Category(name: category),
],
),
];
List placesData = [
{
"name": "Dolmabahçe Sarayı",
"image": "assets/images/dolmabahçe.jpg",
"category": [
"Museum",
],
},
{
"name": "Ayasofya",
"image": "assets/images/ayasofya.webp",
"category": [
"Museum",
]
},
{
"name": "Yerebatan Sarnıcı",
"image": "assets/images/yerebatan.webp",
"category": [
"Museum",
]
}
];
Output:
Place(name: Dolmabahçe Sarayı, image: assets/images/dolmabahçe.jpg, category: [Category(name: Museum)])
Place(name: Ayasofya, image: assets/images/ayasofya.webp, category: [Category(name: Museum)])
Place(name: Yerebatan Sarnıcı, image: assets/images/yerebatan.webp, category: [Category(name: Museum)])
factory constructor
Add fromMap named constructor to Place class
factory Place.fromMap(Map<String, dynamic> places) => Place(
name: places["name"],
image: places["image"],
category: List<Category>.from(
places["category"].map((name) => Category(name: name))),
);
Assign data to a places list like that:
List<Place> places = placesData.map((item) => Place.fromMap(item)).toList();
Full code:
class Place {
final String name;
final String image;
final List<Category> category;
Place({
required this.name,
required this.image,
required this.category,
});
// Add from map named constructor to the model
factory Place.fromMap(Map<String, dynamic> places) => Place(
name: places["name"],
image: places["image"],
category: List<Category>.from(
places["category"].map((name) => Category(name: name))),
);
}
class Category {
final String name;
Category({required this.name});
}
void main() {
List placesData = [
{
"name": "Dolmabahçe Sarayı",
"image": "assets/images/dolmabahçe.jpg",
"category": [
"Museum",
],
},
{
"name": "Ayasofya",
"image": "assets/images/ayasofya.webp",
"category": [
"Museum",
]
},
{
"name": "Yerebatan Sarnıcı",
"image": "assets/images/yerebatan.webp",
"category": [
"Museum",
]
}
];
var categoriesData = [
{"name": "Müze"},
{"name": "Restorant"},
{"name": "Eğlence"},
{"name": "Alışveriş"},
];
//adding data to the list
List<Place> places = placesData.map((item) => Place.fromMap(item)).toList();
// print statement to check if the data is added successfully
places.forEach((item) {
print(item.name);
print('');
item.category.forEach((category) => print(category.name));
});
}

flutter: compare two object and got ERROR: Expected: VenuesDetails:<VenuesDetails> in unit test

I am using Equatable to compare 2 object in unit testing.This is my object that extended from Equatable:
import 'dart:convert';
import 'package:equatable/equatable.dart';
class VenuesDetails extends Equatable {
Response response;
VenuesDetails({
this.response,
});
factory VenuesDetails.fromJson(Map<String, dynamic> json) => VenuesDetails(
response: Response.fromJson(json["response"]),
);
Map<String, dynamic> toJson() => {
"response": response.toJson(),
};
#override
List<Object> get props => [response];
}
class Response extends Equatable {
Venue venue;
Response({
this.venue,
});
factory Response.fromJson(Map<String, dynamic> json) => Response(
venue: Venue.fromJson(json["venue"]),
);
Map<String, dynamic> toJson() => {
"venue": venue.toJson(),
};
#override
List<Object> get props => [venue];
}
class Venue extends Equatable {
String id;
String name;
Contact contact;
Location location;
String canonicalUrl;
List<Category> categories;
int createdAt;
String shortUrl;
String timeZone;
Venue({
this.id,
this.name,
this.contact,
this.location,
this.canonicalUrl,
this.categories,
this.createdAt,
this.shortUrl,
this.timeZone,
});
factory Venue.fromJson(Map<String, dynamic> json) => Venue(
id: json["id"],
name: json["name"],
contact: Contact.fromJson(json["contact"]),
location: Location.fromJson(json["location"]),
canonicalUrl: json["canonicalUrl"],
categories: List<Category>.from(
json["categories"].map((x) => Category.fromJson(x))),
createdAt: json["createdAt"],
shortUrl: json["shortUrl"],
timeZone: json["timeZone"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"contact": contact.toJson(),
"location": location.toJson(),
"canonicalUrl": canonicalUrl,
"categories": List<dynamic>.from(categories.map((x) => x.toJson())),
"createdAt": createdAt,
"shortUrl": shortUrl,
"timeZone": timeZone,
};
#override
List<Object> get props => [
id,
name,
contact,
location,
canonicalUrl,
categories,
createdAt,
shortUrl,
timeZone,
];
}
class Category extends Equatable {
String id;
String name;
String pluralName;
String shortName;
Icon icon;
bool primary;
Category({
this.id,
this.name,
this.pluralName,
this.shortName,
this.icon,
this.primary,
});
factory Category.fromJson(Map<String, dynamic> json) => Category(
id: json["id"],
name: json["name"],
pluralName: json["pluralName"],
shortName: json["shortName"],
icon: Icon.fromJson(json["icon"]),
primary: json["primary"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"pluralName": pluralName,
"shortName": shortName,
"icon": icon.toJson(),
"primary": primary,
};
#override
List<Object> get props => [
id,
name,
pluralName,
shortName,
icon,
primary,
];
}
class Icon extends Equatable {
String prefix;
String suffix;
Icon({
this.prefix,
this.suffix,
});
factory Icon.fromJson(Map<String, dynamic> json) => Icon(
prefix: json["prefix"],
suffix: json["suffix"],
);
Map<String, dynamic> toJson() => {
"prefix": prefix,
"suffix": suffix,
};
#override
List<Object> get props => [
prefix,
suffix,
];
}
class Contact extends Equatable {
Contact();
factory Contact.fromJson(Map<String, dynamic> json) => Contact();
Map<String, dynamic> toJson() => {};
#override
List<Object> get props => [];
}
class Location extends Equatable {
double lat;
double lng;
List<LabeledLatLng> labeledLatLngs;
String cc;
String country;
List<String> formattedAddress;
Location({
this.lat,
this.lng,
this.labeledLatLngs,
this.cc,
this.country,
this.formattedAddress,
});
factory Location.fromJson(Map<String, dynamic> json) => Location(
lat: json["lat"].toDouble(),
lng: json["lng"].toDouble(),
labeledLatLngs: List<LabeledLatLng>.from(
json["labeledLatLngs"].map((x) => LabeledLatLng.fromJson(x))),
cc: json["cc"],
country: json["country"],
formattedAddress:
List<String>.from(json["formattedAddress"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"lat": lat,
"lng": lng,
"labeledLatLngs":
List<dynamic>.from(labeledLatLngs.map((x) => x.toJson())),
"cc": cc,
"country": country,
"formattedAddress": List<dynamic>.from(formattedAddress.map((x) => x)),
};
#override
List<Object> get props => [
lat,
lng,
labeledLatLngs,
cc,
country,
formattedAddress,
];
}
class LabeledLatLng extends Equatable {
String label;
double lat;
double lng;
LabeledLatLng({
this.label,
this.lat,
this.lng,
});
factory LabeledLatLng.fromJson(Map<String, dynamic> json) => LabeledLatLng(
label: json["label"],
lat: json["lat"].toDouble(),
lng: json["lng"].toDouble(),
);
Map<String, dynamic> toJson() => {
"label": label,
"lat": lat,
"lng": lng,
};
#override
List<Object> get props => [
label,
lat,
lng,
];
}
I write this test to get data from remote repository :
test(
'should perform a get request when the response code is 200 (success)',
() async {
when(mockHttpCLient.get(any, headers: anyNamed('headers'))).thenAnswer(
(_) async => http.Response(fixture('venues_details.json'), 200));
final result = await foursquareRepositoryImpl.getVenuesDetails(venueId);
expect(result, venue);
},
);
But in expect(result, venue); i got this error:
getVenuesDetails should perform a get request when the response code is 200 (success):
ERROR: Expected: VenuesDetails:<VenuesDetails>
Actual: VenuesDetails:<VenuesDetails>
package:test_api expect
package:flutter_test/src/widget_tester.dart 234:3 expect
test/repository/foursquare_repository_impl_test.dart 110:9 main.<fn>.<fn>
Where is my mistake to use Equatable ?
As quoted from the package's page on pub.dev:
Note: Equatable is designed to only work with immutable objects so all
member variables must be final (This is not just a feature of
Equatable - overriding a hashCode with a mutable value can break
hash-based collections).
I noticed that your fields are not final so this might have broken the equality

Angular NgRessource with paginated results from django rest framework

My api basically returns something like this:
GET /api/projects/
{
"count": 26,
"next": "http://127.0.0.1:8000/api/projects/?page=2",
"previous": null,
"results": [
{
"id": 21,
"name": "Project A",
...
},
{
"id": 19,
"name": "Project B",
...
},
...
]
}
Using NgResource, I am able to query the api and get the data like this:
var PROJECT = $resource('/api/projects/:id/', {id:'#id'},{
query : {
method : 'GET',
isArray : false
}
});
factory.project_list = function(callback) {
PROJECT.query({},function(project_list){
factory.project_list = project_list.results;
callback();
});
};
My different projects are now available in factory.project_list. The issue here is that each item in factory.project_list are not ngResource items. So I can't call methods such as .$save(), .$update()...
I saw a transformResponse() function but I'm not able to get it working easily...
Do you have any idea what could be the best approach here ?
This is what worked for me:
app.config(['$resourceProvider', function($resourceProvider) {
$resourceProvider.defaults.stripTrailingSlashes = false;
}]);
services.factory('Project', ['$resource',
function($resource) {
return $resource('api/project/:id/', {}, {
query: {
method: 'GET',
url: 'api/projects/',
isArray: true,
transformResponse: function(data, headers) {
return angular.fromJson(data).results;
},
},
});
}
]);

Ember.js and API pagination

I'm using Ember.js (v1.2.0) with an API which returns paginated JSON data like this:
{
"count": 5,
"next": "http://127.0.0.1:8000/some/resource/?page=2",
"previous": null,
"results": [
{
"id": 37,
"title": "Some title",
"description": "Some description",
},
{
"id": 35,
"title": "Sdflskdf",
"description": "sdfkdsjf",
},
{
"id": 34,
"title": "Some other title",
"description": "Dsdlfksdf",
},
]
}
I'm not using ember-data, so I'm using a plain ember object as my model and loading the data like this:
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return response.results.map(function (data) {
return App.SomeResource.create(data);
});
});
},
});
The find method on my model class returns a promise which resolves to an array of objects. While creates SomeResource objects, all the pagination data is lost.
Is there a way to store count, next and previous page urls somewhere when the promise resolves?
I am assigning them to global object but you should do better.
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return RSVP.all(response.results.map(function (data) {
return App.SomeResource.create(data);
})).then(function(createdResources) {
window.count = response.count;
window.next = response.next;
window.previous = response.previous;
return createdResources;
});
});
}
});
Rather than storing this metadata on the global window object, I came up with this:
App.SomeResource.reopenClass({
find: function () {
var url = '/some/resource/';
return Ember.$.getJSON(url).then(function (response) {
response.results = response.results.map(function (resource) {
return App.SomeResource.create(resource);
});
return response;
});
},
});
SomeResource.find() just instantiates Ember objects from the results array and then returns the response with the rest of the data untouched. The route then receives the response and sets up the pagination data and the model in the setupController function:
App.SomeResourceRoute = Ember.Route.extend({
model: function () {
return App.SomeResource.find();
},
setupController: function(controller, model) {
controller.setProperties({count: model.count,
pageCount: model.page_count,
currentPage: model.current_page,
pageRange: model.page_range,
previous: model.previous,
next: model.next,
model: model.results,});
},
});
It works, but maybe there is a better way.