How to solve the problem with types in flutter? - flutter

I have an array of elements that come from api and I get and error from api =>
The operator '[]' isn't defined for the type of 'Country'
Response from api looks like this:
{"success":true,"list":[{"id":2,"createdAt":"2022-11-11T15:25:31.680Z","updatedAt":"2022-11-11T15:25:31.680Z","name":"Afghanistan"}]}
This is the type of an element inside list:
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name
});
}
This is my widget:
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<Country> countries = [];
Future<void> getCountries() async {
try {
final response = await _apiService.getCountries();
countries = response['list']; // [{"id": 2, "createdAt:""...}]
} catch (e) {
log(e.toString());
rethrow;
}
}
#override
void initState() {
getCountries();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
And if I try to call this, IDE lights me this error in country['name'] =>
final List countriesWithNames = countries.map((country) => country['name']).toList();
Or when I try to get an element from the list, like this => countries[index]['name']

response['list'] returns list of map.You need to convert into model class.
You can use this model class
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name,
});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'createdAt': createdAt});
result.addAll({'updatedAt': updatedAt});
result.addAll({'name': name});
return result;
}
factory Country.fromMap(Map<String, dynamic> map) {
return Country(
id: map['id']?.toInt() ?? 0,
createdAt: map['createdAt'] ?? '',
updatedAt: map['updatedAt'] ?? '',
name: map['name'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory Country.fromJson(String source) =>
Country.fromMap(json.decode(source));
}
And getting from local json string
final data = response["list"] as List?;
List<Country> countries =
data?.map((e) => Country.fromMap(e)).toList() ?? [];
print(countries);

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.

A value of type 'Iterable<HospitalListModel>' can't be assigned to a variable of type 'List<HospitalListModel>'

I got a flutter error A value of type 'Iterable<HospitalListModel>' can't be assigned to a variable of type 'List<HospitalListModel>'. This is my model:
List<HospitalListModel> hospitalListModelFromJson(String str) => List<HospitalListModel>.from(json.decode(str).map((x) => HospitalListModel.fromJson(x)));
String hospitalListModelToJson(List<HospitalListModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class HospitalListModel {
HospitalListModel({
this.id,
this.title,
this.content,
this.image,
this.phone,
this.coordinates,
this.website,
this.createdAt,
this.updatedAt,
});
dynamic id;
dynamic title;
dynamic content;
dynamic image;
dynamic phone;
dynamic coordinates;
dynamic website;
dynamic createdAt;
dynamic updatedAt;
factory HospitalListModel.fromJson(Map<String, dynamic> json) => HospitalListModel(
id: json["id"],
title: json["title"],
content: json["content"],
image: json["image"],
phone: json["phone"],
coordinates: json["coordinates"],
website: json["website"],
createdAt: json["created_at"],
updatedAt: json["updated_at"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"content": content,
"image": image,
"phone": phone,
"coordinates": coordinates,
"website": website,
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
};
}
and this is where the error come from, it's from the API provider and im confused why it throw iterable
class ApiProvider {
final Dio _dio = Dio();
final String _url = 'http://lovemonster.my.id/hospital';
Future<List<HospitalListModel>> fetchHospitalList() async {
try {
List<HospitalListModel> hospitalList = [];
Response response = await _dio.get(_url);
var mData = response.data as List;
hospitalList = mData.
map<HospitalListModel>((e) => hospitalListModelFromJson(e)
.toList();
return hospitalList;//return List not object
} catch (error, stacktrace) {
print("Exception occurred: $error stackTrace: $stacktrace");
return Future.error("");
}
}
}
hospitalList = mData.map<HospitalListModel>((e) =>hospitalListModelFromJson(e).toList();this code throw an error, and if you wondering how the other class or method, i will put event & state that seems related to the error:
state:
abstract class HospitalListState extends Equatable {
const HospitalListState();
#override
List<Object?> get props => [];
}
class HospitalListInitial extends HospitalListState {}
class HospitalListLoading extends HospitalListState {}
class HospitalListLoaded extends HospitalListState {
final List<HospitalListModel> hospitalListModel;
const HospitalListLoaded(this.hospitalListModel);
}
class HospitalListError extends HospitalListState {
final String? message;
const HospitalListError(this.message);
}
event:
abstract class HospitalListEvent extends Equatable {
const HospitalListEvent();
#override
List<Object> get props => [];
}
class GetCovidList extends HospitalListEvent {}
i made this code with flutter_bloc and if you want to know more details just let me know, and if you know what's wrong with my code, just type it on the answer, i appreciate every answers and knowledge that you share with me
You have missed ')' before using toList method you have close the map method.
hospitalList = mData.map<HospitalListModel>((e) => hospitalListModelFromJson(e))
.toList();

How to get firebase realtime database data in to a list formate in flutter

I am trying to retrieve data from Firebase Realtime Database into a list in Flutter using a model.I don't get snapshot.data how to get data.value. I have read several other posts about using Firebase with Flutter but have not found a clear answer.
Model class screen:
import 'package:firebase_database/firebase_database.dart';
class DataModel {
final String id;
final String name;
final String price;
final String qty;
DataModel(
{required this.id,
required this.name,
required this.price,
required this.qty});
DataModel.fromSnapshot(DataSnapshot snapshot)
: id = snapshot.key.toString(),
name = (snapshot.value as Map<String, dynamic>?)?['productName'] ?? '',
price =
(snapshot.value as Map<String, dynamic>?)?['productPrice'] ?? '',
qty = (snapshot.value as Map<String, dynamic>?)?['qty'] ?? '';
toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}
Database service with Firebase query:
import 'package:firebase_database/firebase_database.dart';
import 'package:money_management/data_json_model.dart';
class DatabaseService {
static List<DataModel> getData() {
Query needsSnapshot =
FirebaseDatabase.instance.ref("Money Management").orderByKey();
// print(needsSnapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = needsSnapshot.onValue as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
ListView Page:
import 'package:flutter/material.dart';
import 'package:money_management/data_json_model.dart';
import 'database_service.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
State<ListScreen> createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
List<DataModel> _needs = [];
#override
void initState() {
super.initState();
_setupNeeds();
}
_setupNeeds() async {
List<DataModel> needs = DatabaseService.getData();
setState(() {
_needs = needs;
});
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () => _setupNeeds(),
child: ListView.builder(
itemCount: _needs.length,
itemBuilder: (BuildContext context, int index) {
DataModel need = _needs[index];
return Column(
children: [
Text(need.id),
Text(need.name),
Text(need.price),
Text(need.qty),
],
);
}),
);
}
}
Try make the method getData() asynchronous and call get() of FirebaseDatabase instead:
class DatabaseService {
static Future<List<dynamic>> getData() async {
final snapshot = await FirebaseDatabase.instance
.ref("Money Management")
.orderByKey()
.get();
// print(snapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = snapshot.value as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
you can receive data as Map<String,dynamic> in your DataModel like this:
class DataModel {
late String id;
late String name;
late String price;
late String qty;
DataModel({
required this.id,
required this.name,
required this.price,
required this.qty
});
DataModel.fromSnapshot(DataSnapshot snapshot){
Map<String, dynamic> myData= Map<String,dynamic>.from(snapshot.value as
Map);
id = snapshot.key.toString();
name = myData["productName"].toString() ?? '';
price =myData["productPrice"].toString() ?? '';
qty = myData["qty"].toString() ?? '';
}
Map<String,dynamic> toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}

How to retrieve a list of maps from firebase flutter

I'm working on my project in flutter and I'm stuck in kind of a dumb problem. I'd like to retrieve a List of maps from Firebase, but i can't convert the document in an object format.
So every checkout document is identified by the user email. As a consequence every document will contain a list of checkout orders as displayed in the picture.
This is the Document format in firebase
class UserCheckoutOrderList extends Equatable {
final String checkoutOrderDate;
final Map<dynamic, dynamic> customerAddress;
final String customerName;
final String customerPhone;
final String deliveryFee;
final String subTotal;
final String total;
final List<Product> products;
const UserCheckoutOrderList(
{required this.checkoutOrderDate,
required this.customerAddress,
required this.customerName,
required this.customerPhone,
required this.deliveryFee,
required this.subTotal,
required this.total,
required this.products});
static UserCheckoutOrderList fromSnapshot(List<DocumentSnapshot> snapshots) {
final ordersList = snapshots.forEach((snapshot) {
return UserCheckoutOrderList(
checkoutOrderDate: snapshot['checkoutOrderDate'],
customerAddress: snapshot['customerAddress'],
customerName: snapshot['customerName'],
customerPhone: snapshot['customerPhone'],
deliveryFee: snapshot['deliveryFee'],
subTotal: snapshot['subTotal'],
total: snapshot['total'],
products: snapshot['products']);
});
return ordersList;
}
#override
List<Object?> get props => [];
}
This is what I've tried so far for the model.
I highly recommend to always have an id on any document. This is how to handle data:
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
#immutable
class UserCheckoutOrderListEntity extends Equatable{
final String id;
final String checkoutOrderDate;
final Map customerAddress;
final String customerName;
final String customerPhone;
final String deliveryFee;
final String subTotal;
final String total;
final List products;
const UserCheckoutOrderListEntity({
required this.id,
required this.checkoutOrderDate,
required this.customerAddress,
required this.customerName,
required this.customerPhone,
required this.deliveryFee,
required this.subTotal,
required this.total,
required this.products});
Map<String, dynamic> toMap() {
return {
'id': id,
'checkoutOrderDate': checkoutOrderDate,
'customerAddress': customerAddress,
'customerName': customerName,
'customerPhone': customerPhone,
'deliveryFee': deliveryFee,
'subTotal': subTotal,
'total': total,
'products': products,
};
}
factory UserCheckoutOrderListEntity.fromMap(Map<String, dynamic> map) {
return UserCheckoutOrderListEntity(
id: map['id'] as String,
checkoutOrderDate: map['checkoutOrderDate'] as String,
customerAddress: map['customerAddress'] as Map,
customerName: map['customerName'] as String,
customerPhone: map['customerPhone'] as String,
deliveryFee: map['deliveryFee'] as String,
subTotal: map['subTotal'] as String,
total: map['total'] as String,
products: map['products'] as List,
);
}
#override
List<Object?> get props => [id];
}
Then in your widget:
class CheckOut extends StatelessWidget {
const CheckOut({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<List<UserCheckoutOrderListEntity>>(
future: FirebaseFirestore.instance
.collection("orderCheckOut")
.get()
.then((query) => query.docs
.map((map) => UserCheckoutOrderListEntity.fromMap(map.data()))
.toList()),
builder: (context, snapshot) {
return Container();
});
}
}

Flutter: bloc not removing data from the list

I'm trying to create list of favourite news using bloc, now if I want to add to the favourite list it does happen but if I want to remove it then list is not getting updated so it is not removing from UI.
My bloc logic,
class FavouriteBloc extends Bloc<FavouriteEvent, List<Articles>> {
FavouriteBloc() : super(null);
List<Articles> articles = [];
#override
Stream<List<Articles>> mapEventToState(FavouriteEvent event) async* {
switch (event.eventType) {
case EventType.add:
articles.add(event.articles);
yield articles;
break;
case EventType.delete:
articles.remove(event.articles);
yield articles;
break;
}
}
}
event class,
enum EventType {add, delete}
class FavouriteEvent{
Articles articles;
EventType eventType;
FavouriteEvent.add({this.articles,this.eventType});
FavouriteEvent.remove({this.articles,this.eventType});
}
the UI part,
In this screen when I add to favourites it shows list of cards which I have added and then I use onTap to remove it from the list but that is not happening
class FavouriteScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(),
body: BlocBuilder<FavouriteBloc, List<Articles>>(
buildWhen: (previous, current) {
if(previous.length<current.length){
return true;
}
return false;
},
builder: (context, newsList) {
if (newsList == null) {
return Center(
child: Text(
week7.Strings.noFav,
style: Theme.of(context).textTheme.headline6,
),
);
}
return ListView.builder(
itemCount: newsList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
BlocProvider.of<FavouriteBloc>(context).add( //<--- this is how I'm trying to remove
FavouriteEvent.remove(
articles: Articles(
urlToImage: newsList[index].urlToImage,
title: newsList[index].title,
author: newsList[index].author
),
eventType: EventType.delete));
},
child: Card(...),
);
});
},
),
);
}
}
model class,
#JsonSerializable()
class Articles {
Source source;
String author;
String title;
String description;
String url;
String urlToImage;
DateTime publishedAt;
String content;
Articles({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
factory Articles.fromJson(Map<String, dynamic> json) =>
_$ArticlesFromJson(json);
}
so can anyone tell me what I'm doing wrong here?
hi bro add this lib
https://pub.dev/packages/equatable
#JsonSerializable()
class Articles extends Equatable{
Source source;
String author;
String title;
String description;
String url;
String urlToImage;
DateTime publishedAt;
String content;
Articles({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
#override
List<Object> get props => [name];// depending on which field you want to remove the list item, replace "name" with your field.
factory Articles.fromJson(Map<String, dynamic> json) =>
_$ArticlesFromJson(json);
}
Dart compares if the two objects are the same instance. You need to override == operator or use library like equatable.
The first thing you need to do is to delete buildWhen. Right now it will only update(rebuild) when you add items but not when you remove them.
buildWhen: (previous, current) {
if(previous.length<current.length){
return true;
}
return false;
},
Use State class to represent the state because the list is always the same and it will not rebuild. After that adjust your widget code to use state.articles.
class FavouriteState {
final List<Articles> articles;
FavouriteState(this.artticles);
}
class FavouriteBloc extends Bloc<FavouriteEvent, FavouriteState> {
FavouriteBloc() : super(null);
List<Articles> _articles = [];
#override
Stream<FavouriteState> mapEventToState(FavouriteEvent event) async* {
switch (event.eventType) {
case EventType.add:
_articles.add(event.articles);
yield FavouriteState(_articles);
break;
case EventType.delete:
_articles.remove(event.articles);
yield FavouriteState(_articles);
break;
}
}
}
Example comparing urlToImage, title and author
#JsonSerializable()
class Articles {
Source source;
String author;
String title;
String description;
String url;
String urlToImage;
DateTime publishedAt;
String content;
Articles({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Articles && runtimeType == other.runtimeType && urlToImage == other.urlToImage &&
title == other.title && author == other.author;
#override
int get hashCode => urlToImage.hashCode ^ title.hashCode ^ author.hashCode;
factory Articles.fromJson(Map<String, dynamic> json) =>
_$ArticlesFromJson(json);
}
Example using Equatable package
#JsonSerializable()
class Articles extends Equatable{
Source source;
String author;
String title;
String description;
String url;
String urlToImage;
DateTime publishedAt;
String content;
Articles({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
#override
List<Object> get props => [author, title, description, url, urlToImage, content];
factory Articles.fromJson(Map<String, dynamic> json) =>
_$ArticlesFromJson(json);
}