Flutter fetch nested future - flutter

I have a method that fetches a PatientLog from SQLite.However, This PatientLog table mapped to an object with a class named PatientLog. Inside this PatientLog class, several other objects such as Speciality, AttendingPhysician, Course, etc. I need to map these PatienLog records to a local object. However, I have to use nested Futures. I need to retrieve the data from this nested Future. Think of Future of Future.
This is my fetch method
Future<List<Future<PatientLog>>> getForms() async {
Database db = await instance.getDatabase;
List<Map<String, dynamic>> forms =
await db.query(_tablePatientLog, orderBy: 'id DESC');
Institute? institute;
AttendingPhysician? attendingPhysician;
Speciality? speciality;
Course? course;
List<Future<PatientLog>> list = forms.map((myMap) async {
int? courseId = myMap['course_id'] as int?;
int? specialityId = myMap['speciality_id'] as int?;
int? attendingId = myMap['attending_id'] as int?;
int? instituteId = myMap['institute_id'] as int?;
if (courseId != null) {
await getCourse(courseId).then((value) => course=value);
}
if (attendingId != null) {
await getAttending(attendingId).then((value) => attendingPhysician=value);
}
if (specialityId != null) {
await getSpeciality(specialityId).then((value) => speciality=value);
}
if (instituteId != null) {
await getInstitute(instituteId).then((value) => institute=value);
}
return PatientLog.fromMap(
myMap, institute, course, attendingPhysician, speciality);
}).toList();
return list;
}
I need to display that information on a screen. I get an error type 'List<Future<PatientLog>>' is not a subtype of type 'Future<Object?>?'
class _DraftsState extends State<Drafts> {
final SQFLiteHelper _helper = SQFLiteHelper.instance;
#override
void initState() {
super.initState();
_refresh();
}
late List<Future<PatientLog>> fromDatabase;
Future<dynamic> _refresh() async {
await _helper.getForms().then((value) async{
setState(() {
fromDatabase = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _helper.getForms(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data!.isEmpty) {
return Center(
child: Text(
"Henüz kaydedilmiş taslak bulunmamaktadır.",
textAlign: TextAlign.center,
style: TEXT_STYLE,
));
}
if (snapshot.hasError) {
return Center(
child: Text(
'Sanırım bir şeyler ters gitti.',
style: TEXT_STYLE,
));
}
if (snapshot.connectionState == ConnectionState.done) {
return RefreshIndicator(
backgroundColor: Colors.grey[700],
color: LIGHT_BUTTON_COLOR,
onRefresh: _refresh,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
return FutureBuilder(
future: snapshot.data,
builder: (context,innerSnap) {
return Text(innerSnap.toString());/*CustomListTile(
formData: innerSnap.data[index],
index: index,
routeTo: 1,
isDeletable: true,
);*/
}
);
},
),
),
);
}
return const Center(
child: Text("Nothing")//spinkit,
);
}),
);
}
}

Related

Flutter Riverpod StreamProvider not waiting for for loop to finish before building Widget

I have a StreamProvider here:
final secondTabProvider = StreamProvider((ref){
EmergencyContactsController contacts = EmergencyContactsController(currentUserID: ref.read(authProvider).currentUser!.uid);
return contacts.getUserEmergencyContacts();
});
And I call it in my build method like so:
_secondTab.when(
data: (data) {
if (!data.exists){
return Text("no data")
}
Map<String, dynamic doc = data.doc() as Map<String, dynamic>;
List conversations = doc['conversation'];
// Store the user profiles
List<EmergencyContactModel> users = [];
for (Map<String, dynamic> user in userConversations){
contacts.getContactInfo(
uid: user['userID']
).then((value){
if (value != null){
EmergencyContactModel contact = EmergencyContactModel.fromJson(value);
contact.messageID = value["id"] + ref.read(authProvider).currentUser!.uid;
users.add(contact);
}
});
}
return Listview.builder(
itemCount: users.length,
itemBuilder: (BuildContext context, int index) => Text(users[index]['name'])
);
},
error: (err, _){
return Text("Error")
},
loading: () => CircularProgressIndicator()
)
The contacts.getContactInfo() method is an async and I need it to execute before the loop continues to the next iteration, but it's not doing that. Any help would be largely appreciated.
I solved it. I converted the for loop into its own async function as seen below:
// Generate a list of users that the current user has had conversations with
userinfoGenerator(List userIDs) async {
// Get the user profiles
List<EmergencyContactModel> users = [];
for (Map<String, dynamic> user in userIDs){
Map<String, dynamic>? contactInfo = await contacts.getContactInfo(uid: user['userID']);
if (contactInfo != null){
EmergencyContactModel contact = EmergencyContactModel.fromJson(contactInfo);
contact.messageID = contactInfo["id"] + ref.read(authProvider).currentUser!.uid;
users.add(contact);
}
}
return users;
}
And then I used a Future Builder to return the result of the function as seen below:
return FutureBuilder(
future: userinfoGenerator(userConversations),
builder: (BuildContext context, AsyncSnapshot snapshot){
// Checking if future is resolved
if (snapshot.connectionState == ConnectionState.done) {
// If we got an error
if (snapshot.hasError) {
return Center(
child: CustomText(
label: '${snapshot.error} occurred',
),
);
// if we got our data
} else if (snapshot.hasData) {
// Extracting data from snapshot object
final data = snapshot.data;
return ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
List<String> theName = data[index].name
.split(" ");
return Padding(
padding: const EdgeInsets.only(
top: 12.0),
child: CustomListTile(
contact: data[index],
label: theName.length == 1
? theName[0][0]
: "${theName[0][0]} ${theName[theName
.length - 1][0]}"
),
);
}
);
}
}
return const Center(
child: CircularProgressIndicator(
color: kDefaultBackground,
),
);
},
);

list<dynamic> is not a subtype of type FutureOr<List<Map<String,dynamic>> error in flutter

I have been trying with last an hour but not getting solution and failing completely to understand why its showing an error...
I have created a function for fetching data,
I have placed print statement for seeing what does it returns...here it is printing data but while inside feature builder it showing an error...
when I run app its showing output with
list<dynamic> is not a subtype of type FutureOr<List<Map<String,dynamic>>
it means its executes snapshot.haserror part
here is my code
class _HomeScreenState extends State<HomeScreen> {
Future<List<Map<String,dynamic>>> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/photos"));
print("fetchdata function showing"+json.decode(resp.body).toString());
return json.decode(resp.body);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
),
);
}
MyBody() {
return FutureBuilder<List<Map<String,dynamic>>>(
future: fetchdata(),
builder: (context, snapshot) {
print("Futurebuilder showing:"+snapshot.toString());
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
print('againt'+snapshot.toString());
List<Map<String,dynamic>> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Text(data[index]['title']));
});
}
}
},
);
}}
Future<List<Map<String, dynamic>>> fetchdata() async {
var resp = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/photos"));
print("fetchdata function showing" + json.decode(resp.body).toString());
List<dynamic> result = jsonDecode(resp.body);
return result.map((e) => e as Map<String, dynamic>).toList();
}
just change your function like this
Your API Call:
Future<List<dynamic>> getJobsData() async {
String url = 'https://jsonplaceholder.typicode.com/photos';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body);
}
Your Widget:
Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var title = snapshot.data![index]['title'];
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
title: Text(title),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
Result->

How to use setState in FutureBuilder?

I have a function that accepts data via API:
//fetch data from API
Future<List<CurrencyModel>?> _fetchCurrency() async {
currencyList = [];
final response = await http.get(
Uri.parse(
'https:...'),
);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.isNotEmpty) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
currencyList.add(
CurrencyModel.fromJson(map),
);
}
}
setState(() {
currencyList;
});
}
return currencyList;
} else {
throw Exception('Failed to load currencies');
}
}
I moved the logic of working with the API into a separate file, created a regular class with the Future function. How now to be with setState which was in Future?
Because setState cannot be added to a regular class.
How to add it to FutureBuilder?
My code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Currencies'),
centerTitle: true,
),
body: FutureBuilder(
future: client.fetchCurrency(),
builder: (BuildContext context,
AsyncSnapshot<List<CurrencyModel>?> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: currencyList.length,
itemBuilder: (context, index) => CurrencyCard(
currencyList[index],
),
);
} else if (snapshot.hasError) {
return Text(
'${snapshot.error}',
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: startTimer,
child: const Icon(Icons.update_sharp),
),
);
}
It is easy:
1 Declare a function to process your list
List<CurrencyModel>? _processOnEnd(List<CurrencyModel>? value){
//write what you need inside this function
return value;
}
2 Call it inside then method of your Future
FutureBuilder(
future: client.fetchCurrency().then(_processOnEnd),

Future builder returning length as null

I am retreiving data from cloud firestore and using Future builder and Listview Builder to display the data. But i am getting null values in the Listview builder i.e displaying the CircularProgressIndicator always.Can't figure out the problem.Any solution will be of great help.
The print(values) function prints out: [9,8] successfully
This is the code i implemented:
Future<List> getassignment() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final name = prefs.getString('orgname') ?? '';
print(name);
var query = FirebaseFirestore.instance.collection('Org').doc(name).collection('Login').doc(FirebaseAuth.instance.currentUser.uid);
query.snapshots().forEach((doc) {
List values = List.from(doc.data()['fields']['class']);
print(values);
return values;
});
}
// void getlist() async{
// await getassignment();
// }
#override
void initState() {
// getlist();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF1976D2),
body: FutureBuilder(
future: getassignment(),
builder: (context,snapshot){
List list = snapshot.data;
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else{
return Container(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, position) {
return GestureDetector(
onTap: (){
Navigator.of(context).push(MaterialPageRoute<Null>(
builder: (BuildContext context){
return new SubjectList(
clas: list[position].toString(),
);
}
));
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(list[position].toString(), style: TextStyle(fontSize: 22.0),),
),
),
);
},
),
);
}
},
),
);
}
You are assigning and returning data inside of foreach loop. So that won't return anything.
// try adding await in this line.
var query = await FirebaseFirestore.instance.collection('Org').doc(name).collection('Login').doc(FirebaseAuth.instance.currentUser.uid);
List values = query.snapshots().forEach((doc) => List.from(doc.data()['fields']['class']));
print(values);
return values;
You need to do something like this.

type 'int' is not a subtype of type 'String' - Flutter Android

My app was working fine yesterday and it was displaying the posts on Timeline page correctly. But now today when I opened by project again and wanted to continue working on it, when I run the Debug app, it generated an error on the Timeline page that "type int is not a subtype of type String".
Here is my Timeline.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/pages/search.dart';
import 'package:fluttershare/widgets/header.dart';
import 'package:fluttershare/widgets/post.dart';
import 'package:fluttershare/widgets/progress.dart';
class Timeline extends StatefulWidget {
final User currentUser;
Timeline({this.currentUser});
#override
_TimelineState createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
List<Post> posts;
List<String> followingList = [];
// #override
// void initState() {
// super.initState();
// getTimeline().whenComplete(() {
// setState(() {});
// });
// getFollowing();
// }
Future<void> getTimeline() async {
QuerySnapshot snapshot = await timelineRef
.document(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.getDocuments();
List<Post> posts =
snapshot.documents.map((doc) => Post.fromDocument(doc)).toList();
setState(() {
this.posts = posts;
});
}
getFollowing() async {
QuerySnapshot snapshot = await followingRef
.document(currentUser.id)
.collection('userFollowing')
.getDocuments();
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList();
});
}
buildTimeline() {
if (posts == null) {
return circularProgress();
} else if (posts.isEmpty) {
return buildUsersToFollow();
} else {
return ListView.builder(
itemCount: posts.length,
itemBuilder: (BuildContext ctxt, int index) {
return Text(posts[index].toString());
});
}
}
buildUsersToFollow() {
return StreamBuilder(
stream:
usersRef.orderBy('timestamp', descending: true).limit(30).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<UserResult> userResults = [];
snapshot.data.documents.forEach((doc) {
User user = User.fromDocument(doc);
final bool isAuthUser = currentUser.id == user.id;
final bool isFollowingUser = followingList.contains(user.id);
if (isAuthUser) {
return;
} else if (isFollowingUser) {
return;
} else {
UserResult userResult = UserResult(user);
userResults.add(userResult);
}
});
return Container(
color: Theme.of(context).accentColor.withOpacity(0.2),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.person_add,
color: Theme.of(context).primaryColor,
size: 30.0,
),
SizedBox(
width: 8.0,
),
Text(
"Users to Follow",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 30.0,
),
),
],
),
),
Column(children: userResults),
],
),
);
},
);
}
#override
Widget build(context) {
return Scaffold(
appBar: header(context, isAppTitle: true),
body: RefreshIndicator(
onRefresh: () => getTimeline(),
child: FutureBuilder(
future: timelineRef
.document(widget.currentUser.id)
.collection('timelinePosts')
// .orderBy('timestamp', descending: true)
.getDocuments(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState != ConnectionState.waiting) {
print("${widget.currentUser.id}");
print("${snapshot.data.documents.length}");
var posts = snapshot.data.documents
.map((doc) => Post.fromDocument(doc))
.toList();
if (posts.length > 0)
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
// posts[index] gives you the post item.
return posts[index];
});
// else
// return
} else
return CircularProgressIndicator();
})),
);
}
}
Debug Console:
In
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList();
});
Do This
setState(() {
followingList = snapshot.documents.map((doc) => doc.documentID).toList().toString();
});