I am new to flutter and I'm trying to get weather data with two futures but only one is resolved.
I have two async methods getCurrentWeather and getForecasts and getCurrentWeather is not resolve but if I remove getForecasts then getCurrentWeather future is resloved.
What do I miss?
class _YellowBirdState extends State<YellowBird> {
// Future<List<Weather>> weatherField;
Future<Weather> weatherFuture;
Future<List<Weather>> getForecasts() async {
List<Weather> forecasts = await weatherStation.fiveDayForecast();
return forecasts;
}
Future<Weather> getCurrentWeather() async {
Weather current = await weatherStation.currentWeather();
return current;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: FutureBuilder<Weather>(
future: getCurrentWeather(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.country);
}
return Text('Loading');
})),
Expanded(
child: FutureBuilder<List<Weather>>(
future: getForecasts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Weather> weathers = snapshot.data ?? [];
return ListView.builder(
itemCount: weathers.length,
itemBuilder: (context, index) {
Weather weather = weathers[index];
return ListTile(
leading: Text('aaaa'),
title: new Text(weather.weatherDescription),
onTap: () {},
);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
}
// color: const Color(0xFFFFE306)
),
),
],
),
));
}
}
Related
The code below displays list of records from FirebaseFirestore using AsyncSnapshot with StreamBuilder. It works great, however I want to display the total number of records in the AppBar title and tht works when the app is launched, but doesn't update after any addition or deletion.
Question: How can I update the number of records (and display in Appbar title) after the list has an addition or deletion?
Note that I'm displaying the total number of records in the AppBar title using title: Text('# Waiting: $numberWaiting'),, but I can't figure out how to refresh this after the list changes. Any suggestions are greatly appreciated.
class HomePageState extends State<HomePage> {
Query waitingList = FirebaseFirestore.instance
.collection('waiting')
.orderBy('Time_In');
int numberWaiting = 0; // Starts at 0; updated in StreamBuilder
Future<void> delete(String docID) async {
await FirebaseFirestore.instance.collection('waiting').doc(docID).delete();
// TODO: How to update numberWaiting in AppBar title?
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("# Waiting: ${numberWaiting.toString()}"),
),
body: SizedBox(
width: double.infinity,
child: Center(
child: StreamBuilder(
stream: waitingList.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Column(
...
);
}
else if (snapshot.hasData) {
return ListView.builder (
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, index) {
numberWaiting = index + 1;
String name = snapshot.data?.docs[index]['Name'];
return Card(
child: SizedBox(
child:ListTile(
title:
Row(
children: <Widget>[
Text(name),
],
),
onTap: () {
// Create or Update Record
// TODO: Update numberWaiting for title
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){
return CrudPage(
docId: snapshot.data?.docs[index].id.toString() ?? "",
docSnap: snapshot.data?.docs[index]);
}));
},
onLongPress: () {
// Delete Record
// TODO: Update numberWaiting for title
delete(snapshot.data?.docs[index].id.toString() ?? "");
},
),
),
);
},
);
}
else {
return const Text('No Data');
}
}, // Item Builder
),
),
),
);
}
}
Unfortunately this code only updates the # Waiting: X title once and doesn't refresh when an item is deleted or added.
Thank you for your help!
Simply update value and rebuild on "else if (snapshot.hasData)"
class HomePageState extends State {
Query waitingList = FirebaseFirestore.instance
.collection('waiting')
.orderBy('Time_In');
Future<int> countStream(Stream<QuerySnapshot<Object?>> stream) async =>
stream.length;
#override
Widget build(BuildContext context) {
var numberWaiting = "";
return Scaffold(
appBar: AppBar(
title: Text("# Waiting: $numberWaiting"),
),
body: SizedBox(
width: double.infinity,
child: Center(
child: StreamBuilder(
stream: waitingList.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Column(
...
);
}
else if (snapshot.hasData) {
setState((){
numberWaiting = snapshot.data?.docs.length.toString();
})
return ListView.builder (
itemCount: snapshot.data?.docs.length,
itemBuilder: (BuildContext context, index) {
String name = snapshot.data?.docs[index]['Name'];
return Card(
child: SizedBox(
child:ListTile(
title:
Row(
children: <Widget>[
Text(name),
],
),
),
),
);
},
);
}
else {
return const Text('No Data');
}
}, // Item Builder
),
),
),
);
}
}
Whenever I try fetching data from a REST API, I keep getting an error "Expected a value of type 'Widget?', but got one of type 'String'". There is nothing wrong with my code yet I keep getting the error.
This is the function for fetching items from the database.
Future<List<Map>> fetchItems() async {
List<Map> items = [];
//get data from API and assign to variable
http.Response response =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
if (response.statusCode == 200) {
//get data from the response
String jsonString = response.body;
items = jsonDecode(jsonString).cast<Map>();
}
return items;
}
This is my main.dart file
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PostList(),
);
}
}
class PostList extends StatelessWidget {
PostList({super.key});
final Future<List<Map>> _futurePosts = HTTPHelper().fetchItems();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Posts"),
),
body: FutureBuilder(
future: _futurePosts,
builder: ((context, snapshot) {
//check for error
if (snapshot.hasError) {
return Center(
child: Text("Some error has occured ${snapshot.error}"));
}
//has data
if (snapshot.hasData) {
List<Map> _posts = snapshot.data!;
return ListView.builder(
itemCount: _posts.length,
itemBuilder: ((context, index) {
Map _thisItem = _posts[index];
return ListTile(
title: _thisItem["title"],
subtitle: _thisItem["body"],
);
}));
}
//display a loader
return Center(child: CircularProgressIndicator());
}),
),
);
}
}
Any solution to this error?
The answer is pretty simple. You're assigning directly string value to the title(Which is expecting Widget).
You can try below code
ListView.builder(
itemCount: _posts.length,
itemBuilder: ((context, index) {
Map _thisItem = _posts[index];
return ListTile(
title: Text(_thisItem["title"].toString()),
subtitle: Text(_thisItem["body"].toString()),
);
}));
If this doesn't work. Please let me know.
ListTile(
title:NEED WIDGET HERE,
subtitle:NEED WIDGET HERE,)
I am using streambuilder to display snapshot data but it is not displaying. The screen is just blank but When I use the future builder with get() methode it display the data but I want realtime changes. I am new to flutter please help me with this. here is code.
class TalentScreen2 extends StatelessWidget {
final Query _fetchFavUser = FirebaseRepo.instance.fetchFavUsers();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
Text('Talent Screen 2(Favourites)'),
Expanded(child: _retrieveData(context))
],
),
),
);
}
Widget _retrieveData(BuildContext context) => StreamBuilder<QuerySnapshot>(
stream: _fetchFavUser.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return const Text('Something went wrong');
if (!snapshot.hasData) return const Text('Alas! No data found');
if (snapshot.connectionState == ConnectionState.waiting)
return Center(
child: CircularProgressIndicator(
strokeWidth: 2.0,
));
if (snapshot.connectionState == ConnectionState.done)
return theUserInfo(snapshot.data.docs);
return Container();
});
Widget theUserInfo(List<QueryDocumentSnapshot> data) {
return ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
var uid = data[index]['uid'];
TalentHireFavModel userData = TalentHireFavModel.fromMap(
data[index].data(),
);
return Card(
child: Column(
children: <Widget>[
Text(data[index]['orderBy']),
// Text(userData.name ?? ''),
Text(userData.categories),
Text(userData.skills),
// Text(userData.country ?? ''),
Text(userData.phoneNo),
Text(userData.hourlyRate),
Text(userData.professionalOverview),
Text(userData.skills),
Text(userData.expert),
// Text(userData.createdAt ?? ''),
_iconButton(userData.uid, context),
],
),
);
});
}
Future<DocumentSnapshot> fetch(data) async =>
await FirebaseRepo.instance.fetchWorkerUserData(data);
Widget _iconButton(uid, context) {
return IconButton(
icon: Icon(Icons.favorite),
onPressed: () {
BlocProvider.of<TalentFavCubit>(context).removeTalentFav(uid);
});
}
}
and here is the firestore query methode where I am just applying simple query to fetch all documents and display them. I want real-time changes
Query fetchFavUsers() {
var data = _firestore
.collection('workerField')
.doc(getCurrentUser().uid)
.collection('favourites')
// .where('uid', isNotEqualTo: getCurrentUser().uid)
.orderBy('orderBy', descending: true);
return data;
}
The solution is to just return the function. Get that method out of if statement and place it in just return statement.
At first, when i started writing my calls to get data from firestore, it worked. But when i tried writing more docs to my collection, it failed to bring data for the docs i recently added. Then, when i deleted the first one i added, i stopped receiveing data from firestore all together. I have tried several methods, but have all ended in failure.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class collect extends StatefulWidget {
#override
_collectState createState() => _collectState();
}
class _collectState extends State<collect>
{
Future _data;
void initState()
{
super.initState();
_data = getStuff();
}
Future getStuff()
async {
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection("buses").get();
return qn.docs;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _data,
builder: (_, snapshot)
{
if(snapshot.connectionState == ConnectionState.waiting)
{
return Center(
child:Text("Loading")
);
}
else if(snapshot.connectionState == ConnectionState.done)
{
return ListView.builder(itemCount: snapshot.data.length,itemBuilder:(_, index)
{
return Container(
child: ListTile(
title: Text(snapshot.data[index].data()["name"].toString()),
subtitle: Text(snapshot.data[index].data()["price"].toString()),
),
);
});
}
},
),
);
}
}
```![enter image description here](https://i.stack.imgur.com/L7FqF.jpg)
Define your database call as,
Future getStuff() async {
var docs;
await FirebaseFirestore.instance
.collection("buses")
.get()
.then((querySnapshot) {
docs = querySnapshot.docs;
});
return docs;
}
Then use the FutureBuilder in the build() function as,
return Scaffold(
body: Center(
child: FutureBuilder<dynamic>(
future: getStuff(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return Container(
child: ListTile(
title: Text(
snapshot.data[index].data()["name"].toString()),
subtitle: Text(
snapshot.data[index].data()["price"].toString()),
),
);
});
} else {
return CircularProgressIndicator();
}
},
),
),
);
I wrapped the FutureBuilder inside a Center just for clarity, you may remove that Center widget.
Helo, I am trying to check if user's account exists, if no, I want to run text 'account is deleted'.
But the problem is that when I start the app there is screen for existing account and only after reset I can get the real result.
Looks like check for account is done after running app for the first time, but I don't know where is the mistake.
Here is the code, thank you in advance:
class CheckIfDeletedAccount extends StatelessWidget {
String isAccountDeleted;
getData() async {
var userType = await Firestore.instance
.collection('users')
.where('userEmail', isEqualTo: email)
.getDocuments();
userType.documents.forEach((result) {
log(result.data["deleted"]);
isAccountDeleted = result.data["deleted"].toString();
});
}
#override
Widget build(BuildContext context) {
getData();
//log(isAccountDeleted);
if (isAccountDeleted == "true") {
return Scaffold(
body: Container(
child: Center(
child: Text("account is deleted"),
),
),
);
}
return MaterialApp(
theme: themeData,
home: Scaffold(
body: Bar(),
),
);
}
}
You need to wait for the result from Firebase. You are trying to build the widget before the isAccountDeleted is initialized.
In your scenario, you can use FutureBuilder as follows:
class CheckIfDeletedAccount extends StatelessWidget {
String isAccountDeleted;
Future<String> getData() async {
var userType = await Firestore.instance
.collection('users')
.where('userEmail', isEqualTo: email)
.getDocuments();
userType.documents.forEach((result) {
log(result.data["deleted"]);
isAccountDeleted = result.data["deleted"].toString();
});
return isAccountDeleted;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final isAccountDeleted = snapshot.data;
if (isAccountDeleted == "true") {
return Scaffold(
body: Container(
child: Center(
child: Text("account is deleted"),
),
),
);
}
return MaterialApp(
theme: themeData,
home: Scaffold(
body: Bar(),
),
);
}
return Center(child: const CircularProgressIndicator());
},
);
}
}
Based on savke comment you can use the following code using FutureBuilder:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class CheckIfDeletedAccount extends StatelessWidget {
Future getData() async {
String isAccountDeleted;
var userType = await Firestore.instance
.collection('users')
.where('userEmail', isEqualTo: email)
.getDocuments();
userType.documents.forEach((result) {
log(result.data["deleted"]);
isAccountDeleted = result.data["deleted"].toString();
});
return isAccountDeleted;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: themeData,
home: Scaffold(
body: FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
strokeWidth: 6,
valueColor: AlwaysStoppedAnimation<Color>(Colors.red),
),
);
} else {
if (snapshot.data == "true") {
return Container(
child: Center(
child: Text("account is deleted"),
),
);
}
else {
return Bar();
}
}
}),
));
}
}