Trying to fetch data from an API with a GET method, but it keeps showing this error: `
"Error: type ‘Null’ is not a subtype of type ‘Map<String, dynamic>’ in
type cast"
` why? I am making use of the json_serializable and the json_annotation dart packages as seen in the code below… It keeps returning the error as shown.
// This is my class for fetching the data...
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:big_shelf/screens/models/models.dart';
class MyProfileApiServices {
final String myProfileUrl = 'https://bigshelf-node-dev.onrender.com/api/v1/users/my-profile';
Future<UserModel> fetchUserData() async {
final response = await http.get(Uri.parse(myProfileUrl));
if (response.statusCode == 200) {
final jsonResponse = jsonDecode(response.body);
final userData = UserModel.fromJson(jsonResponse);
debugPrint('$userData');
return userData;
} else {
debugPrint('Failed to load data');
throw Exception('Failed to load user data');
}
}
}
// The below is where I want to display the data that has been fetched...
import 'widget.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../services/users/users_services.dart';
import 'package:big_shelf/screens/models/models.dart';
import 'package:big_shelf/screens/common/widget.dart';
class MyShelf extends StatefulWidget {
const MyShelf({super.key});
@override
State<MyShelf> createState() => _MyShelfState();
}
class _MyShelfState extends State<MyShelf> {
late Future<UserModel> futureUser;
@override
void initState() {
super.initState();
futureUser = MyProfileApiServices().fetchUserData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'My Shelf',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
fontFamily: 'Playfair',
),
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: [
IconButton(
onPressed: () {
GoRouter.of(context).go('${CustomNavigationHelper.myShelfPath}/${CustomNavigationHelper.settingsPath}');
},
icon: SvgPicture.asset(
'assets/icons/Settings.svg',
colorFilter: ColorFilter.mode(
Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
BlendMode.srcIn,
),
),
),
InkWell(
onTap: () => GoRouter.of(context).go('${CustomNavigationHelper.myShelfPath}/${CustomNavigationHelper.notificationPath}'),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Stack(
alignment: AlignmentDirectional.topEnd,
children: [
SvgPicture.asset(
width: 25,
height: 23,
'assets/icons/Bell.svg',
colorFilter: ColorFilter.mode(
Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
BlendMode.srcIn,
),
),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
shape: BoxShape.circle,
),
child: const Text(
'9',
style: TextStyle(
fontSize: 10.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
],
),
),
),
],
),
),
],
),
body: FutureBuilder<UserModel>(
future: futureUser,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
debugPrint('Error: ${snapshot.error}');
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
final user = snapshot.data!.data.user;
return SingleChildScrollView(
child: Column(
children: [
Container(
alignment: Alignment.center,
padding: const EdgeInsets.fromLTRB(0, 20, 0, 10),
color: Colors.grey.withOpacity(0.1),
child: Column(
children: [
CircleAvatar(
radius: 50.0,
backgroundColor: Colors.grey[200],
backgroundImage: const AssetImage('assets/images/Profile icon.png'),
),
const SizedBox(height: 10),
Text(
user.username,
style: const TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.bold,
fontFamily: 'Playfair',
),
),
Text(
'${snapshot.data!.data.followersCount} followers, ${snapshot.data!.data.followingCount} following',
style: const TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w300,
fontFamily: 'Satoshi',
),
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 80),
child: ElevatedButton(
onPressed: () => GoRouter.of(context).go('${CustomNavigationHelper.myShelfPath}/${CustomNavigationHelper.createCollectionPath}'),
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
),
borderRadius: BorderRadius.circular(10),
),
minimumSize: const Size(30, 60),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add,
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
),
const SizedBox(width: 10),
Text(
'Create a collection',
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 15.0,
color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
),
),
],
),
),
),
],
),
),
const SizedBox(height: 10),
Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: const Text(
'Your Shelf',
style: TextStyle(
fontFamily: 'Satoshi',
fontSize: 17.0,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
),
const SizedBox(height: 10),
DefaultTabController(
length: 3,
child: Column(
children: [
Container(
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.fromLTRB(15, 0, 15, 0),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10)),
color: Colors.grey.withOpacity(0.1),
),
child: TabBar(
indicatorSize: TabBarIndicatorSize.tab,
dividerColor: Colors.transparent,
indicatorColor: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black,
indicator: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
color: Colors.white,
),
tabs: [
Tab(
child: Text(
'Collections',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 12.0,
color: Theme.of(context).brightness == Brightness.dark ? Colors.grey : Colors.black,
),
),
),
Tab(
child: Text(
'Followers',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 12.0,
color: Theme.of(context).brightness == Brightness.dark ? Colors.grey : Colors.black,
),
),
),
Tab(
child: Text(
'Following',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 12.0,
color: Theme.of(context).brightness == Brightness.dark ? Colors.grey : Colors.black,
),
),
),
],
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: const TabBarView(
children: [
CollectionsMyShelf(),
FollowersMyShelf(),
FollowingMyShelf(),
],
),
),
],
),
),
],
),
);
} else {
return const Center(child: Text('No data found'));
}
},
),
);
}
}
import 'package:json_annotation/json_annotation.dart';
part 'user_model.g.dart';
@JsonSerializable(explicitToJson: true)
class UserModel {
final String status;
final UserData data;
UserModel({
required this.status,
required this.data,
});
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
}
@JsonSerializable(explicitToJson: true)
class UserData {
final User user;
final int followersCount;
final int followingCount;
UserData({
required this.user,
required this.followersCount,
required this.followingCount,
});
factory UserData.fromJson(Map<String, dynamic> json) => _$UserDataFromJson(json);
Map<String, dynamic> toJson() => _$UserDataToJson(this);
}
@JsonSerializable(explicitToJson: true)
class User {
@JsonKey(name: '_id')
final String id;
final String name;
final String username;
final String email;
final String role;
final List<String> savedCollection;
final List<dynamic> cart;
final List<dynamic> address;
final int v;
final String bankName;
final String accountName;
final bool activeAffiliate;
final int redeemable;
final int redeemed;
final DateTime updatedAffiliateAt;
User({
required this.id,
required this.name,
required this.username,
required this.email,
required this.role,
required this.savedCollection,
required this.cart,
required this.address,
required this.v,
required this.bankName,
required this.accountName,
required this.activeAffiliate,
required this.redeemable,
required this.redeemed,
required this.updatedAffiliateAt,
});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
UserModel _$UserModelFromJson(Map<String, dynamic> json) => UserModel(
status: json['status'] as String,
data: UserData.fromJson(json['data'] as Map<String, dynamic>),
);
Map<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
'status': instance.status,
'data': instance.data.toJson(),
};
UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
user: User.fromJson(json['user'] as Map<String, dynamic>),
followersCount: (json['followersCount'] as num).toInt(),
followingCount: (json['followingCount'] as num).toInt(),
);
Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
'user': instance.user.toJson(),
'followersCount': instance.followersCount,
'followingCount': instance.followingCount,
};
User _$UserFromJson(Map<String, dynamic> json) => User(
id: json['_id'] as String,
name: json['name'] as String,
username: json['username'] as String,
email: json['email'] as String,
role: json['role'] as String,
savedCollection: (json['savedCollection'] as List<dynamic>)
.map((e) => e as String)
.toList(),
cart: json['cart'] as List<dynamic>,
address: json['address'] as List<dynamic>,
v: (json['v'] as num).toInt(),
bankName: json['bankName'] as String,
accountName: json['accountName'] as String,
activeAffiliate: json['activeAffiliate'] as bool,
redeemable: (json['redeemable'] as num).toInt(),
redeemed: (json['redeemed'] as num).toInt(),
updatedAffiliateAt: DateTime.parse(json['updatedAffiliateAt'] as String),
);
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
'_id': instance.id,
'name': instance.name,
'username': instance.username,
'email': instance.email,
'role': instance.role,
'savedCollection': instance.savedCollection,
'cart': instance.cart,
'address': instance.address,
'v': instance.v,
'bankName': instance.bankName,
'accountName': instance.accountName,
'activeAffiliate': instance.activeAffiliate,
'redeemable': instance.redeemable,
'redeemed': instance.redeemed,
'updatedAffiliateAt': instance.updatedAffiliateAt.toIso8601String(),
};
2
Answers
here, a null value is coming from API into some field of Map type in this model. just check for nullable model fields
in the user class ensure passing the key coming from API response whith same name for key
if tou generate class manully try to use some website to generate class in right method like this website then in function fetchUserData befor assigning the data for model try to print it to view ehat the value comes error in response