How do I Display Nested json array in flutter? - flutter

Whenever I try accessing a field from the nested array I get the following Range error. Not sure what's gone wrong, any help would be appreciated.
RangeError (index): Invalid value: Only valid value is 0: 1
This is my User Model :
import 'dart:convert';
List<User> userFromJson(String str) =>
List<User>.from(json.decode(str).map((x) => User.fromJson(x)));
class User {
User({
required this.userName,
required this.facilities,
});
final String userName;
final List<Facility>? facilities;
factory User.fromJson(Map<String, dynamic> json) => User(
userName: json["userName"],
facilities: json["facilities"] == null
? null
: List<Facility>.from(
json["facilities"].map((x) => Facility.fromJson(x))),
);
}
class Facility {
Facility({
required this.id,
required this.employeeGlobalId,
required this.facilityId,
});
final int id;
final int employeeGlobalId;
final int facilityId;
factory Facility.fromJson(Map<String, dynamic> json) => Facility(
id: json["id"],
employeeGlobalId: json["employeeGlobalId"],
facilityId: json["facilityId"],
);
}
This is how I connect with the API and convert the response to a list.
class ApiService {
final url = Uri.parse("http://47.254.237.237:81/api/Users/GetAllUser");
Map<String, String> headers = {
"Accept": 'application/json',
'content-type': 'application/json',
'Authorization':
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyIiwibmJmIjoxNjY2OTU0NTEzLCJleHAiOjE2NjcwNDA5MTMsImlhdCI6MTY2Njk1NDUxM30.EXEzTX8-MHQqZuuILwHGoQ0Vpw2fAgsi2QypGNFgMAE',
'userId': '62'
};
Future<List<User>> fetchUsers(http.Client client) async {
final response = await client.get(url, headers: headers);
return compute(parseUsers, response.body);
}
List<User> parseUsers(String response) {
final parsed = jsonDecode(response).cast<Map<String, dynamic>>();
return parsed.map<User>((json) => User.fromJson(json)).toList();
}
}
Lastly, This is how I am trying to display the facility ID.
ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].userName),
subtitle: Text(users[index].facilities![index].facilityId.toString()),
);
},
);

Probably facilities![index] is causing the issue. You're trying to access facilities values at index range, builder's current position, while only one value might be available.
Change to facilities![0]. Or use loop/map fn to print each facilities if there are more than 1 facilities values.
Updated code:
ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(users[index].userName),
subtitle: Text(users[index].facilities![0].facilityId.toString()),
);
},
);

Related

Flutter Firebase Iterate through all documents that matches field value and inserting field from each one into a List of String

I'm trying to get afield value into a list of string from documents in the firebase that match the WHERE of a field value from a value of my own.
(I wanna show images above each other to create a full picture depending on the data, using two fields to be exact and then creating a list of images to show as positioned inside a Stack)
my code:
Implant Class:
class Implant {
final String id;
final String pid;
final String aptid;
late String type;
late double? sizew;
late int? sizel;
late String? positionQ;
late int? positionN;
Implant(this.id, this.pid, this.aptid, this.type, this.sizew, this.sizel,
this.positionQ, this.positionN);
Map<String, dynamic> toJson() => {
'ID': id,
'PID': pid,
'APTID': aptid,
'TYPE': type,
'SIZEW': sizew,
'SIZEL': sizel,
'POSITIONQ': positionQ,
'POSITIONN': positionN,
};
factory Implant.fromJson(Map<String, dynamic> json) {
return Implant(
json['ID'],
json['PID'],
json['APTID'],
json['TYPE'],
json['SIZEW'],
json['SIZEL'],
json['POSITIONQ'],
json['POSITIONN'],
);
}
static Map<String, dynamic> toMap(Implant implant) => {
'ID': implant.id,
'PID': implant.pid,
'APTID': implant.aptid,
'TYPE': implant.type,
'SIZEW': implant.sizew,
'SIZEL': implant.sizel,
'POSITIONQ': implant.positionQ,
'POSITIONN': implant.positionN,
};
static String encode(List<Implant> implant) => json.encode(
implant
.map<Map<String, dynamic>>((implant) => Implant.toMap(implant))
.toList(),
);
static List<Implant> decode(String implants) =>
(json.decode(implants) as List<dynamic>)
.map<Implant>((implant) => Implant.fromJson(implant))
.toList();
}
Future of list of string function:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
var collection = FirebaseFirestore.instance.collection('implants');
var docSnapshot = await collection.doc(patient.id).get();
if (docSnapshot.exists) {
Map<String, dynamic> data = docSnapshot.data()!;
result.add(data['POSITIONQ'] + data['POSITIONN']);
}
return result;
}
How I translate them into Stack and Positioned:
static Future<void> showPatientImplants(BuildContext context, Patient patient,
{bool spaces = true}) async {
List<String> myImplants;
myImplants = await getImplants(patient);
List<Widget> x = [Image.asset('assets/images/teeth/empty.png')];
myImplants.forEach((element) {
x.add(Image.asset(element));
});
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
content: SingleChildScrollView(
child: GestureDetector(
onTap: () {
log(myImplants.toString());
},
child: Stack(
children: x,
),
),
),
);
});
}
The only problem I'm facing -I think- is that the Future<List> function doesn't get me the values I need.
I tried using other functions I found on SO and google, nothing worked.
I guess I can try using the stream and Listview.builder just to get the files names but it seems like exactly how it shouldn't be done.
any help is appreciated.
Never mind Solved it using the following code:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
log('ID:${patient.id}');
var collection = FirebaseFirestore.instance
.collection('implants')
.where('PID', isEqualTo: patient.id);
var docSnapshot = await collection.get();
for (var element in docSnapshot.docs) {
if (element.exists) {
Map<String, dynamic> data = element.data();
result.add(data['POSITIONQ'].toString() + data['POSITIONN'].toString());
}
}
return result;
}
but if anyone has a better idea I appreciate it along with everyone who has a similar problem.

type 'Null' is not a subtype of type 'List<dynamic>' in type cast in flutter

I am trying to fetch image from an api. For that I am using http package for flutter. I created Model View Controller pattern to arrange the structure of the project. Here is the api link and response:
https://wrestlingworld.co/wp-json/wp/v2/posts/128354
Response =>
[{"id":128640,"date":"2022-11-04T15:09:58","date_gmt":"2022-11-04T09:39:58","guid":{"rendered":"https:\/\/wrestlingworld.co\/?p=128640"},"modified":"2022-11-04T15:10:04","modified_gmt":"2022-11-04T09:40:04","slug":"impact-knockouts-tag-team-championship-match-announced-for-over-drive-2022","status":"publish","type":"post","link":"https:\/\/wrestlingworld.co\/news\/impact-knockouts-tag-team-championship-match-announced-for-over-drive-2022","title":{"rendered":"Impact Knockouts Tag Team Championship Match Announced for Over Drive"},"content":{"rendered":"\n<p>Impact Knockouts Tag Team Championships will be on the line at Over Drive on November 18th. It has <a href=\"https:\/\/impactwrestling.com\/2022\/11\/03\/tasha-steelz-savannah-evans-look-to-topple-the-death-dollz-in-knockouts-world-tag-team-title-showdown-at-over-drive\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">been announced<\/a> that Death Dollz (Taya Valkyrie and Jessicka) will be defending their titles against Tasha Steelz and Savannah
Here is my model:
class NewsModel {
int? id;
String? date;
String? slug;
String? status;
Title? title;
Title? content;
List<OgImage>? ogImage;
int? author;
NewsModel(
{this.id,
this.date,
this.slug,
this.status,
this.title,
this.content,
this.ogImage,
this.author});
NewsModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
date = json['date'];
slug = json['slug'];
status = json['status'];
title = json['title'] != null ? new Title.fromJson(json['title']) : null;
content =
json['content'] != null ? new Title.fromJson(json['content']) : null;
if (json['og_image'] != null) {
ogImage = <OgImage>[];
json['og_image'].forEach((v) {
ogImage!.add(new OgImage.fromJson(v));
});
}
author = json['author'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['date'] = this.date;
data['slug'] = this.slug;
data['status'] = this.status;
if (this.title != null) {
data['title'] = this.title!.toJson();
}
if (this.content != null) {
data['content'] = this.content!.toJson();
}
if (this.ogImage != null) {
data['og_image'] = this.ogImage!.map((v) => v.toJson()).toList();
}
data['author'] = this.author;
return data;
}
}
class Title {
String? rendered;
Title({this.rendered});
Title.fromJson(Map<String, dynamic> json) {
rendered = json['rendered'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['rendered'] = this.rendered;
return data;
}
}
class OgImage {
String? url;
OgImage({this.url});
OgImage.fromJson(Map<String, dynamic> json) {
url = json['url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['url'] = this.url;
return data;
}
}
Here you can see OgImage is a list So I created a card and tried this code:
final int id;
final String title;
final String description;
final List<dynamic> img;
const NewsCard({
required this.id,
required this.title,
required this.description,
required this.img,
});
ListView.builder(
itemCount: img.length,
itemBuilder: (context, item){
return Image.network(
img[item],
height: 120,
width: double.infinity
);
},
),
Here is the front end code where I am passing value :
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: allNews.length,
itemBuilder: (context, i) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: NewsCard(
id: allNews[i].id as int,
title: allNews[i].title!.rendered!,
description: allNews[i].content!.rendered!,
img: allNews[i].ogImage?[0].url as List<dynamic>,
),
);
}),),
Here is my controller :
Future<bool> getNews() async {
var url = Uri.parse(urlnews);
// var token = storage.getItem('token');
try {
http.Response response = await http.get(url);
print(response.body);
var data = json.decode(response.body) as List;
// print(data);
List<NewsModel> temp = [];
data.forEach((element) {
NewsModel product = NewsModel.fromJson(element);
temp.add(product);
});
_news = temp;
notifyListeners();
return true;
} catch (e) {
print(e);
return false;
}
}
List<NewsModel> get allNews {
return [..._news];
}
This code has errors I mentioned in the title already. Here I have a qustion like how can I pass list value inside the card. What is right way to fetch lists of image inside a widget.
There are a few issues with your code:
1. In your NewsModel class file, you're mapping it all wrong.
You're mapping json['og_image'] to a List, while json['og_image'] doesn't exist in the first place. If you see the JSON response, instead it's within the json['yoast_head_json'] key. So, instead of json['og_image'] you need to do json['yoast_head_json']['og_image'].
Change:
if (json['og_image'] != null) {
ogImage = <OgImage>[];
json['og_image'].forEach((v) {
ogImage!.add(new OgImage.fromJson(v));
});
}
to:
if (json['yoast_head_json'] != null &&
json['yoast_head_json']['og_image'] != null) {
ogImage = <OgImage>[];
json['yoast_head_json']['og_image'].forEach((v) {
ogImage!.add(OgImage.fromJson(v));
});
2. In your frontend part, you're trying to cast a nullable type of list allNews[i].ogImage?[0].url as List<dynamic> to List<dynamic>, which will throw exception in case the list is NULL which is in your case.
so, instead of:
img: allNews[i].ogImage?[0].url as List<dynamic>
do:
img: allNews[i].ogImage ?? []
3. Finally, in your NewsCard class:
Change:
Image.network(
img[item],
...
);
to
Image.network(
img[item].url,
...
);
Enjoy :)

FLUTTER_ NoSuchMethodError (NoSuchMethodError: Class '_InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments

I did research from many source about this error but i've found nothing. I'm trying to build a ListView.builder in Flutter, where the itemBuilder is from my JSON response data like this:
My event Model was generated for me like
Events eventsFromJson(String str) => Events.fromJson(json.decode(str));
String eventsToJson(Events data) => json.encode(data.toJson());
class Events {
Events({
required this.status,
required this.message,
required this.data,
required this.total,
});
bool status;
String message;
Data data;
int total;
factory Events.fromJson(Map<String, dynamic> json) => Events(
status: json["status"],
message: json["message"],
data: Data.fromJson(json["data"]),
total: json["total"],
);
Map<String, dynamic> toJson() => {
"status": status,
"message": message,
"data": data.toJson(),
"total": total,
};
}
class Data {
Data({
required this.events,
});
List<Event> events;
factory Data.fromJson(Map<String, dynamic> json) => Data(
events: List<Event>.from(json["events"].map((x) => Event.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"events": List<dynamic>.from(events.map((x) => x.toJson())),
};
}
class Event {
Event({
required this.id,
required this.eventName,
required this.eventType,
required this.eventLocation,
required this.checkInDate,
required this.checkOutDate,
required this.eventImage,
});
String id;
String eventName;
String eventType;
String eventLocation;
DateTime checkInDate;
DateTime checkOutDate;
String eventImage;
factory Event.fromJson(Map<String, dynamic> json) => Event(
id: json["id"],
eventName: json["event_name"],
eventType: json["event_type"],
eventLocation: json["event_location"],
checkInDate: DateTime.parse(json["check_in_date"]),
checkOutDate: DateTime.parse(json["check_out_date"]),
eventImage: json["event_image"],
);
Map<String, dynamic> toJson() => {
"id": id,
"event_name": eventName,
"event_type": eventType,
"event_location": eventLocation,
"check_in_date": checkInDate.toIso8601String(),
"check_out_date": checkOutDate.toIso8601String(),
"event_image": eventImage,
};
}
I need to get the 'eventName' of item to fetch from my ListView.builder, This is what I've come up with
class EventAPI extends BaseAPI {
Future<List<Event>> fetchAllEvents() async {
final response = await http
.get(Uri.parse(super.allEventsPath), headers: super.headers);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
return parsed.map<Event>((json) => Event.fromJson(json)).toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load Events');
}
}
}
and its being called on the future builder like this
child: FutureBuilder<List<Event>>(
future: futureEvent,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (_, index) => Container(
height: 75,
color: Colors.white,
child: Center(child: Text("${snapshot.data![index].eventName}"),
),
)
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
),
I have tried different methods but still get the same error
My Full Error Is
NoSuchMethodError: Class ‘_internal Linked HashMap<String, dynamic>’ has no instance method ‘map’ with matching arguments.
Receiver : _linkedHashMap Len:4
Tried calling map<Event>(Closure: (dynamic) => Event)
Found: map<Y0,Y1>({X0,X1}) => MapEntry<Y0,Y1>) => Map<Y0,Y1>

Flutter http.get

My problem is, that when I try to map the brands I need to declare brandsData as a Map<String, dynamic> but in that case jsonDecode(s) is red because it could be null
You can change your Model to this .
// To parse this JSON data, do
//
// final album = albumFromJson(jsonString);
import 'dart:convert';
List<Album> albumFromJson(String str) => List<Album>.from(json.decode(str).map((x) => Album.fromJson(x)));
String albumToJson(List<Album> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Album {
Album({
this.userId,
this.id,
this.title,
});
int userId;
int id;
String title;
factory Album.fromJson(Map<String, dynamic> json) => Album(
userId: json["userId"],
id: json["id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"userId": userId,
"id": id,
"title": title,
};
}
and use the function that #Ravindra via link like this
Future<List<Album>> fetchPost() async {
String url =
'https://jsonplaceholder.typicode.com/albums/1';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return Album.fromJson(json
.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Hope it helps you. Lastly you can use this link https://app.quicktype.io/ to create your model class.

How to use json and serialization with firebase and bloc? Error: Converting object to an encodable object failed

this is my cloud firestore looks like:
Error Message: Unhandled Exception: Converting object to an encodable
object failed: Photography
used jsonSerialization for my database
import 'package:json_annotation/json_annotation.dart';
part 'Model.g.dart';
#JsonSerializable()
class Photography{
String couplePhoto;
String female;
String image_url;
String info;
String male;
AllImages all_images;
Photography();
factory Photography.fromJson(Map<String, dynamic> json) => _$PhotographyFromJson(json);
Map<String,dynamic> toJson() => _$PhotographyToJson(this);
}
#JsonSerializable()
class AllImages {
List<String> imageUrl = List<String>();
AllImages();
factory AllImages.fromJson(Map<String, dynamic> json) => _$AllImagesFromJson(json);
Map<String,dynamic> toJson() => _$AllImagesToJson(this);
}
By running flutter pub run build_runner build in the project root, I generated JSON serialization code for my Photography and AllImages whenever they are needed.
Model.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'Model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Photography _$PhotographyFromJson(Map<String, dynamic> json) {
return Photography()
..couplePhoto = json['couplePhoto'] as String
..female = json['female'] as String
..image_url = json['image_url'] as String
..info = json['info'] as String
..male = json['male'] as String
..all_images = json['all_images'] == null
? null
: AllImages.fromJson(json['all_images'] as Map<String, dynamic>);
}
Map<String, dynamic> _$PhotographyToJson(Photography instance) =>
<String, dynamic>{
'couplePhoto': instance.couplePhoto,
'female': instance.female,
'image_url': instance.image_url,
'info': instance.info,
'male': instance.male,
'all_images': instance.all_images
};
AllImages _$AllImagesFromJson(Map<String, dynamic> json) {
return AllImages()
..imageUrl = (json['imageUrl'] as List)?.map((e) => e as String)?.toList();
}
Map<String, dynamic> _$AllImagesToJson(AllImages instance) =>
<String, dynamic>{'imageUrl': instance.imageUrl};
After that, I created the DB class,
How to use the model class?
class DB {
final db = Firestore.instance;
// Stream<QuerySnapshot> initStream() {
// return db.collection('photography').snapshots();
// }
getPhotography() async {
return db.collection('photography')
.document("0yUc5QBGHNNq6WK9CyyF")
.setData(jsonDecode(jsonEncode(Photography)));
}
}
DB db = DB();
my photography_bloc class
class PhotographyBloc extends BlocBase{
//PhotographyBloc(){
// db.initStream().listen((data) => inFirestore.add(data));
//}
PhotographyBloc(){
init();
}
Photography photography;
//final _firestoreController = StreamController<Photography>();
//Stream<Photography> get outFirestore => _firestoreController.stream;
//Sink<Photography> get inFirestore => _firestoreController.sink;
final _firestoreController = StreamController<Photography>();
Stream<Photography> get outFirestore => _firestoreController.stream;
Sink<Photography> get inFirestore => _firestoreController.sink;
void init() async{
photography = db.getPhotography();
inFirestore.add(photography);
}
#override
void dispose() {
_firestoreController.close();
}
}
my StreamBuilder Widget
How to get data using JSON serialization
child: StreamBuilder<Photography>(
stream: bloc.outFirestore,
initialData: null,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: buildItem(snapshot.data, bloc));
// children: snapshot.data.documents
// .map<Widget>((doc) => buildItem(doc, bloc))
// .toList());
} else {
return SizedBox();
}
}),
builderItem() method,
buildItem(Photography doc, PhotographyBloc bloc) {
...
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: FadeInImage.assetNetwork(
placeholder: "assets/images/photography.jpg",
image: doc.couplePhoto,
// image: doc.data['couplePhoto'],
fit: BoxFit.fill,
),
),
According to the package source :
/// Writes to the document referred to by this [DocumentReference].
///
/// If the document does not yet exist, it will be created.
///
/// If [merge] is true, the provided data will be merged into an
/// existing document instead of overwriting.
Future<void> setData(Map<String, dynamic> data, {bool merge = false}) {
return Firestore.channel.invokeMethod<void>(
'DocumentReference#setData',
<String, dynamic>{
'app': firestore.app.name,
'path': path,
'data': data,
'options': <String, bool>{'merge': merge},
},
);
}
You must give a <String, dynamic> Map to setData(x) method.
So in your case you should maybe do it like this :
getPhotography() async {
return db.collection('photography')
.document("0yUc5QBGHNNq6WK9CyyF")
.setData(photography.toJson());
}