Data gets lost when added to a Model - flutter

I am getting data from Firebase Database and Adding it to a List of my Model class. I tested the incoming data by printing to Console and it works fine, but once i add the data to my model class, it disappears.
Here's my Provider class where i'm loading the data.
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:local_stuffs_notification/apis/fcm.dart';
import 'package:local_stuffs_notification/models/request_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class IncomingRequest with ChangeNotifier {
List<RequestModel> _incomingRequests = [];
IncomingRequest(this._incomingRequests);
List<RequestModel> get incomingRequest {
return [..._incomingRequests];
}
Future<void> setIncomingRequest(RequestModel requestModel) async {
try {
DatabaseReference reference =
FirebaseDatabase.instance.ref("incomingRequests");
reference.child(requestModel.id).child(Fcm.getUid()).set(
{
"name": requestModel.name.toString(),
"phone": requestModel.phone.toString(),
"email": requestModel.email.toString(),
"fcmToken": requestModel.fcmToken.toString(),
},
);
notifyListeners();
} catch (error) {
rethrow;
}
}
Future<void> loadIncomingRequests() async {
try {
SharedPreferences preferences = await SharedPreferences.getInstance();
DatabaseReference reference = FirebaseDatabase.instance
.ref('incomingRequests/${preferences.getString('userId')!}');
Stream<DatabaseEvent> stream = reference.onValue;
stream.listen((DatabaseEvent event) {
print(event.snapshot.value);
final data = event.snapshot.value as Map;
print('data: $data');
final List<RequestModel> loadedRequest = [];
data.forEach(
(key, value) {
print('requestData: ${value['name']}');
loadedRequest.add(
RequestModel(
id: key.toString(),
name: value['name'].toString(),
fcmToken: value['fcmToken'].toString(),
phone: value['phone'].toString(),
email: value['email'].toString(),
),
);
print(loadedRequest);
},
);
_incomingRequests = loadedRequest;
print('LoadedRequests: $loadedRequest');
notifyListeners();
});
// reference.onValue.listen(
// (event) {
// if (event.snapshot.value == null) {
// return;
// }
// final data = event.snapshot.value as Map;
// final List<RequestModel> loadedRequests = [];
// data.forEach(
// (key, requestData) {
// loadedRequests.add(
// RequestModel(
// id: key,
// name: requestData['name'],
// fcmToken: requestData['fcmToken'],
// phone: requestData['phone'],
// email: requestData['email'],
// ),
// );
// },
// );
// _incomingRequests = loadedRequests;
// notifyListeners();
// },
//);
} catch (error) {
rethrow;
}
}
}
Here's my Model Class
class RequestModel {
final String id;
final String name;
final String fcmToken;
final String phone;
final String email;
RequestModel({
required this.id,
required this.name,
required this.fcmToken,
required this.phone,
required this.email,
});
}
I'm getting the data until i added it to loadedRequest List
Please help, i've spent hours on this and i don't know what i'm doing wrong. When i print the loadedRequest list, i get an empty list. Thanks.

Those logs aren't showing an empty list - It says [Instance of 'RequestModel']. That means there is a value there, but Dart simply doesn't know how to convert RequestModel to a String so that it can be printed out on the console.
An empty list would be printed simply as [], and if you had two values, for example, you would see [Instance of 'RequestModel', Instance of 'RequestModel'].
To print out your values with more detail, you can override the toString() method on your class.
For example:
class RequestModel {
final String id;
final String name;
final String fcmToken;
final String phone;
final String email;
RequestModel({
required this.id,
required this.name,
required this.fcmToken,
required this.phone,
required this.email,
});
#override
String toString() =>
"RequestModel(id: $id, name: $name, fcmToken: $fcmToken, phone: $phone, email: $email)";
}

take a look at the raw data once again, it contains all the users data so you need to get the access the uid before the name
final uid = FirebaseAuth.instance.currentUser!.uid;
and then for the RequestModel:
name: data[uid]['name']

Related

Riverpod Model from List<Model> to Map<String>

I am new to flutter and I am a bit confused about Riverpod and have wasted a few days on this issue which is probably really easy. I have a Model, Provider and Service created with Riverpod which I will share below. I have a widget that takes a Map and an API that is structured
{
"job": [
{"feild1": "data",..},
{"feild2": "data",..},
{"feild3": "data",..}
]
}
It is being mapped as List how can I change that to Map for a child widget I have created.
This is my Provider:
final jobsDataProvider = FutureProvider<List<JobsModel>>((ref) async {
return ref.watch(jobsProvider).getJobs();
});
This is my model:
class JobsModel {
final String jobid;
final String from_state;
final String from_suburb;
final String to_state;
final String to_suburb;
final String travel_time;
final String date;
final String distance;
final String status;
final String map;
JobsModel({
required this.jobid,
required this.from_state,
required this.from_suburb,
required this.to_state,
required this.to_suburb,
required this.travel_time,
required this.date,
required this.distance,
required this.status,
required this.map,
});
factory JobsModel.fromJson(Map<String, dynamic> json) {
return JobsModel(
jobid: json['jobid'],
from_state: json['from']['state'],
from_suburb: json['from']['suburb'],
to_state: json['to']['state'],
to_suburb: json['to']['suburb'],
travel_time: json['travel_time'],
date: json['date'],
distance: json['distance'],
status: json['status'],
map: json['map'],
);
}
}
This is my service:
class ApiServices {
String endpoint = 'https://localhost:3000/jobs';
Future<List<JobsModel>> getJobs() async {
Response response = await get(Uri.parse(endpoint));
if (response.statusCode == 200) {
final List result = jsonDecode(response.body)['jobs'];
return result.map(((e) => JobsModel.fromJson(e))).toList();
} else {
throw Exception(response.reasonPhrase);
}
}
}
final jobsProvider = Provider<ApiServices>((ref) => ApiServices());
My child widget takes a Map<String, dynamic> how can I make this work so I can map multiple widgets from the returned api call into a row.
Thanks heaps all.

How to retrieve all documents in a collection in Firebase and add to a list?

I have a collection in Firebase that I am trying to retrieve and add to a list:
I also have an events model defined. Before adding the event to a list, I would like to create an Event object using the data read from Firebase.
event_model:
class Event {
String eid;
String title;
String location;
String start;
String end;
String instructor;
String image;
String description;
Event({
required this.eid,
required this.title,
required this.location,
required this.start,
required this.end,
required this.instructor,
required this.image,
required this.description
});
String getEid() {
return eid;
}
String getTitle() {
return title;
}
String getLocation() {
return location;
}
String getStart() {
return start;
}
String getEnd() {
return end;
}
String getInstructor() {
return instructor;
}
String getImage() {
return image;
}
String getDescription() {
return description;
}
void setEid(String eid) {
this.eid = eid;
}
void setTitle(String title) {
this.title = title;
}
void setLocation(String location) {
this.location = location;
}
void setStart(String start) {
this.start = start;
}
void setEnd(String end) {
this.end = end;
}
void setInstructor(String instructor) {
this.instructor = instructor;
}
void setImage(String image) {
this.image = image;
}
void setDescription(String description) {
this.description = description;
}
}
This is what I have so far. I am creating the list of Event objects then trying to get the entire collection and for each document in the collection, I am creating the Event object and trying to add it to the list. I am not sure if this is correct.
List<Event> _events = [];
Future<UserProfile> getUserProfile() async {
try {
final FirebaseAuth auth = FirebaseAuth.instance;
final snapshot = await FirebaseFirestore.instance.collection('events').get();
snapshot.docs.forEach((doc) {
Map<String, dynamic>? data = snapshot.data();
Event event = Event(
eid: data?['eid'],
title: data?['title'],
...
});
a better approach for this is that the conversation of the Map<String, dynamic> to an Event class object, should be done using a factory constructor of the Event class, and setting a default value for each property so if something goes null, your app won't crash, it will have a default value and work fine, like this:
add this to your Event class:
factory Event.fromMap(Map<String, dynamic>? map) {
return Event(
eid: map?["eid"] ?? "defaultValue,"
title: map?["title"] ?? "defaultValue",
location: map?["location"] ?? "defaultValue",
start: map?["start"] ?? "defaultValue,"
end: map?["ends"] ?? "defaultValue,"
instructor: map?["instructor"] ?? "defaultValue,"
image: map?["image"] ?? "defaultValue,"
description: map?["description"] ?? "defaultValue",
);
}
then instead of implementing your methods, save yourself from the boilerplate code by using the:
Event event = Event.fromMap(snapshot.data() as Map<String, dynamic>);
_events.add(event);

Flutter: problem in fetching data: type 'Null' is not a subtype of type 'String' error

I am trying to fetch google book search api data.
https://www.googleapis.com/books/v1/volumes?q=%EA%B2%BD%EC%A0%9C
I followed this one:
https://docs.flutter.dev/cookbook/networking/fetch-data
My class:
class Book {
final String id;
final String title;
final List<String> authors;
const Book({
required this.id,
required this.title,
required this.authors,
});
factory Book.fromJson(Map json) {
return Book(
id: json['id'],
title: json['title'],
authors: json['author'],
);
}
}
request data:
late Future<List<Book>> futureBooks;
Future<List<Book>> fetchBooks() async {
Uri url = Uri.parse(
'https://www.googleapis.com/books/v1/volumes?q=경제 경영'); //&maxResults=1
final response = await http.get(url);
if (response.statusCode == 200) {
var json = jsonDecode(response.body);
List<dynamic> items = json['items'];
List<Book> books = (items.map((item) {
return Book.fromJson(item);
})).toList();
return books;
} else {
throw Exception('Failed to load Books');
}
}
#override
void initState() {
super.initState();
futureBooks = fetchBooks();
}
I think I have same issue with this.
How to solve the "Type Null is not a subtype of type ..." error?
So I appended [?] for fields.
class Book {
final String? id;
final String? title;
final List<String>? authors;
It still give me null.
my code:
https://github.com/kangsudal/millie/blob/7f1f912c5a0eba0fe09de67c1c729be73b660da1/lib/screens/0_today/tab_widdget/tab_now.dart#L62
how to get data?
because title and author is not inside item object, it inside volumeInfo, so you much change fromJson method of your Book class to
factory Book.fromJson(Map json) {
return Book(
id: json['id'],
title: json['volumeInfo']['title'],
authors: json['volumeInfo']['author'],
);
}

Error fetching API / A value of type 'List<Heroes>' can't be returned from the method 'getHeroes' because has a return type of 'Future<List<String>?>

im new in Dart/Flutter and im struggling with consuming API, here is my file thats inside my model folder:
List<Heroes> heroesFromJson(String str) =>
List<Heroes>.from(json.decode(str).map((x) => Heroes.fromJson(x)));
String heroesToJson(List<Heroes> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Heroes {
Heroes({
required this.id,
required this.name,
required this.localizedName,
required this.primaryAttr,
required this.attackType,
required this.roles,
});
int id;
String name;
String localizedName;
String primaryAttr;
String attackType;
List<String> roles;
factory Heroes.fromJson(Map<String, dynamic> json) => Heroes(
id: json["id"],
name: json["name"],
localizedName: json["localized_name"],
primaryAttr: json["primary_attr"],
attackType: json["attack_type"],
roles: List<String>.from(json["roles"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"localized_name": localizedName,
"primary_attr": primaryAttr,
"attack_type": attackType,
"roles": List<dynamic>.from(roles.map((x) => x)),
};
}
And here is where im getting the error, inside services folder:
class DotaServices {
Future<List<String>?> getHeroes() async {
var client = http.Client();
var url = Uri.parse('https://api.opendota.com/api/heroes');
var response = await client.get(url);
if (response.statusCode == 200) {
var json = response.body;
return heroesFromJson(json);
}
}
}
The error is occuring in that line:
return heroesFromJson(json);
And the message that appears is:
A value of type 'List<Heroes>' can't be returned from the method 'getHeroes' because it has a return type of 'Future<List<String>?>'.
how to solve it? Im struggling real hard on this :/
Your method returns a list of heroes... so... you need to return a list of heroes:
Future<List<String>?> getHeroes() async {
needs to be
Future<List<Heroes>?> getHeroes() async {
heroesFromJson returns a list of heroes so getHeroes has to return a list of heroes:
Future<List<Heroes>?> getHeroes()
Also, your method heroesFromJson returns a List<Heroes> not nullable, but your method getHeroes() return a List<Heroe>? which is nullable.
You either can make your return from heroesFromJson a nullable list List<T>? or your return from getHeroes() a non-nullable list List
Be careful making your List nullable or non-nullable List<Hero>?, not your Hero List<Hero?>
It seems to me that such code should work more reliably.
return Hero.fromJsonList(json as List);
This small example (including function main) was generated with a very small script.
import 'dart:convert';
import 'package:http/http.dart' as http;
void main(List<String> args) async {
final svc = DotaServices();
final heroes = await svc.getHeroes();
print('Heroes: ${heroes.length}');
}
class DotaServices {
Future<List<Hero>> getHeroes() async {
final client = http.Client();
final url = Uri.parse('https://api.opendota.com/api/heroes');
final response = await client.get(url);
if (response.statusCode == 200) {
final source = response.body;
final json = jsonDecode(source);
return Hero.fromJsonList(json as List);
}
throw StateError('Http error: ${response.statusCode}');
}
}
class Hero {
Hero(
{required this.id,
required this.name,
required this.localizedName,
required this.primaryAttr,
required this.attackType,
required this.roles});
factory Hero.fromJson(Map json) {
return Hero(
id: json['id'] as int,
name: json['name'] as String,
localizedName: json['localized_name'] as String,
primaryAttr: json['primary_attr'] as String,
attackType: json['attack_type'] as String,
roles: json['roles'] == null
? []
: (json['roles'] as List).map((e) => e as String).toList(),
);
}
final int id;
final String name;
final String localizedName;
final String primaryAttr;
final String attackType;
final List<String> roles;
static List<Hero> fromJsonList(List json) {
return json.map((e) => Hero.fromJson(e as Map)).toList();
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'localized_name': localizedName,
'primary_attr': primaryAttr,
'attack_type': attackType,
'roles': roles,
};
}
static List<Map<String, dynamic>> toJsonList(List<Hero> list) {
return list.map((e) => e.toJson()).toList();
}
}
Using this codegen script you can generate the models and serializers.
It also generates a working example.
import 'dart:io';
import 'package:object_serializer/json_serializer_generator.dart';
import 'package:yaml/yaml.dart';
void main() {
final classes = loadYaml(_classes) as Map;
final g = JsonSerializerGenerator();
final classesCode = g.generateClasses(classes);
final values = {
'classes': classesCode,
};
var source = g.render(_template, values);
source = g.format(source);
File('bin/stackoverflow.dart').writeAsStringSync(source);
}
const _classes = '''
Hero:
fields:
id: int
name: String
localizedName: {type: String, alias: localized_name}
primaryAttr: {type: String, alias: primary_attr}
attackType: {type: String, alias: attack_type}
roles: List<String>
''';
const _template = r'''
import 'dart:convert';
import 'package:http/http.dart' as http;
void main(List<String> args) async {
final svc = DotaServices();
final heroes = await svc.getHeroes();
print('Heroes: ${heroes.length}');
}
class DotaServices {
Future<List<Hero>> getHeroes() async {
final client = http.Client();
final url = Uri.parse('https://api.opendota.com/api/heroes');
final response = await client.get(url);
if (response.statusCode == 200) {
final source = response.body;
final json = jsonDecode(source);
return Hero.fromJsonList(json as List);
}
throw StateError('Http error: ${response.statusCode}');
}
}
{{classes}}
''';

type 'Null' is not a subtype of type 'List<RestaurantModel>'

I'm new to programming and currently learning JSON. I got this error when using Cubit to access the JSON:
RestaurantFailed(type 'Null' is not a subtype of type 'List<RestaurantModel>')
JSON Sample: https://restaurant-api.dicoding.dev/list
I'm trying to access the API and insert it to RestaurantModel.
this is my code:
restaurant_service.dart
class RestaurantService {
Future<List<RestaurantModel>> fetchAllData() async {
try {
Uri url = Uri.http('restaurant-api.dicoding.dev', '/list');
http.Response response = await http.get(url);
Map<String, dynamic> result = jsonDecode(response.body);
List<RestaurantModel> restaurants = result['restaurants'].forEach((json) {
return RestaurantModel.fromJson(json: json);
});
return restaurants;
} catch (e) {
rethrow;
}
}
}
restaurant_cubit.dart
class RestaurantCubit extends Cubit<RestaurantState> {
RestaurantCubit() : super(RestaurantInitial());
void fetchData() async {
try {
emit(RestaurantLoading());
List<RestaurantModel> restaurants =
await RestaurantService().fetchAllData();
emit(RestaurantSuccess(restaurants));
} catch (e) {
emit(RestaurantFailed(e.toString()));
}
}
}
restaurant_model.dart
class RestaurantModel {
final String id;
final String name;
final String description;
final String pictureId;
final String city;
final double rating;
String? address;
List<String>? categories;
List<String>? menus;
List<CustomerReviewModel>? customerReviews;
RestaurantModel({
required this.id,
required this.name,
required this.description,
required this.pictureId,
required this.city,
this.rating = 0.0,
this.address = '',
this.categories,
this.menus,
this.customerReviews,
});
factory RestaurantModel.fromJson({required Map<String, dynamic> json}) =>
RestaurantModel(
id: json['id'],
name: json['name'],
description: json['description'],
pictureId: json['pictureId'],
city: json['city'],
rating: json['rating'].toDouble(),
address: json['address'] ?? '',
categories: json['categories'] ?? [],
menus: json['menus'] ?? [],
customerReviews: json['customerReviews'] ?? [],
);
}
any feedback or input would be very appreciated! Cheers
The forEach should be replaced by map(...).toList() like the following code snippet:
List<RestaurantModel> restaurants = result['restaurants'].map((json) {
return RestaurantModel.fromJson(json: json);
}).toList();
This is because forEach returns void and it cannot be assigned to anything. On the other hand, map returns a Iterable<RestaurantModel> and it's just a matter of converting it to list with the toList() method.