How to cast dynamic to T or List<T> - flutter

I have a service to call api and some methods expects just Object and the rest expects list of objects. And I'm trying some tricks with generics but then errors in _get method with casting occurs.
// List
Future<List<ToDo>> getToDos() async {
final List<Map<String, dynamic>> response =
await _get<List<Map<String, dynamic>>>(url: "ToDos");
final List<ToDo> result =
response.map((data) => ToDo.fromJson(data)).toList();
return result;
//Object
Future<ToDo> getToDo() async {
final Map<String, dynamic> response =
await _get<Map<String, dynamic>>(url: "ToDo");
final ToDo result = ToDo.fromJson(response);
return result;
}
Future<T> _get<T>({
required String url,
}) async {
final Response response = await get(
Uri.https(_apiURL, url),
);
if (response.statusCode == 200) {
final T jsonResponse = json.decode(response.body) as T;
return jsonResponse;
} else {
throw Exception(response.body);
}
}
Error:
Exception has occurred. _CastError (type 'List<dynamic>' is not a subtype of type 'List<Map<String, dynamic>>' in type cast)
Is it even possible to solve it like I'm trying?
[Update]:
Solution:
Future<T> _get<T, K>({
required String url,
}) async {
final Response response = await get(
Uri.https(_apiURL, url),
);
if (response.statusCode == 200) {
final dynamic jsonResponse = json.decode(response.body);
if (jsonResponse is List) {
return List<K>.from(jsonResponse) as T;
} else {
return jsonResponse as T;
}
} else {
throw Exception(response.body);
}
}

There's a list function called cast. You can cast a dynamic list to List<Map<String, dynamic>> like this:
final aList=[]//your list;
final castedList=aList.cast<Map<String, dynamic>>();

Related

Flutter - Firstore returns null data even when data is there

I have the data which stored in firestore database. But when I try to fetch the data it returns null.
Here is the code,
Future<Map<String, dynamic>> getDocumentInfo(String collectionName, String docId) async {
DocumentSnapshot document = await FirebaseFirestore.instance.collection(collectionName).doc(docId).get();
print(document.data());
Map<String, dynamic> docInfo = document.data() as Map<String, dynamic>;
return docInfo;
}
Can anyone say what is the error? It gives the error "_CastError (type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast)"
Check if something related to this works ... it's not a snapshot but it'll give you the idea.
Future<void> addNewQuestion(Map AddQuestionData, File image) async {
CommanDialog.showLoading();
var pathimage = image.toString();
var temp = pathimage.lastIndexOf('/');
var result = pathimage.substring(temp + 1);
final ref = FirebaseStorage.instance.ref().child('').child(result);
var response = await ref.putFile(image);
var imageUrl = await ref.getDownloadURL();
CommanDialog.hideLoading();
try {
CommanDialog.showLoading();
var response = await firebaseInstance.collection('QuestionList').add({
'QuestionId': AddQuestionData['QuestionId'],
'QuestionText': AddQuestionData['QuestionText'],
"QuestionImageURL": imageUrl,
'AnswerA': AddQuestionData['AnswerA'],
"AnswerB": AddQuestionData['AnswerB'],
"AnswerC": AddQuestionData['AnswerC'],
"AnswerD": AddQuestionData['AnswerD'],
"AskedIn": AddQuestionData['AskedIn'],
"SubjectName": AddQuestionData['SubjectName'],
"ChapterName": AddQuestionData['ChapterName'],
"CorrectAnswer": AddQuestionData['CorrectAnswer'],
});
CommanDialog.hideLoading();
Get.back();
} catch (exception) {
CommanDialog.hideLoading();
}
}

Generic deserialization in Flutter / Dart

I'm trying to write a HTTP driver class that takes in a generic class and deserializes the response. I haven't found a good, clean way to do this in Flutter.
I've defined datamodel classes like this:
class MyClass {
String field1;
String field2;
MyClass.fromJson(Map<dynamic, dynamic> json)
: field1 = json["field1"],
field2 = json["field2"];
}
This works well and good if I do it manually...
MyClass makeRequest() {
Response response = http.get(url);
MyClass class = MyClass.fromJson(jsonDecode(response.body));
return class;
}
What I want, is to make a generic HTTP driver like this:
void makeRequest<T>() {
Response response = http.get(url);
T parsed = T.fromJson(jsonDecode(response.body));
return parsed;
}
Is there a way to do this in Flutter/Dart? I've been trying to figure out the right syntax to use a base class and extends but haven't gotten it. Any ideas?
This is what I usually use in my network call, feel free to use. Btw, I recommend the dio package for convenient headers and params config, as well as other error handling features.
// Define an extension
extension BaseModel on Type {
fromJson(Map<String, dynamic> data) {}
}
// For single object
Future<T> makeGetRequest<T>({String url, Map<String, dynamic> params}) {
return http
.get(buildUrl(url, params)) // Don't need the buildUrl() if you use Dio
.then((response) => handleJsonResponse(response))
.then((data) => T.fromJson(data));
// For list of object
Future<List<T>> makeGetRequestForList<T>({String url, Map<String, dynamic> params}) {
return http
.get(buildUrl(url, params)) // Don't need the buildUrl() if you use Dio
.then((response) => handleJsonResponse(response))
.then((data) => List<T>.from(data.map((item) => T.fromJson(item)));
}
// Helper classes without Dio
String buildUrl(String url, [Map parameters]) {
final stringBuilder = StringBuffer(url);
if (parameters?.isNotEmpty == true) {
stringBuilder.write('?');
parameters.forEach((key, value) => stringBuilder.write('$key=$value&'));
}
final result = stringBuilder.toString();
print(result);
return result;
}
// With Dio, you can simply do this:
final res = await API().dio
.get(url, queryParameters: params) // Don't need the [buildUrl] here
.then((response) => handleJsonResponse(response))
.then((data) => T.fromJson(data));
// Handle JSON response
handleJsonResponse(http.Response response, [String endpoint = '']) {
print(
'API: $endpoint \nCODE: ${response.statusCode} \nBODY: ${response.body}');
if (_okStatus.contains(response.statusCode)) {
return jsonDecode(response.body);
}
if (response.statusCode == HttpStatus.unauthorized) {
throw Exception(response.statusCode);
} else {
throw Exception("HTTP: ${response.statusCode} ${response.body}");
}
}
Usage:
// Example class
class Post {
final String title;
Post({this.title});
#override
Post.fromJson(Map<String, dynamic> data) : title = data['title'];
}
// Use the function
Future<Post> getPost() async {
final result = await makeGetRequest<Post>(params: {'post_id': 1});
return result;
}

Question mark converted to %3F in URI

I'm working on a project and I'm trying to get information from an API. When I write the link it doesn't detect the character "?" and it substitutes this char for "%3F" so I can't access to the API.
final String _charactersUrl = '/api/character/?page=2';
I get status code 500 from the API:
https://rickandmortyapi.com/api/character/%3Fpage=3
The class that gets information from the API
class Api {
final String _baseUrl = 'rickandmortyapi.com';
final String _charactersUrl = '/api/character/?page=2';
final String _charactersJsonKey = 'results';
final HttpClient _httpClient = HttpClient();
Future<List<Character>> getCharacters() async {
final uri = Uri.https(_baseUrl, _charactersUrl);
final response = await _getJson(uri);
if (response == null || response[_charactersJsonKey] == null) {
print('Api.getCharacters(): Error while retrieving characters');
return null;
}
return _convert(response[_charactersJsonKey]);
}
Future<Map<String, dynamic>> _getJson(Uri uri) async {
try {
final request = await _httpClient.getUrl(uri);
final response = await request.close();
if (response.statusCode != HttpStatus.OK) {
print('Api._getJson($uri) status code is ${response.statusCode}');
return null;
}
final responseBody = await response.transform(utf8.decoder).join();
return json.decode(responseBody);
} on Exception catch (e) {
print('Api._getJson($uri) exception thrown: $e');
return null;
}
}
List<Character> _convert(List charactersJson) {
List<Character> characters = <Character>[];
charactersJson.forEach((character) {
characters.add(Character.fromJson(character));
});
return characters;
}
}
I would be very grateful if someone could help me. Thanks!
The Uri class expects you to use the Uri.https constructor differently.
The third positional parameter is queryParameters, which you should use instead of passing your query parameters to the unencodedPath:
final String _baseUrl = 'rickandmortyapi.com';
final String _charactersPath = '/api/character/';
final Map<String, String> _queryParameters = <String, String>{
'page': '2',
};
Future<List<Character>> getCharacters() async {
final uri = Uri.https(_baseUrl, _charactersPath, _queryParameters);
...

Getting this error - type 'Future<dynamic>' is not a subtype of type 'List<dynamic>'

Whenever trying to call future data and trying converting to List, it returns the error
type 'Future' is not a subtype of type 'List'
Tried type-casting, but no help
On HomePage.dart
final getPost = NetworkFile().getPosts();
List posts;
void getPostsList() {
setState(() {
var res = getPost;
posts = res as List<dynamic>;
print(posts);
});
}
On Network.dart
class NetworkFile{
Future<dynamic> getPosts() async {
var response = await http.get('$kBlogURL' + 'posts?_embed');
Iterable resBody = await jsonDecode(response.body.toString());
return resBody;
}
}
You are decoding the response and its a List of type dynamic. There are few method to handle it. You can create a simple PODO class and cast/mapped to it. Or just do like below:
List posts = [];
void getPostsList() async {
final fetchedPosts = await NetworkFile().getPosts();
setState(() {
posts = fetchedPosts;
});
print(posts);
}
Here is a nice article about PODO.
final getPost = NetworkFile().getPosts();
Map posts;
void getPostsList() async {
var res = await getPost;
setState(() {
posts = res as Map<String, dynamic>;
print(posts);
});
}
class NetworkFile {
Future<dynamic> getPosts() async {
var response = await http.get('https://onetechstop.net/wp-json/wp/v2');
var resBody = await jsonDecode(response.body.toString());
return resBody;
}
}

Type Future<dynamic> is not a subtype of type FutureOr<List<Status>>

I need your help..
I have a list with CheckboxListTile. If I checked or unchecked a item, the list reload. So I tried it with an AsyncMemoizer.
Now the following error is returned: 'Future<dynamic> is not a subtype of type Future<List<Status>>'
Maybe you have other ideas as I can solve the problem with the list differently
final AsyncMemoizer _memoizer = AsyncMemoizer();
Future<List<Status>> fetchPost(){
return _memoizer.runOnce(() async {
final response =
await http.get(URL);
if (response.statusCode == 200) {
List<Status> tmp = Status.fromJsonList(json.decode(response.body));
return tmp;
} else {
throw Exception('Failed to load post');
}
});
}
static List<Status> fromJsonList(List<dynamic> json) {
List<Status> ret = new List<Status>();
for (int i=0;i<json.length;i++){
Status itm = Status(
id: json[i]['I'],
geschlecht: json[i]['G'],
adresse: json[i]['A'],
name: json[i]['AZ'],
//Checkbox
isSelected : isValueChange(i)
);
ret.add(itm);
}
return ret;
}
You need to tell your AsyncMemoizer what type to expect:
final AsyncMemoizer<List<Status>> _memoizer = AsyncMemoizer<List<Status>>();