How to Map Model Class from JSON Response Flutter/MongoDB - json

I have used Node/MongoDB for my back end and everything works fine wrt requests and responses.
For my front end, I am building a mobile app with flutter and therefore have to create model classes to represent my response.
Sample response:
{success: true, message: Logged in Successfully, user: {_id: 6028965c16056b37eca50076, username: spideyr, email: peterparker#gmail.com, password: $2b$10$R4kYBA3Ezk7z2EBIY3dfk.6Qy.IXQuXJocKVS5PCzLf4fXYckUMju, phone: 89066060484, __v: 0}, accessToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MTM1NDg3ODIsImV4cCI6MTYxNjE0MDc4MiwiYXVkIjoiNjAyODk2NWMxNjA1NmIzN2VjYTUwMDc2IiwiaXNzIjoicGlja3VycGFnZS5jb20ifQ.DX8-WGRkCQ9geAaQASOIzoPGpvpjdI7aV0C5o1i5Thw, refreshToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MTM1NDg3ODIsImV4cCI6MTY0NTEwNjM4MiwiYXVkIjoiNjAyODk2NWMxNjA1NmIzN2VjYTUwMDc2IiwiaXNzIjoicGlja3VycGFnZS5jb20ifQ.RkVCGK9FfU0rxs2qf5QtJsyFaGShsL05CI320GsmAwg}
In this response body, I am only interested in the user field and therefore have created my POJO/PODO class like below:
class UserModel {
final String id;
final String username;
final String email;
final String phone;
const UserModel({
this.id,
#required this.email,
#required this.username,
#required this.phone,
});
UserModel copyWith({String id, String username, String email, String phone}){
if (
(id == null) || identical(id, this.id) &&
(username == null) || identical(id, this.username) &&
(email == null || identical(email, this.email)) &&
(phone == null || identical(phone, this.phone))) {
return this;
}
return new UserModel(
id: id ?? this.id,
username: username ?? this.username,
email: email ?? this.email,
phone: phone ?? this.phone,
);
}
static const empty = UserModel(email: '', username: null, phone: null, id: '');
#override
String toString() {
return 'User{id: $id, username: $username, email: $email, phone: $phone}';
}
factory UserModel.fromMap(Map<String, dynamic> map){
return new UserModel(
id:map['_id'], // unable to understand why it shows error here
username:map['username'],
email:map['email'],
phone:map['phone'],
);
}
Map<String, dynamic> toMap(){
return {
'id': id,
'username': username,
'email': email,
'phone': phone,
};
}
}
I am able to login and register a user but this error keeps appearing regarding my mapping from mongo db's _id to id in my model class UserModel.fromJSON() method. Here's the error:
I/flutter (19353): NoSuchMethodError: The method '[]' was called on null.
I/flutter (19353): Receiver: null
I/flutter (19353): Tried calling: []("_id")
Does anyone know what changes I need to make in my UserModel class? Thanks.

I converted your JSON to class
class Model {
Model({
this.success,
this.message,
this.user,
this.accessToken,
this.refreshToken,
});
bool success;
String message;
User user;
String accessToken;
String refreshToken;
factory Model.fromJson(Map<String, dynamic> json) => Model(
success: json["success"],
message: json["message"],
user: User.fromJson(json["user"]),
accessToken: json["accessToken"],
refreshToken: json["refreshToken"],
);
Map<String, dynamic> toJson() => {
"success": success,
"message": message,
"user": user.toJson(),
"accessToken": accessToken,
"refreshToken": refreshToken,
};
}
class User {
User({
this.id,
this.username,
this.email,
this.password,
this.phone,
this.v,
});
String id;
String username;
String email;
String password;
String phone;
int v;
factory User.fromJson(Map<String, dynamic> json) => User(
id: json["_id"],
username: json["username"],
email: json["email"],
password: json["password"],
phone: json["phone"],
v: json["__v"],
);
Map<String, dynamic> toJson() => {
"_id": id,
"username": username,
"email": email,
"password": password,
"phone": phone,
"__v": v,
};
}

thanks to everyone that tried to help me understand the issue. I was missing a return statement in my APIClient class and that ensured that all the values in my map in fromJSON method were null.
Here's the correction:
class APIClient {
dynamic post(String pathSegment, Map<String, dynamic> body) async {
Dio dio = Dio();
Response response = await dio.post(
'${APIConstants.BASE_URL}${APIConstants.AUTH_URL}/$pathSegment',
data: body
);
if(response.statusCode == 200) {
print(response.data);
return response.data; // here's the change
} else {
print(response.statusMessage);
throw Exception(response.statusMessage);
}
}
}

The problem is in the UserResponseModel class. You are assigning Map to user.
Try this:
factory UserResponseModel.fromJSON(Map<String, dynamic> json) {
return UserResponseModel(
user: UserModel.fromJSON(json['user']),
success: json['success'],
message: json['message'],
accessToken: json['accessToken'],
refreshToken: json['refreshToken'],
);
}

Related

Error in Flutter: type 'string' is not a subtype of type 'map<string, dynamic>?' in type cast

I'm trying to built an app for my college degree with flutter that has a login screen where you insert the username and password and pass to the main screen. I use retrofit for the REST API. When I press the login button with the credentials I get the error: Exception: DioError [DioErrorType.other]: type 'String' is not a subtype of type 'Map<String, dynamic>?' in type cast.
I'm very new in Flutter can you help me? Here is my code:
Api_Service
#RestApi(baseUrl: '...')
abstract class ApiService {
factory ApiService(Dio dio, {required String baseUrl}) {
dio.options = BaseOptions(
receiveTimeout: 3000,
connectTimeout: 3000,
contentType: 'application/json',
headers: <String, String>{
'Authorization': 'Basic Y29hY2g6Y29hY2g=',
'Accept': 'application/json',
},
followRedirects: false,
validateStatus: (status) {
return status! < 400;
});
return _ApiService(dio, baseUrl: baseUrl);
}
//Login Service
#POST('...')
#FormUrlEncoded()
Future<LoginResponse> login(#Body() Map<String, dynamic> body);
Api_Response
#JsonSerializable()
class LoginResponse {
//show login response data
#JsonKey(name: 'Status')
final int statusCode;
#JsonKey(name: 'Message')
final String message;
#JsonKey(name: 'Content')
final UserEntity userEntity;
LoginResponse(this.statusCode, this.message, this.userEntity);
factory LoginResponse.fromJson(Map<String, dynamic> json) =>
_$LoginResponseFromJson(json);
Map<String, dynamic> toJson() => _$LoginResponseToJson(this);
}
User_entity
import 'package:json_annotation/json_annotation.dart';
part 'user_entity.g.dart';
//done this file
#JsonSerializable()
class UserEntity {
#JsonKey(name: 'id')
final String id;
#JsonKey(name: 'username')
final String username;
#JsonKey(name: 'role')
final String role;
UserEntity(this.id, this.username, this.role);
factory UserEntity.fromJson(Map<String, dynamic> json) =>
_$UserEntityFromJson(json);
Map<String, dynamic> toJson() => _$UserEntityToJson(this);
}
User
class User {
String? id;
String? username;
String? role;
String? token;
String? renewalToken;
User({this.id, this.username, this.role, this.token, this.renewalToken});
factory User.fromJson(Map<String, dynamic> responseData) {
return User(
id: responseData['id'],
username: responseData['username'],
role: responseData['role'],
token: responseData['token'],
renewalToken: responseData['token'],
);
}
User_provider
class UserProvider extends ChangeNotifier {
User _user = User();
User get user => _user;
void setUser(User? user) {
_user = user!;
notifyListeners();
}
}
Auth_provider
enum Status { NotLoggedIn, LoggedIn, Authenticating, LoggedOut }
class AuthProvider extends ChangeNotifier {
Status _loggedInStatus = Status.NotLoggedIn;
Status get loggedInStatus => _loggedInStatus;
set loggedInStatus(Status value) {
_loggedInStatus = value;
}
static Future<FutureOr> onValue(Response response) async {
var result;
final Map<String, dynamic> responseData = json.decode(response.body);
print(responseData);
if (response.statusCode == 200) {
// now we will create a user model
User authUser = User.fromJson(responseData);
// now we will create shared preferences and save data
UserPreferences().saveUser(authUser);
result = {
'status': true,
'message': 'Successfully registered',
'data': authUser
};
} else {
result = {
'status': false,
'message': 'Successfully registered',
'data': responseData
};
}
return result;
}
Future<Map<String, dynamic>> login(String username, String password) async {
var result;
Map<String, dynamic> loginData = {
'Username': username,
'Password': password,
};
_loggedInStatus = Status.Authenticating;
notifyListeners();
ApiService apiService = ApiService(dio.Dio(), baseUrl: '');
final response = await apiService.login(loginData);
print('${response.toJson()}');
if (response.statusCode == 200) {
User authUser = User(
id: response.userEntity.id,
username: response.userEntity.username,
role: response.userEntity.role,
);
UserPreferences().saveUser(authUser);
_loggedInStatus = Status.LoggedIn;
notifyListeners();
result = {'status': true, 'message': 'Successful', 'user': authUser};
} else {
_loggedInStatus = Status.NotLoggedIn;
notifyListeners();
result = {'status': false, 'message': ''};
}
return result;
}
onError(error) {
print('the error is ${error.detail}');
return {'status': false, 'message': 'Unsuccessful Request', 'data': error};
}
}
Main
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Future<User> getUserData() => UserPreferences().getUser();
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => UserProvider())
],
child: MaterialApp(
theme: ThemeData(
backgroundColor: Color(0Xfff7f7f5),
fontFamily: 'Cera',
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
),
),
debugShowCheckedModeBanner: false,
// home: LoginScreen(),
home: FutureBuilder<User>(
future: getUserData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else if (snapshot.data!.token == null) {
return LoginScreen();
} else
Provider.of<UserProvider>(context).setUser(snapshot.data);
return TeamsScreen();
}
}),
routes: {
'/auth': (context) => LoginScreen(),
'/teams': (context) => TeamsScreen(),
},
));
}
}
shared_preferences
class UserPreferences {
Future<bool> saveUser(User user) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('id', user.id as String);
prefs.setString('username', user.username as String);
prefs.setString('role', user.role as String);
prefs.setString('token', user.token as String);
return saveUser(user);
}
Future<User> getUser() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String id = prefs.getString("id") ?? '';
String username = prefs.getString("username") ?? '';
String role = prefs.getString("role") ?? '';
String token = prefs.getString("token") ?? '';
String renewalToken = prefs.getString("renewalToken") ?? '';
return User(
id: id,
username: username,
role: role,
token: token,
renewalToken: renewalToken);
}
void removeUser() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove('id');
prefs.remove('username');
prefs.remove('role');
prefs.remove('token');
}
Future<String?> getToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString("token");
return token;
}
}
That's because the server is returning a json string with a different encoding than application/json. Check your response headers and you should probably see that the Content-Type of your response is not application/json but something else. I had the error when getting a GitLab snippet that was not returned as actual json but as a string.
You have two solutions here :
Ask your server for application/json Content-Type, and if he's a nice server he will comply to your request and return proper json. You can do that by setting HTTP headers either in Dio global options or for a specific request.
In your case it seems you already have tried this option but the server didn't want to comply to your request, so you should probably try option 2.
Add a Dio interceptor that will do the conversion from a String to a Map<dynamic, String> before it's actually treated by Retrofit generated code :
final dio = Dio()
..interceptors.add(
InterceptorsWrapper(
onResponse: (response, handler) {
if (response.requestOptions.method == HttpMethod.GET) {
response.data = jsonDecode(response.data as String);
}
return handler.next(response);
},
),
);

User model sends null data to the api

I have an api where i get my users data. I made a user model which holds the data of the user after decoding it. After making some changes and POST to the user resource in the api (e.g profile name) the toJson() method of the user model sends other user object as null values to the api, excluding the updated field.
Expected behavior:
The toJson() method supposed to check for null objects and replace it with the previous data gotten from the api instead of sending null to the api.
Is there any way to make the user model holds the user value when sending to the api?
This is my UserModel class
String userModelToJson(UserModel data) => jsonEncode(data);
#JsonSerializable()
class UserModel {
UserModel({
this.type,
this.bio,
this.name,
this.interests,
this.presentation,
this.links,
this.location,
this.school,
this.occupation,
this.createdAt,
this.lastUpdatedAt,
});
final String? type;
final String? bio;
final String? name;
final List<dynamic>? interests;
final Presentation? presentation;
final Links? links;
final Location? location;
final School? school;
final Occupation? occupation;
final int? createdAt;
final int? lastUpdatedAt;
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
}
json_serializable of user model
UserModel _$UserModelFromJson(Map<String, dynamic> json) => UserModel(
type: json['type'] as String?,
bio: json['bio'] as String?,
name: json['name'] as String?,
interests: json['interests'] as List<dynamic>?,
presentation: json['presentation'] == null
? null
: Presentation.fromJson(json['presentation'] as Map<String, dynamic>),
links: json['links'] == null
? null
: Links.fromJson(json['links'] as Map<String, dynamic>),
location: json['location'] == null
? null
: Location.fromJson(json['location'] as Map<String, dynamic>),
school: json['school'] == null
? null
: School.fromJson(json['school'] as Map<String, dynamic>),
occupation: json['occupation'] == null
? null
: Occupation.fromJson(json['occupation'] as Map<String, dynamic>),
createdAt: json['createdAt'] as int?,
lastUpdatedAt: json['lastUpdatedAt'] as int?,
);
Map<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
'type': instance.type,
'bio': instance.bio,
'name': instance.name,
'interests': instance.interests,
'presentation': instance.presentation,
'links': instance.links,
'location': instance.location,
'school': instance.school,
'occupation': instance.occupation,
};
Get user method
Future<UserModel> getUserData() async {
UserModel? user;
final token = await _storage.getKey("Token");
ApiService.client.options.headers['Authorization'] = 'Bearer $token';
final response = await ApiService.client.get(ApiEndpoint.profile);
if (response.statusCode == 200 || response.statusCode == 201) {
user = UserModel.fromJson(response.data);
return user;
} else {
throw "Couldn't load user";
}
}
create file static_variable.dart
class StaticVariable {
static UserModel? user;
}
save user data to StaticVariable
Future<UserModel> getUserData() async {
UserModel? user;
final token = await _storage.getKey("Token");
ApiService.client.options.headers['Authorization'] = 'Bearer $token';
final response = await ApiService.client.get(ApiEndpoint.profile);
if (response.statusCode == 200 || response.statusCode == 201) {
user = UserModel.fromJson(response.data);
StaticVariable.user = user; //save user variable to StaticVariable
return user;
} else {
throw "Couldn't load user";
}
}
use ?? operation for check null
Map<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
'type': instance.type ?? StaticVariable!.user.type,
'bio': instance.bio ?? StaticVariable!.user.bio,
'name': instance.name ?? StaticVariable!.user.name,
'interests': instance.interests ?? StaticVariable!.user.interests,
'presentation': instance.presentation ?? StaticVariable!.user.presentation,
'links': instance.links ?? StaticVariable!.user.links,
'location': instance.location ?? StaticVariable!.user.location,
'school': instance.school ?? StaticVariable!.user.school,
'occupation': instance.occupation ?? StaticVariable!.user.ccupation,
};
'type': instance.type ?? "", mean:
if(instance.type == null){
'type': StaticVariable.user.type
} else {
'type': instance.type
}

How to display different different data form in Json response?

I'm successfully got responded from my API , also able to display Status and Message in my App.
But How to display Name & EncUserId as it is inside UserData
Here is my JSON response in Postman
{
"Status": "1",
"Message": "You are Logged in successfully",
"UserData": {
"Name": "qwerty#gmail.com",
"EncUserId": "GO9gj3aSUKCpxE3AMSbh/A=="
}
}
Im Displaying my Json response in my App in this format
Text("Status: ${widget.response.status}"),
Text("Message: ${widget.response.message}"),
Here is API model
class ApiResponse {
ApiResponse({
required this.status,
required this.message,
required this.userData,
});
String status;
String message;
UserData? userData;
factory ApiResponse.fromJson(Map<String, dynamic> json) => ApiResponse(
status: json["Status"],
message: json["Message"],
userData: json["UserData"] == null? null:UserData.fromJson(json["UserData"] as Map<String, dynamic>),
);
}
class UserData {
UserData({
required this.name,
required this.encUserId,
});
String name;
String encUserId;
factory UserData.fromJson(Map<String, dynamic> json) => UserData(
name: json["Name"],
encUserId: json["EncUserId"],
);
}
This will work
Text("name: ${widget.response.userData.name}"),
Text("encUserId: ${widget.response.userData.encUserId}")

Json Status=0 not displaying

I'm trying to show my json response after user login. It successfully displaying response in my app when user put correct info. But the app getting crashed when user putting wrong credentials
Here is my json response when user put wrong info. It should displaying Status=0 in my app display(or whatever response I want to display
I/flutter (16426): {"Status":"0","Message":"Wrong Password provided","UserData":null}
Here is my API call when user press the login button
Future<void> login() async{
var jsonResponse = null;
if (passwordontroller.text.isNotEmpty && emailController.text.isNotEmpty) {
var response = await http.post(Uri.parse("http://jhhjhjhjhjhjhj"),
body: ({
'LoginId': emailController.text,
'Password': passwordontroller.text
}));
if (response.statusCode == 200) {
print("Correct");
print(response.body);
jsonResponse = json.decode(response.body.toString());
print(jsonResponse);
Navigator.push(context, MaterialPageRoute(builder: (context)=>AfterLoginResPage(response: ApiResponse.fromJson(jsonResponse))));
}
else {
print("Wronggooooooooooooooooooooooooooo");
print(response.body);
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Invalid credentials")));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Blank field is not allowed")));
}
}
My Model class
class ApiResponse {
ApiResponse({
required this.status,
required this.message,
required this.userData,
});
String status;
String message;
UserData userData;
factory ApiResponse.fromJson(Map<String, dynamic> json) => ApiResponse(
status: json["Status"],
message: json["Message"],
//userData: UserData.fromJson(json["UserData"]),
userData: json["UserData"] == null? null:UserData.fromJson(json["UserData"]), //======== Updated
);
}
class UserData {
UserData({
required this.name,
required this.encUserId,
});
String name;
String encUserId;
factory UserData.fromJson(Map<String, dynamic> json) => UserData(
name: json["Name"],
encUserId: json["EncUserId"],
);
}
And then here I'm displaying my response which is successfully getting executed when User putting correct credentials. but got crashed when putting wrong credentials
children: [
Text("Status: ${widget.response.status}"),
Text("Message: ${widget.response.message}"),
one more problem I'm facing ,here im able to display status and message What If I want to display Name & EncUserId also?.
here is my json response in postman after putting right info
{
"Status": "1",
"Message": "You are Logged in successfully",
"UserData": {
"Name": "tuhinroy881#gmail.com",
"EncUserId": "bbA/HajfPdswT0fhhiMvEg=="
}
}
.
Since null safety is enabled, you need to change UserData userData to UserData? userData; and userData: json["UserData"] == null? null:UserData.fromJson(json["UserData"] as Map<String, dynamic>)
Your problem is that you are trying to parse UserData while your error response doesn't have any UserData related information.
class ApiResponse {
ApiResponse({
required this.status,
required this.message,
required this.userData,
});
String status;
String message;
UserData? userData;
factory ApiResponse.fromJson(Map<String, dynamic> json) => ApiResponse(
status: json["Status"],
message: json["Message"],
userData: json["UserData"] == null? null:UserData.fromJson(json["UserData"]),
);
}

Dart - error doing deserialization of FCM Json notification

I have an app made in flutter 1.22.2, and I'm putting together a "Notification Center" view. When the notification arrives at the device, it arrives in json format, and I'm trying to make a decode, to display it in this view.
fcm.configure(
onMessage: (message) async {
try {
print("onMessage: $message");
PushDecode.fromJson(message);
print(PushDecode().notification.title);
} catch (e) {
print(e);
}
return message;
},
onLaunch: (message) async {
print("onLaunch: $message");
},
onResume: (message) async {
print("onResume: $message");
},
);
My decode class:
// final pushDecode = pushDecodeFromJson(jsonString);
import 'dart:convert';
PushDecode pushDecodeFromJson(String str) => PushDecode.fromJson(json.decode(str));
String pushDecodeToJson(PushDecode data) => json.encode(data.toJson());
class PushDecode {
PushDecode({
this.data,
this.notification,
});
Data data;
PushNotification notification;
factory PushDecode.fromJson(Map<dynamic, dynamic> json) => PushDecode(
data: Data.fromJson(json["data"]),
notification: PushNotification.fromJson(json["notification"]),
);
Map<dynamic, dynamic> toJson() => {
"data": data.toJson(),
"notification": notification.toJson(),
};
}
class Data {
Data({
this.title,
this.message,
});
String title;
String message;
factory Data.fromJson(Map<dynamic, dynamic> json) => Data(
title: json["title"],
message: json["message"],
);
Map<dynamic, dynamic> toJson() => {
"title": title,
"message": message,
};
}
class PushNotification {
PushNotification({
this.body,
this.title,
});
String body;
String title;
factory PushNotification.fromJson(Map<dynamic, dynamic> json) =>
PushNotification(
body: json["body"],
title: json["title"],
);
Map<dynamic, dynamic> toJson() => {
"body": body,
"title": title,
};
}
I have received this error when I run the code.
I/flutter (xx): onMessage: {notification: {title: Test one, body: Test one body}, data: {title: number 1, message: number too}}
I/flutter (xx): NoSuchMethodError: The getter 'title' was called on null.
I/flutter (xx): Receiver: null
I/flutter (xx): Tried calling: title
What happens is that the onMessage () try goes straight through the decode class, and the error pops up, returning null, what should I do?
Try using PushDecode.fromJson(jsonDecode(message.data));
You might be getting it as a string.