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.
Related
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->
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,
);
}),
);
}
}
I have a problem with my BLoC implementation, I have this code in synchronize.dart:
...
class _SynchronizeState extends State<Synchronize> {
UserBloc userBloc;
//final dbRef = FirebaseDatabase.instance.reference();
#override
Widget build(BuildContext context) {
userBloc = BlocProvider.of(context);
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Container(
...
),
child: StreamBuilder(
stream: dbRef.child('info_tekax').limitToLast(10).onValue,
builder: (context, snapshot) {
if(snapshot.hasData && !snapshot.hasError){
Map data = snapshot.data.snapshot.value;
List keys = [];
data.forEach( (index, data) => keys.add(index) );
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) => SynchronizeItem(title: keys[index], bottom: 10, onPressed: (){ print(keys[index]); })
);
}else{
return Container(
child: Center(
child: Text('Loading...'),
),
);
}
}
),
),
);
}
}
The previos code, works correctly, but i want implemente bloc Pattern, i have userBloc then i want to put this
userBloc.getDevicesForSinchronized()
instead of
dbRef.child('info_tekax').limitToLast(10).onValue,
my problem is this:
void getDevicesForSynchronized() {
return dbRef.child(DEVICES).limitToLast(10).onValue;
}
i get this error **A vaue of type 'Stream' can't be returned from method 'getDevicesForSynchronized' because it has a return type of 'void'
The error is very clear, but i don't know what is type that i need return, try:
Furure<void> getDevicesForSynchronized() async {
return await dbRef.child(DEVICES).limitToLast(10).onValue;
}
or
Furure<void> getDevicesForSynchronized() async {
dynamic result = await dbRef.child(DEVICES).limitToLast(10).onValue;
}
and another solutions, but I don't know how return correctly value for use in the StreamBuilder
From the error message you can see that the return type is Stream. Change your method like:
Future<Stream> getDevicesForSynchronized() async {
return dbRef.child(DEVICES).limitToLast(10).onValue;
}
I have a StreamBuilder inside my Widget build of UserListDart:
StreamBuilder(
stream: stream.asStream(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData) {
return Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
snapshot.data[index].firstname + " " +
snapshot.data[index].lastname
),
onTap: () {
Navigator.of(context).push(DetailScreenDart(snapshot.data[index]));
},
);
}
)
);
}
}
...
)
The Stream is defined in the initState:
Future<List> stream;
#override
void initState() {
super.initState();
stream = fetchPost();
}
The fetchPost() is an api call:
Future<List<User>> fetchPost() async {
final response = await http.get('url');
final jsonResponse = json.decode(response.body);
List<User> users = [];
for(var u in jsonResponse){
User user = User(
firstname: u["firstname"],
lastname: u["lastname"],
);
users.add(user);
}
return users;
}
I Navigate to another Page to change for example the firstname (api get updated) and I Navigate back to the UserList:
Navigator.pushReplacement(
context,
new MaterialPageRoute(builder: (context) => new UserListDart())
).then((onValue) {
fetchPost();
});
But the StreamBuilder won't get updated and I don't know why.
Note:
I think the StreamBuilder don't realise that a change has happend when I navigate back. It only applies the changes if I reopen the Page..
You should be using setState and updating your stream variable with the result of the fetchList() call:
Navigator.pushReplacement(
context,
new MaterialPageRoute(builder: (context) => new UserListDart())
).then((onValue) {
setState((){
stream = fetchPost();
});
});
Here's a working example of what you want to achieve:
class StreamBuilderIssue extends StatefulWidget {
#override
_StreamBuilderIssueState createState() => _StreamBuilderIssueState();
}
class _StreamBuilderIssueState extends State<StreamBuilderIssue> {
Future<List<String>> futureList;
List<String> itemList = [
'item 1',
'item 1',
'item 1',
'item 1',
'item 1',
];
#override
void initState() {
futureList = fetchList();
super.initState();
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Center(
child: StreamBuilder(
stream: futureList.asStream(),
builder: (context, snapshot){
if(snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index){
return Text(snapshot.data[index]);
},
);
}else{
return CircularProgressIndicator();
}
},
),
),
),
RaisedButton(
onPressed: goToAnotherView,
child: Text('Next View'),
),
RaisedButton(
onPressed: addItem,
child: Text('AddItem'),
)
],
),
);
}
Future<List<String>> fetchList(){
return Future.delayed(Duration(seconds: 2), (){
return itemList;
});
}
void goToAnotherView(){
Navigator.push(context, MaterialPageRoute(
builder: (context){
return StreamBuilderIssueNewView(addItem);
})
).then((res){
setState(() {
futureList = fetchList();
});
});
}
void addItem(){
itemList.add('anotherItem');
}
}
class StreamBuilderIssueNewView extends StatelessWidget {
final Function buttonAction;
StreamBuilderIssueNewView(this.buttonAction);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: <Widget>[
Text('New view'),
RaisedButton(
onPressed: buttonAction,
child: Text('AddItem'),
)
],
),
),
);
}
}
By the way, you could also just use a FutureBuilder as your are not using a real Stream here, just an api fetch and you have to update with setState anyway.
My dashboard code looks like this,
Here I am doing get req in getReport method, I have added the RefreshIndicator in the code which when pulled down inside container should do the refresh, there I am calling my getData(), But I am not getting the refreshed content, I am adding my code below, let me know if anywhere I made a mistake.
below my dashboard.dart
class Window extends StatefulWidget {
#override
_WindowState createState() => _WindowState();
}
class _WindowState extends State<Window> {
Future reportList;
#override
void initState() {
super.initState();
reportList = getReport();
}
Future<void> getReport() async {
http.Response response =
await http.get(reportsListURL, headers: {"token": "$token"});
switch (response.statusCode) {
case 200:
String reportList = response.body;
var collection = json.decode(reportList);
return collection;
case 403:
break;
case 401:
return null;
default:
return 1;
}
}
getRefreshScaffold() {
return Center(
child: RaisedButton(
onPressed: () {
setState(() {
reportList = getReport();
});
},
child: Text('Refresh, Network issues.'),
),
);
}
getDashBody(var data) {
double maxHeight = MediaQuery.of(context).size.height;
return Column(
children: <Widget>[
Container(
height: maxHeight - 800,
),
Container(
margin: new EdgeInsets.all(0.0),
height: maxHeight - 188,
child: new Center(
child: new RefreshIndicator( //here I am adding the RefreshIndicator
onRefresh:getReport, //and calling the getReport() which hits the get api
child: createList(context, data),
),),
),
],
);
}
Widget createList(BuildContext context, var data) {
Widget _listView = ListView.builder(
itemCount: data.length,
itemBuilder: (context, count) {
return createData(context, count, data);
},
);
return _listView;
}
createData(BuildContext context, int count, var data) {
var metrics = data["statistic_cards"].map<Widget>((cardInfo) {
var cardColor = getColorFromHexString(cardInfo["color"]);
if (cardInfo["progress_bar"] != null && cardInfo["progress_bar"]) {
return buildRadialProgressBar(
context: context,
progressPercent: cardInfo["percentage"],
color: cardColor,
count: cardInfo["value"],
title: cardInfo["title"],
);
} else {
return buildSubscriberTile(context, cardInfo, cardColor);
}
}).toList();
var rowMetrics = new List<Widget>();
for (int i = 0; i < metrics.length; i += 2) {
if (i + 2 < metrics.length)
rowMetrics.add(Row(children: metrics.sublist(i, i + 2)));
else
rowMetrics.add(Row(children: [metrics[metrics.length - 1], Spacer()]));
}
return SingleChildScrollView(
child: LimitedBox(
// maxHeight: MediaQuery.of(context).size.height / 1.30,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: rowMetrics,
),
),
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: reportList,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
var data = snapshot.data;
if (snapshot.hasData && !snapshot.hasError) {
return getDashBody(data);
} else if (data == null) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("Timeout! Log back in to continue"),
Padding(
padding: EdgeInsets.all(25.0),
),
RaisedButton(
onPressed: () {
setState(() {
token = null;
});
Navigator.of(context).pushReplacement(
CupertinoPageRoute(
builder: (BuildContext context) => LoginPage()),
);
},
child: Text('Login Again!'),
),
],
),
);
} else {
getRefreshScaffold();
}
}
},
);
}
}
Basic Example
Below is a State class of a StatefulWidget, where:
a ListView is wrapped in a RefreshIndicator
numbersList state variable is its data source
onRefresh calls _pullRefresh function to update data & ListView
_pullRefresh is an async function, returning nothing (a Future<void>)
when _pullRefresh's long running data request completes, numbersList member/state variable is updated in a setState() call to rebuild ListView to display new data
import 'package:flutter/material.dart';
import 'dart:math';
class PullRefreshPage extends StatefulWidget {
const PullRefreshPage();
#override
State<PullRefreshPage> createState() => _PullRefreshPageState();
}
class _PullRefreshPageState extends State<PullRefreshPage> {
List<String> numbersList = NumberGenerator().numbers;
#override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: _pullRefresh,
child: ListView.builder(
itemCount: numbersList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(numbersList[index]),
);
},),
),
);
}
Future<void> _pullRefresh() async {
List<String> freshNumbers = await NumberGenerator().slowNumbers();
setState(() {
numbersList = freshNumbers;
});
// why use freshNumbers var? https://stackoverflow.com/a/52992836/2301224
}
}
class NumberGenerator {
Future<List<String>> slowNumbers() async {
return Future.delayed(const Duration(milliseconds: 1000), () => numbers,);
}
List<String> get numbers => List.generate(5, (index) => number);
String get number => Random().nextInt(99999).toString();
}
Notes
If your async onRefresh function completes very quickly, you may want to add an await Future.delayed(Duration(seconds: 2)); after it, just so the UX is more pleasant.
This gives time for the user to complete a swipe / pull down gesture & for the refresh indicator to render / animate / spin indicating data has been fetched.
FutureBuilder Example
Here's another version of the above State<PullRefreshPage> class using a FutureBuilder, which is common when fetching data from a Database or HTTP source:
class _PullRefreshPageState extends State<PullRefreshPage> {
late Future<List<String>> futureNumbersList;
#override
void initState() {
super.initState();
futureNumbersList = NumberGenerator().slowNumbers();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<String>>(
future: futureNumbersList,
builder: (context, snapshot) {
return RefreshIndicator(
child: _listView(snapshot),
onRefresh: _pullRefresh,
);
},
),
);
}
Widget _listView(AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index]),
);
},);
}
else {
return Center(
child: Text('Loading data...'),
);
}
}
Future<void> _pullRefresh() async {
List<String> freshNumbers = await NumberGenerator().slowNumbers();
setState(() {
futureNumbersList = Future.value(freshNumbers);
});
}
}
Notes
slowNumbers() function is the same as in the Basic Example above, but the data is wrapped in a Future.value() since FutureBuilder expects a Future, but setState() should not await async data
according to Rémi, Collin & other Dart/Flutter demigods it's good practice to update Stateful Widget member variables inside setState() (futureNumbersList in FutureBuilder example & numbersList in Basic example), after its long running async data fetch functions have completed.
see https://stackoverflow.com/a/52992836/2301224
if you try to make setState async, you'll get an exception
updating member variables outside of setState and having an empty setState closure, may result in hand-slapping / code analysis warnings in the future
Not sure about futures, but for refresh indicator you must return a void so
Use something like
RefreshIndicator(
onRefresh: () async {
await getData().then((lA) {
if (lA is Future) {
setState(() {
reportList = lA;
});
return;
} else {
setState(() {
//error
});
return;
}
});
return;
},
Try this and let me know!
EDIT:
Well, then just try this inside you refresh method
setState(() {
reportList = getReport();
});
return reportList;
Try this:
onRefresh: () {
setState(() {});
}}
instead of onRefresh:getReport
reportList field is Future which returns its value once. So, when you call getReport again it changes nothing. Actually, more correctly it'll be with Stream and StreamBuilder instead of Future and FutureBuilder. But for this code it can be shortest solution
Easy method: you can just use Pull Down to Refresh Package - https://pub.dev/packages/pull_to_refresh
In Non-scrollable list view, RefreshIndicator does not work, so you have to wrap your widget with Stack for implementing pull down to refresh.
RefreshIndicator(
onRefresh: () {
// Refresh Functionality
},
child: Stack(
children: [
ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
)
],
),
// Your Widget
],
);
),
I am working on a huge project which contains CustomScrollView, NestedScrollView, ListView, etc I tried every answer above and all of the answers use RefreshIndicator from flutter SDK. It doesn't work entirely with my app because I also have horizontal scroll views. So in order to implement it I had to use NestedScrollView on almost every screen. Then I came to know about liquid_pull_to_refresh, applied it to the top widget, and WOLAAH! If you need a separate logic for each screen then use it at the top of each screen but in my case, I'm refreshing the whole project's data.