I am developing a Stock app in which I have to display News related to the stocks. I made a News class for the same as well as a factory constructor to convert the data from json
class News {
final String title;
final String desc;
final String imgURL;
final String url;
News(
{required this.title,
required this.desc,
required this.imgURL,
required this.url});
factory News.fromJSON(Map<String, dynamic> json) {
final title = json["title"] as String;
final desc = json["description"] as String;
final imgUrl = json["image_url"] as String;
final url = json["url"] as String;
return News(title: title, desc: desc, imgURL: imgUrl, url: url);
}
}
I have made a method to fetch the data from the API:
Future getNews() async {
final response = await http.get(Uri.parse(
'https://api.stockdata.org/v1/news/all?&filter_entities=true&language=en&api_token=${api_token}&countries=${country}'));
if (response.statusCode == 200) {
final jsonResponse = json.decode(response.body);
return jsonResponse.map((data) => News.fromJSON(data));
} else {
throw Exception('Unexpected error occurred!');
}
}
I am having trouble understanding how I can display the data in my app. I tried using FutureBuilder but I can't seem to understand how it's working.
Any help would be appreciated!
For the FutureBuilder you can do it this way :
FutureBuilder(
future: getNews(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData){
// Save your data in a variable
List<News> news = snapshot.data;
// Create a listview to show all of the news
return newsListView(news); //This is a list
} else {
return Center(
child: Container(
width: 300,
height: 290,
child: Center(child: Text("Error"))
)
);
}
}
),
Related
I am stuck at trying to read the current user's data after storing it in Firestore. The page keeps showing a blank page instead of showing the actual data from Firestore.
I have created a model for the data like this
class Vital {
String id;
final String bloodSugar;
final String bloodPressure;
final String bodyTemp;
final DateTime? createdOn;
Vital({
this.id = '',
required this.bloodSugar,
required this.bloodPressure,
required this.bodyTemp,
required this.createdOn,
});
Map<String, dynamic> toJson() => {
'id': id,
'bloodSugar': bloodSugar,
'bloodPressure': bloodPressure,
'bodyTemp': bodyTemp,
"createdOn": Utils.fromDateTimeToJson(createdOn)
};
Vital.fromSnapShot(DocumentSnapshot<Map<String, dynamic>> snapshot)
: id = snapshot.id,
bloodSugar = snapshot.data()!["bloodSugar"],
bloodPressure = snapshot.data()!["bloodPressure"],
bodyTemp = snapshot.data()!["bodyTemp"],
createdOn = snapshot.data()!["createdOn"].toDate();
}
I have stored the data in Firestore using the below code
Future addVitals() async {
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = await auth.currentUser;
final uid = user?.uid;
final vitals = FirebaseFirestore.instance
.collection('vitalsign')
.doc(uid)
.collection("usersVitals");
final vital = Vital(
id: vitals.id,
createdOn: DateTime.now(),
bloodSugar: _bloodSugar.text,
bloodPressure: _bloodPressure.text,
bodyTemp: _bodyTemp.text);
final json = vital.toJson();
await vitals.add(json);
Navigator.push(
context, MaterialPageRoute(builder: (context) => VitalsSaved()));
}
Please note that users will be allowed to add their vitals everyday and what I want to achieve is to get the vitals of the current users in a separate page using stream builder.
This is what I would like to achieve
blood pressure
day1 day2
120/70 120/65 etc
blood glucose
day1 day2
27 26 etc
this will be for a specific user and once another user login to the app, they should only get their own data from what they put in the input.
This is what I have tried to do in the stream of the stream builder
Stream<QuerySnapshot> readVitals() async* {
final FirebaseAuth auth = FirebaseAuth.instance;
final user = auth.currentUser;
final uid = user?.uid;
yield* FirebaseFirestore.instance
.collection('vitalsign')
.doc(uid)
.collection("userVitals")
.snapshots();
}
then I created a widget as follow to get data from firestore
Widget buildPressure(BuildContext context, DocumentSnapshot document) {
return Container(
child: Card(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
document["bloodSugar"],
style: TextStyle(fontSize: 20),
)
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [Text(document["bloodPressure"])],
),
),
],
),
),
);
}
Then lastly I use my widget in the streanbuilder as follow
stream: readVitals(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return const Text("Loading...");
}
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
return buildPressure(context, snapshot.data.docs[index]);
});
}
return CircularProgressIndicator();
},
)
After everything I still get a blank page which means that I am not getting any data from Firestore despite having create data and add it in Firestore.
I have tried several ways but could not get it.
I think you don't need to put .data() in = snapshot.data()! in model class part.
I will give you a simple example from code of my project that may help you move on..
class ReadsModel {
late String id;
late String firstName;
late String lastName;
late String father;
late String mother;
late String readDate;
late String subType;
late String counterNumber;
late String paymentStatus;
late int counterValue;
ReadsModel({
required this.id,
required this.firstName,
required this.lastName,
required this.father,
required this.mother,
required this.readDate,
required this.subType,
required this.counterNumber,
required this.paymentStatus,
required this.counterValue,
});
ReadsModel.fromMap(DocumentSnapshot data){
id = data.id;
firstName = data['FirstName'];
lastName = data['LastName'];
father = data['Father'];
mother = data['Mother'];
readDate = data['ReadDate'];
subType = data['Subscription type'];
counterNumber = data['CounterNumber'];
paymentStatus = data['Payment Status'];
counterValue = data['CounterValue'];
}
}
For the saving to Firestore part:
Future savingToFireStore() async {
await villagesCollectionRef
.doc()
.collection('Reads')
.doc(const Uuid().v4())
.set({
'FirstName': firstName,
'LastName': lastName,
'Father': father,
'Mother': mother,
'ReadDate': readDate,
'Subscription type': subType,
'Payment Status': 'unPaid',
'CounterValue': int.parse(readValueController.text),
'CounterNumber': counterNumber,
});
}
Then I create a list from the model type to store data in:
List<VillageModel> areaModel = <VillageModel>[].obs;
Then use this function to from GetX package to stream data and get it "You can use Stream builder in the same way"
getReads() {
return villagesCollectionRef
.doc()
.collection('Reads')
.snapshots()
.listen((event) {
readsModel.clear();
for (var element in event.docs) {
readsModel.add(ReadsModel.fromMap(element));
}
});
}
Finally, in the UI part you can use the suitable widget to show data, for me I used ListView.builder().
I hope that helps you.
Try changing your:
Stream<QuerySnapshot> readVitals() async* {
final FirebaseAuth auth = FirebaseAuth.instance;
final user = auth.currentUser;
final uid = user?.uid;
yield* FirebaseFirestore.instance
.collection('vitalsign')
.doc(uid)
.collection("userVitals")
.snapshots();
}
into:
Stream<Iterable<Vital>> readVitals() async* {
final FirebaseAuth auth = FirebaseAuth.instance;
final user = auth.currentUser;
final uid = user?.uid;
yield* FirebaseFirestore.instance
.collection('vitalsign')
.doc(uid)
.collection("userVitals")
.snapshots().map((event) =>
event.docs
.map((doc) => Vital.fromSnapshot(doc)));
}
This way you should return the values of the documents in the collection UserVitals. Note that if this documents don't have all the fields (bloodSugar, bloodPressure, bodyTemp and createdOn) you are probably gonna get an error.
I want to retrieve data from a realtime database for a flutter application. My data is built like this.
I need to loop through this data to display it on the application (ecommerce app, obviously). I have tried and failed in many ways. Currently when trying to get the data I see "Instance of '_Future'" as the message.
class Cart extends StatefulWidget {
Cart({Key? key}) : super(key: key);
#override
State<Cart> createState() => _CartState();
}
class _CartState extends State<Cart> {
DatabaseReference ref = FirebaseDatabase.instance.ref();
Object? products;
List productList = [];
String displayText = 'Results go here!';
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
print(snapshot);
print(snapshot.value);
} else {
print('No Data Available');
}
}
#override
void initState() {
super.initState();
snapshot();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const PreferredSize(
preferredSize: Size.fromHeight(60), child: MyAppBar()),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
// await ref.set({"name": "Tyler"});
snapshot();
},
child: Text("Add Data"),
),
Text("${snapshot()}", style: TextStyle(color: Colors.white))
],
)
);
}
}
I also have this data class built from other posts I have seen. I have to admit, I am not entirely sure how to use it.
import 'dart:convert';
class ProductData {
final int productID;
final String productCategory;
final String productDesc;
final String productName;
final String productPrice;
final String productSize;
final bool productInStock;
final String productImage1;
final String productGender;
final String productImage2;
ProductData(
{required this.productID,
required this.productCategory,
required this.productDesc,
required this.productName,
required this.productPrice,
required this.productSize,
required this.productInStock,
required this.productImage1,
required this.productGender,
required this.productImage2});
ProductData copyWith(
{int? productID,
String? productCategory,
String? productDesc,
String? productName,
String? productPrice,
String? productSize,
bool? productInStock,
String? productImage1,
String? productGender,
String? productImage2}) {
return ProductData(
productID: productID ?? this.productID,
productCategory: productCategory ?? this.productCategory,
productDesc: productDesc ?? this.productDesc,
productName: productName ?? this.productName,
productPrice: productPrice ?? this.productPrice,
productSize: productSize ?? this.productSize,
productInStock: productInStock ?? this.productInStock,
productImage1: productImage1 ?? this.productImage1,
productGender: productGender ?? this.productGender,
productImage2: productImage2 ?? this.productImage2,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'productID': productID,
'productCategory': productCategory,
'productDesc': productDesc,
'productName': productName,
'productPrice': productPrice,
'productSize': productSize,
'productInStock': productInStock,
'productImage1': productImage1,
'productGender': productGender,
'productImage2': productImage2,
};
}
factory ProductData.fromMap(Map<String, dynamic> map) {
return ProductData(
productID: map['productID'] as int,
productCategory: map['productCategory'] as String,
productDesc: map['productDesc'] as String,
productName: map['productName'] as String,
productPrice: map['productPrice'] as String,
productSize: map['productSize'] as String,
productInStock: map['productInStock'] as bool,
productImage1: map['productImage1'] as String,
productGender: map['productGender'] as String,
productImage2: map['productImage2'] as String,
);
}
String toJson() => json.encode(toMap());
factory ProductData.fromJson(String source) =>
ProductData.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() {
return 'ProductData(productID: $productID, productCategory: $productCategory, productDesc: $productDesc, productName: $productName, productPrice: $productPrice, productSize: $productSize, productInStock: $productInStock, productImage11: $productImage1, productGender: $productGender, productImage2: $productImage2)';
}
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ProductData &&
other.productID == productID &&
other.productCategory == productCategory &&
other.productDesc == productDesc &&
other.productName == productName &&
other.productPrice == productPrice &&
other.productSize == productSize &&
other.productInStock == productInStock &&
other.productImage1 == productImage1 &&
other.productGender == productGender &&
other.productImage2 == productImage2;
}
#override
int get hashCode {
return productID.hashCode ^
productCategory.hashCode ^
productDesc.hashCode ^
productName.hashCode ^
productPrice.hashCode ^
productSize.hashCode ^
productInStock.hashCode ^
productImage1.hashCode ^
productGender.hashCode ^
productImage2.hashCode;
}
}
Since the data is loaded from Firebase asynchronously, its get() method returns a Future. That's also why you had to declare your snapshot() function as async, which means that you also return a Future.
On its own the rendering code doesn't know anything about Futures, so it renders it by calling its toString() method, which leads to the output you see:
Instance of '_Future'
What you want instead is to wait for the future to resolve, which is just a fancy way of saying that you want to wait for the data to load. An easy way to do that is to use a FutureBuilder, which handles the asynchronous nature of a Future and all possible states it can be in.
That'd look something like:
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
} else {
print('No Data Available');
}
return productList;
}
body: Column(
children: [
ElevatedButton(
onPressed: () async {
snapshot();
},
child: Text("Add Data"),
),
FutureBuilder(
future: snapshot(),
builder: (BuildContext context, AsyncSnapshot asyncSnapshot) {
if (snapshot.hasData) {
var productList = asyncSnapshot.data! as List;
return Text(productList.length.toString());
} else if (snapshot.hasError) {
return Text('Error: ${asyncSnapshot.error}');
} else {
return CircularProgressIndicator(),
}
}
)
],
)
I'm creating an app that use assets_audio_player to play music from a json response that are generated from a php script. There is a Future list function that return an Audio list. Audio isn't a Widget so i can't use a FutureBuilder. How can i use the future list?
Future<List<Audio>> creaLista() async {
final response = await http.post(Uri.parse(url));
String responseBody = response.body;
dynamic jsonObject = json.decode(responseBody);
final convertedJsonObject = jsonObject.cast<Map<String, dynamic>>();
List<Song> list =
convertedJsonObject.map<Song>((json) => Song.fromJson(json)).toList();
List<Audio> audioList = list
.map<Audio>((json) => Audio.network(
urlSong + json.url,
metas: Metas(
title: json.title,
artist: json.artist,
album: json.album,
image: MetasImage.network(
urlImage + json.image,
),
),
))
.toList();
return audioList;
}
This is the Song class:
class Song {
String title;
String artist;
String album;
String image;
String genre;
String url;
Song(
{required this.title,
required this.artist,
required this.album,
required this.image,
required this.genre,
required this.url});
factory Song.fromJson(Map<String, dynamic> json) => Song(
title: json['title'],
artist: json['artist'],
album: json['album'],
image: json['image'],
genre: json['genre'],
url: json['url']);
}
This is the json response:
[{"title":"Mille","artist":"Fedez, Achille Lauro, Orietta Berti","album":"Singolo","image":"mille.jpg","genre":"pop","url":"mille.mp3"}]
You don't need the Future to return a Widget to use FutureBuilder. You can create widgets based on the returned results of the future. Here's an example that would display the just the artist, but it should give you the idea.
In general I would suggest creating a custom Widget that takes an Audio object and displays all of the Audio data how you would like.
FutureBuilder(
future: creaLista(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final audioList = snapshot.data as List<Audio>;
return ListView.builder(
itemCount: audioList.length,
itemBuilder: (context, index) {
return Text(audioList[index].artist);
});
} else if (snapshot.hasError) {
// handle error here
return Text('${snapshot.error}');
} else {
return CircularProgressIndicator(); // displays while loading data
}
},
)
This assumes your Audio class looks like this
class Audio {
String title, artist, album;
Audio(this.title, this.artist, this.album);
}
I'm sure its more complex than that but it should give you the idea.
I keep getting the error mentioned above during runtime of my flutter app. Basically what I am trying to achieve is to fetch data from an api and display it in a form of a SliverList by using a FutureBuilder.
This was working perfectly fine until I changed my code for the list from FutureBuilder<List> to FutureBuilder<List> to make use of the class EntertainerEvent which has all the fields I need for display from the json file.
How can I resolve this because it seems like the contructor or the application itslef is not picking up the data when I make use of a custom class.
This is the code for the EntertainerEvent class:
class EntertainerEvent {
final int eventId;
final int entertainerId;
final int eventTypeId;
final int categoryId;
final String eventName;
final String description;
final String imagePoster;
final String location;
final DateTime startDate;
final DateTime endDate;
final double entreeFee;
const EntertainerEvent({required this.eventId, required this.entertainerId, required this.eventTypeId,
required this.categoryId, required this.eventName, required this.description, required this.imagePoster,
required this.location, required this.startDate, required this.endDate, required this.entreeFee});
factory EntertainerEvent.fromJson(Map<String, dynamic> event) {
return EntertainerEvent(
eventId: event['EventID'],
entertainerId: event['EntertainerID'],
eventTypeId: event['EventTypeID'],
categoryId: event['CategoryID'],
eventName: event['EventName'],
description: event['Description'],
imagePoster: event['ImagePoster'],
location: event['Location'],
startDate: event['StartDate'],
endDate: event['EndDate'],
entreeFee: event['EntryFee'],
);
}
}
Below is the code for fetching data from the api:
Future<List<EntertainerEvent>> fetchEvents() async {
var result = await http.get(Uri.parse(apiUrl));
if (result.statusCode == 200) {
var content = result.body;
var arr = json.decode(content) as List;
return arr.map((eve) => new EntertainerEvent.fromJson(eve)).toList();
} else {
print('Not loaded');
throw Exception('Unable to fetch data from the Rest API');
}
}
late Future<List<EntertainerEvent>> _fetchEvents;
#override
void initState() {
_fetchEvents = fetchEvents();
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<EntertainerEvent>>(
future: _fetchEvents,
builder: (BuildContext context, AsyncSnapshot snapshot) {
var childCount = 0;
if (snapshot.connectionState != ConnectionState.done) {
childCount = 1;
} else {
childCount = snapshot.data.length;
}
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
if (snapshot.hasData) {
List<EntertainerEvent> someData = snapshot.data;
print('data here');
//Do some stuff
}
}, childCount: childCount),
);
});
}
I do not know what exactly is it that I am missing because this code works if I use the type dynamic instead of the custom class EntertainerEvent.
Thank you all in advance!
Wrap it with hasData:
if(snapshot.hasData){
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
List<EntertainerEvent> someData = snapshot.data;
print('data here');
//Do some stuff
}, childCount: childCount),
);}
return CircularProgressIndicator();
I'm trying to use https://pub.dev/packages/flappy_search_bar#-readme-tab- to create a list of data which I plan on getting from an api(just testing now), but I can't seem to add the list created from the response json to the search widget.
The argument type 'Future<List<Album>> Function()' can't be assigned to the parameter type 'Future<List<Album>> Function(String)'.
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Future<List<Album>> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
List jsonResponse = json.decode(response.body);
return jsonResponse.map((job) => Album.fromJson(job)).toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SearchBar<Album>(
onSearch: fetchAlbum, <------ error here
onItemFound: (Album post, int index) {
return ListTile(
onTap: () => widget.setProviderData(post.title),
title: Text(post.title),
subtitle: Text(post.id.toString()),
);
},
),
),
);
}
Can anyone help me with this, please?
I can't test your code right now, but at first glance the problem is onSearch expects a function that gets String parameter and your code doesn't provide it.
You should modify this line Future<List<Album>> fetchAlbum() async as follows:
Future<List<Album>> fetchAlbum(String album) async