A build function returned null. Flutter Firebase - firebase

I tried many things but couldn't get any workable solution, please help.
Even wit the FutureBuilder it doesn't seem to work and without it, I get the result when I hot reload. I don't know how to change the code to make it work. Probably there is something small missing, but I couldn't figure yet what it is or how to solve it.
"""import 'package:flutter/material.dart';
import 'package:list/screens/add_new_item_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedinUser;
Future<void> _fetchdata;
FirebaseAuth _auth = FirebaseAuth.instance;
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
void initState() {
super.initState();
_fetchdata = getCurrentUser();
}
Future<void> getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedinUser = user;
// print(loggedinUser.email);
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(
builder: (context) => Addnewitem()));
},
child: Icon(Icons.add),
),
appBar: AppBar(
leading: Container(),
title: Text("Shopping List"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
// messagesStream();
_auth.signOut();
Navigator.pop(context);
})
],
),
body: SafeArea(child:
MessagesStream(),
),
);
}
}
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
FutureBuilder(
future: _fetchdata,
builder: (context, myFuture){
if (myFuture.connectionState == ConnectionState.done && !myFuture.hasError && myFuture.hasData) {
if (myFuture.data != null) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('users')
.document(loggedinUser.uid)
.collection('items')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.hasError || snapshot.data == null || snapshot.connectionState == ConnectionState.waiting || loggedinUser.email == null) {
return (Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent)));
}
final items = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in items) {
final item = message.data['item'];
final quant = message.data['quant'];
final id = message.data['id'];
final boli = message.data['bool'];
// final currentUser = loggedinUser.email;
final messageBubble = MessageBubble(
text: item,
quant: quant,
documentReference: message.reference,
);
messageBubbles.add(messageBubble);
}
try {
return Expanded(
child: ListView(
// reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
children: messageBubbles,
),
);
} catch (e) {
return Container();
}
});
}else {
return Container();
}
} else {
return CircularProgressIndicator();
}
});
}
}
class MessageBubble extends StatelessWidget {
MessageBubble({this.text, this.quant, this.documentReference});
final String text;
final String quant;
final DocumentReference documentReference;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: Colors.tealAccent,
child: FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
text,
style: TextStyle(color: Colors.black, fontSize: 20),
),
Text(quant,
style: TextStyle(color: Colors.black, fontSize: 20))
],
),
onPressed: () {
documentReference.delete();
}),
)
],
),
),
);
}
}"""
Flutter doctor:
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.44.0)
[✓] Connected device (3 available)
• No issues found!

You need to add return keyword when using the build function since it returns a Widget:
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _fetchdata,
builder: (context, myFuture){
if (myFuture.connectionState == ConnectionState.done && !myFuture.hasError && myFuture.hasData) {
if (myFuture.data != null) {
return StreamBuilder<QuerySnapshot>(

For people having the same issue - here is my full code! Hope it helps
import 'package:flutter/material.dart';
import 'package:list/screens/add_new_item_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedinUser;
Future<void> _fetchdata;
FirebaseAuth _auth = FirebaseAuth.instance;
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
void initState() {
super.initState();
_fetchdata = getCurrentUser();
}
Future<void> getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedinUser = user;
// print(loggedinUser.email);
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Addnewitem()));
},
child: Icon(Icons.add),
),
appBar: AppBar(
leading: Container(),
title: Text("Shopping List"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
// messagesStream();
_auth.signOut();
Navigator.pop(context);
})
],
),
body: SafeArea(
child: MessagesStream(),
),
);
}
}
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _fetchdata,
builder: (context, myFuture) {
if (myFuture.connectionState == ConnectionState.done &&
!myFuture.hasError) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('users')
.document(loggedinUser.uid)
.collection('items')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData ||
snapshot.hasError ||
snapshot.data == null ||
snapshot.connectionState == ConnectionState.waiting ||
loggedinUser.email == null) {
return (Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent)));
}
final items = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in items) {
final item = message.data['item'];
final quant = message.data['quant'];
final id = message.data['id'];
final boli = message.data['bool'];
// final currentUser = loggedinUser.email;
final messageBubble = MessageBubble(
text: item,
quant: quant,
documentReference: message.reference,
);
messageBubbles.add(messageBubble);
}
try {
return Expanded(
child: ListView(
// reverse: true,
padding:
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
children: messageBubbles,
),
);
} catch (e) {
return Container();
}
});
} else {
return CircularProgressIndicator();
}
});
}
}
class MessageBubble extends StatelessWidget {
MessageBubble({this.text, this.quant, this.documentReference});
final String text;
final String quant;
final DocumentReference documentReference;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: Colors.tealAccent,
child: FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
text,
style: TextStyle(color: Colors.black, fontSize: 20),
),
Text(quant,
style: TextStyle(color: Colors.black, fontSize: 20))
],
),
onPressed: () {
documentReference.delete();
}),
)
],
),
),
);
}
}

Related

How to get userdata from firestore when refreshing the browser?

I have a Flutter web application where I sign in and navigate to a homepage.
Here I display the name of the user and that is working fine until I refresh my browser.
When I refresh my browser it returns a null value, but when I navigate to another page and back again I retrieve the users name. So I hoping there is another way to do this so I get my users name after refreshing the browser.
When I try to retrieve the users email from FirebaseAuth instead of the usersname from Firestore it works fine when refreshing the browser. So I think it has something to do with how I retrieve it from Firestore.
Here is some of my code:
Main.dart
class MyApp extends StatelessWidget {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
final FirebaseAuth auth = FirebaseAuth.instance;
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (context, snapshot)
{
if (snapshot.hasError) {
return (MaterialApp(
home: UserLoginPage(
),
));
}
if (snapshot.connectionState == ConnectionState.done) {
print('CONNECTED');
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, snapshot) {
return MaterialApp(
home: snapshot.hasData && snapshot.data != null
? const UserHomePage()
: const UserLoginPage(),
routes: {
UserLoginPage.id: (context) => const UserLoginPage(),
UserHomePage.id: (context) => const UserHomePage(),
},
debugShowCheckedModeBanner: false,
);
}
);
}
return Column();
});
}
}
HomePage
class _UserHomePageState extends State<UserHomePage> {
final scaffoldkey = GlobalKey<ScaffoldState>();
User? user = FirebaseAuth.instance.currentUser;
Future _getUserData() async {
await FirebaseFirestore.instance.collection('users').doc(user?.uid)
.get()
.then((snapshot) async {
if (snapshot.exists) {
setState(() {
name = snapshot.data()!['Navn'];
print(name!);
});
}
});
}
DateTime now = DateTime.now();
#override
void initState() {
super.initState();
_getUserData();
}
//String
String? name = '';
String? formattedDate = DateFormat('yyyy-MM-dd').format(DateTime.now());
#override
Widget build(BuildContext context) {
var appBarHeight = kToolbarHeight;
return Scaffold(
key: scaffoldkey,
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: const Color.fromRGBO(119, 221, 167, 1),
title: const Text(
'Test',
style: TextStyle(
fontFamily: 'Poppins',
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
),
backgroundColor: Colors.white,
drawer: Container(
padding: EdgeInsets.only(top: appBarHeight + 1),
child: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(name!),
),
const SizedBox(
height: 10,
),
],
),
),
),
body: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 20,
),
Text('Hei, $name!',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
],
),
),
));
}
}

How do I write to firebase using a TextFormField in a ListView in flutter

Hi Im trying to use List views to make the UI more interactive and appealing, however I cant figure out a way to pass the TextEditingController() pass through to other pages. I've attached the relevant pages of code below.
admin_exercise.dart
this creates both list views this is then passed to admin_screen.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
final _firestore = Firestore.instance;
late FirebaseUser users;
class ExerciseView extends StatefulWidget {
const ExerciseView({Key? key}) : super(key: key);
#override
State<ExerciseView> createState() => _ExerciseViewState();
}
class _ExerciseViewState extends State<ExerciseView> {
void getUsers() {
users.email.toString();
}
var selectedUser;
bool setDefaultUser = true;
var selectedExercise;
bool setDefaultExercise = true;
int set1 = 0;
List<Widget> _cardList = [];
void _addCardWidget() {
setState(() {
_cardList.add(SetCard(
setNo: (set1 + 1),
exercise: selectedExercise,
));
});
}
void _deleteCardWidget() {
setState(() {
_cardList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('exercises')
.orderBy('Title')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return Container();
if (setDefaultExercise) {
selectedExercise = snapshot.data?.documents[0]['Title'];
}
return DropdownButton(
isExpanded: false,
value: selectedExercise,
items: snapshot.data?.documents.map((value2) {
return DropdownMenuItem(
value: value2['Title'],
child: Text('${value2['Title']}'),
);
}).toList(),
onChanged: (value2) {
setState(
() {
// Selected value will be stored
selectedExercise = value2;
// Default dropdown value won't be displayed anymore
setDefaultExercise = false;
},
);
},
);
},
),
),
ElevatedButton(
onPressed: () {
setState(() {
_addCardWidget();
set1++;
});
},
child: Icon(Icons.add)),
Text('Sets: $set1'),
ElevatedButton(
onPressed: () {
_deleteCardWidget();
setState(() {
set1--;
});
},
child: Icon(Icons.remove))
],
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _cardList.length,
itemBuilder: (context, index) {
return _cardList[index];
}),
],
);
}
}
class SetCard extends StatelessWidget {
SetCard({required this.setNo, required this.exercise});
late int setNo;
late String exercise;
final repTextController = TextEditingController();
final notesTextController = TextEditingController();
Future<void> insertData(final reps) async {
_firestore.collection("plans").add(reps).then((DocumentReference document) {
print(document.documentID);
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// added in exercise here to help to understand how to write to firebase
Text('Set $setNo, $exercise'),
Row(
children: [
Expanded(
flex: 1,
child: TextFormField(
controller: repTextController,
decoration: InputDecoration(hintText: 'Reps...'),
keyboardType: TextInputType.number,
),
),
SizedBox(
width: 10,
),
Expanded(
flex: 4,
child: TextFormField(
controller: notesTextController,
decoration: InputDecoration(hintText: 'Notes...'),
keyboardType: TextInputType.number,
),
),
],
),
],
);
}
}
admin_screen.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:intl/intl.dart';
import 'package:fitness_guide/components/admin_exercise.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AdminScreen extends StatefulWidget {
static const String id = 'AdminScreen';
const AdminScreen({Key? key}) : super(key: key);
#override
State<AdminScreen> createState() => _AdminScreenState();
}
class _AdminScreenState extends State<AdminScreen> {
TextEditingController dateinput = TextEditingController();
final _auth = FirebaseAuth.instance;
final _firestore = Firestore.instance;
late FirebaseUser users;
void getUsers() {
users.email.toString();
}
var selectedUser;
bool setDefaultUser = true;
var selectedExercise;
bool setDefaultExercise = true;
int set1 = 0;
#override
void initState() {
dateinput.text = ""; //set the initial value of text field
super.initState();
}
List<Widget> _exerciseList = [];
void _addExerciseWidget() {
setState(() {
_exerciseList.add(ExerciseView());
});
}
void _deleteExerciseWidget() {
setState(() {
_exerciseList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
//submit to fbase here
},
label: const Text('Submit'),
icon: const Icon(Icons.thumb_up),
backgroundColor: Color(0xff75D6F2),
),
body: ListView(
physics: BouncingScrollPhysics(),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
height: 50,
child: Center(
child: TextField(
controller:
dateinput, //editing controller of this TextField
decoration: InputDecoration(
icon: Icon(Icons.calendar_today), //icon of text field
labelText: "Enter Date" //label text of field
),
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
2000), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() {
dateinput.text =
formattedDate; //set output date to TextField value.
});
} else {
print("Date is not selected");
}
},
))),
Center(
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('users')
.orderBy('email')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
// Safety check to ensure that snapshot contains data
// without this safety check, StreamBuilder dirty state warnings will be thrown
if (!snapshot.hasData) return Container();
// Set this value for default,
// setDefault will change if an item was selected
// First item from the List will be displayed
if (setDefaultUser) {
selectedUser = snapshot.data?.documents[0]['email'];
}
return DropdownButton(
isExpanded: false,
value: selectedUser,
items: snapshot.data?.documents.map((value1) {
return DropdownMenuItem(
value: value1['email'],
child: Text('${value1['email']}'),
);
}).toList(),
onChanged: (value1) {
setState(
() {
// Selected value will be stored
selectedUser = value1;
// Default dropdown value won't be displayed anymore
setDefaultUser = false;
},
);
},
);
},
),
),
//Row below for the first exercise sets etc
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _exerciseList.length,
itemBuilder: (context, index) {
return _exerciseList[index];
}),
Center(
child: Text('Add another Exercise...'),
),
ElevatedButton(
onPressed: () {
_addExerciseWidget();
},
child: Icon(Icons.add),
),
],
),
],
),
);
}
}```
p.s. sorry for any spagetti code
[Image shows the layout UI I'm trying to achieve][1]
[1]: https://i.stack.imgur.com/d3qxg.png

'Future is not a subtype of type 'Widget' - error only present when using Future.wait

I have a string error which I can't seem to debug I think its related to how I'm trying to retrieve data when using Future.wait I get the above error. If I only build a single future and don't use Future.wait and retrieve data using snapshot.data!.data()!["prediction"] I don't get any errors.
Code below for my future.wait
Any help appreciated!
class CorrectMood extends StatefulWidget {
const CorrectMood({Key? key}) : super(key: key);
#override
_CorrectMoodState createState() => _CorrectMoodState();
}
class _CorrectMoodState extends State<CorrectMood> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([getData(), getLatestMood()]),
builder: (context, AsyncSnapshot<List> snapshot) {
snapshot.data![0]; //getLatestMood
snapshot.data![1]; //getData
if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: const Text('Display the Picture'),
backgroundColor: kPrimaryColor,
),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
body: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 20.0, 8.0, 8.0),
child: Column(
children: [
Center(
child:
Text(
"${snapshot.data![0]["firstName"]}"
// "${snapshot.data![0].prediction}"
//"${snapshot.data!.data()!["firstName"]} \n\n "
"We have predicted your mood as:\n\n "
//"${DatabaseService.getMood()}\n\n"
"Please select a reason associated to your mood",
style: const TextStyle(
color: Colors.black, fontSize: 15),
textAlign: TextAlign.center,
),
),
countDocuments(),
],
),
),
],
),
),
);
}else {
return CircularProgressIndicator();
}
},
);
}
}
Future<DocumentSnapshot<Map<String, dynamic>>> getData() async {
var currentUser = FirebaseAuth.instance.currentUser;
return await FirebaseFirestore.instance
.collection('USER_TABLE')
.doc(currentUser!.uid)
.get();
}
countDocuments() async {
var currentUser = FirebaseAuth.instance.currentUser;
QuerySnapshot _myDoc = await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID')
.collection(currentUser!.uid)
.get();
List<DocumentSnapshot> _myDocCount = _myDoc.docs;
return (_myDocCount.length);
}
Future<DocumentSnapshot<Map<String, dynamic>>> getLatestMood() async {
var currentUser = FirebaseAuth.instance.currentUser;
var latestMoodDoc = countDocuments();
return await FirebaseFirestore.instance
.collection('userMoods')
.doc('useerID') // make this the userID
.collection('UbcNaFtJwXWoId9J5RLuVBVPhpN2') // make this increment every time
.doc('2') //this can be system generated name don't care about it
.get();
}

Stream Builder load more data

I am trying to load more data once I reach the end of the stream builder, everytime I reach the end, the stream builder reloads and bounces back to the beginning, and it does not allow me to scroll down ( keep reload and bounce back to the top).
What I tried is once I reach the end, increase the limit from the firebase, but I am not sure what the problem is.. anyone can help me with this?
Here is the sample code for what I want to do
_onEndScroll(ScrollMetrics metrics) {
//loadToTrue();
setState(() {
documentLimit = documentLimit + 10;
});
}
StreamBuilder(
initialData: cache,
stream: FirebaseFirestore.instance
.collection("timeline")
.doc(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> streamSnapshot) {
items = streamSnapshot.data != null &&
streamSnapshot.data.docs != null
? streamSnapshot.data.docs
: [];
List<Post> posts =
items.map((doc) => Post.fromDocument(doc)).toList();
cache = streamSnapshot.data;
return !streamSnapshot.hasData ||
streamSnapshot.connectionState ==
ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) {
_onEndScroll(scrollNotification.metrics);
}
},
child: Container(
height: MediaQuery.of(context).size.height - 200,
margin: EdgeInsets.only(bottom: 1),
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: ScrollPhysics()),
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (_, i) =>
timelineDecision == "follow"
? posts[i]
: postsLocal[i])));
})
and this is code for all
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/search.dart';
import 'package:fluttershare/pages/upload.dart';
import 'package:fluttershare/pages/upload_limit.dart';
import 'package:fluttershare/widgets/post.dart';
import 'package:latlong/latlong.dart';
final usersRef = FirebaseFirestore.instance.collection('users');
class Timeline extends StatefulWidget {
final User currentUser;
Timeline({this.currentUser});
#override
_TimelineState createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
QuerySnapshot cache;
List<Post> posts;
List<Post> postsLocal;
List<DocumentSnapshot> items;
List<String> followingList = [];
String timelineDecision = "local";
String address = "Norman";
ScrollController listScrollController;
int documentLimit = 5;
void initState() {
super.initState();
getFollowing();
}
getFollowing() async {
QuerySnapshot snapshot = await followingRef
.doc(currentUser.id)
.collection('userFollowing')
.get();
if (mounted) {
setState(() {
followingList = snapshot.docs.map((doc) => doc.id).toList();
});
}
}
setTimeline(String timelineDecision) {
setState(() {
this.timelineDecision = timelineDecision;
});
}
buildToggleTimeline() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TextButton(
onPressed: () => setTimeline("follow"),
child: Text('Following',
style: TextStyle(
color: timelineDecision == 'follow'
? Theme.of(context).primaryColor
: Colors.grey)),
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
),
),
TextButton(
onPressed: () => setTimeline("local"),
child: Text('Local',
style: TextStyle(
color: timelineDecision == 'local'
? Theme.of(context).primaryColor
: Colors.grey)),
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
)),
],
);
}
_onEndScroll(ScrollMetrics metrics) {
//loadToTrue();
setState(() {
documentLimit = documentLimit + 10;
});
}
#override
Widget build(context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Entango'),
actions: <Widget>[
PopupMenuButton(
icon: const Icon(Icons.add),
onSelected: choiceAction,
itemBuilder: (context) => [
PopupMenuItem(
child: Text("Timelimit post"),
value: 1,
),
PopupMenuItem(
child: Text("Normal post"),
value: 2,
)
]),
IconButton(
icon: const Icon(Icons.search),
tooltip: 'Show Snackbar',
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => Search())),
)
],
),
body: ListView(
children: <Widget>[
buildToggleTimeline(),
Divider(
height: 0.0,
),
//buildSlider(),
StreamBuilder(
initialData: cache,
stream: timelineDecision == "follow"
? FirebaseFirestore.instance
.collection("timeline")
.doc(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots()
: FirebaseFirestore.instance
.collection("postsLocalRef")
.doc(address)
.collection("userPosts")
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> streamSnapshot) {
items = streamSnapshot.data != null &&
streamSnapshot.data.docs != null
? streamSnapshot.data.docs
: [];
List<Post> posts =
items.map((doc) => Post.fromDocument(doc)).toList();
List<Post> postsLocal =
items.map((doc) => Post.fromDocument(doc)).toList();
cache = streamSnapshot.data;
return !streamSnapshot.hasData ||
streamSnapshot.connectionState ==
ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) {
_onEndScroll(scrollNotification.metrics);
}
},
child: Container(
height: MediaQuery.of(context).size.height - 200,
margin: EdgeInsets.only(bottom: 1),
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: ScrollPhysics()),
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (_, i) =>
timelineDecision == "follow"
? posts[i]
: postsLocal[i])));
})
],
),
);
}
void choiceAction(int value) {
if (value == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Upload_limit(
currentUser: currentUser,
)));
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Upload(
currentUser: currentUser,
)));
}
}
}
There are few issues I could find. One of them is using StreamBuilder to fetch paged data. While it might be great in theory but it won't work in case of Firebase as Firebase is providing Stream. So every-time, setState is called, a new steam will be created. I have wrote sample app for fetching data from firestore in paginated way.
//main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const PAGE_SIZE = 30;
bool _allFetched = false;
bool _isLoading = false;
List<ColorDetails> _data = [];
DocumentSnapshot? _lastDocument;
#override
void initState() {
super.initState();
_fetchFirebaseData();
}
Future<void> _fetchFirebaseData() async {
if (_isLoading) {
return;
}
setState(() {
_isLoading = true;
});
Query _query = FirebaseFirestore.instance
.collection("sample_data")
.orderBy('color_label');
if (_lastDocument != null) {
_query = _query.startAfterDocument(_lastDocument!).limit(PAGE_SIZE);
} else {
_query = _query.limit(PAGE_SIZE);
}
final List<ColorDetails> pagedData = await _query.get().then((value) {
if (value.docs.isNotEmpty) {
_lastDocument = value.docs.last;
} else {
_lastDocument = null;
}
return value.docs
.map((e) => ColorDetails.fromMap(e.data() as Map<String, dynamic>))
.toList();
});
setState(() {
_data.addAll(pagedData);
if (pagedData.length < PAGE_SIZE) {
_allFetched = true;
}
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: NotificationListener<ScrollEndNotification>(
child: ListView.builder(
itemBuilder: (context, index) {
if (index == _data.length) {
return Container(
key: ValueKey('Loader'),
width: double.infinity,
height: 60,
child: Center(
child: CircularProgressIndicator(),
),
);
}
final item = _data[index];
return ListTile(
key: ValueKey(
item,
),
tileColor: Color(item.code | 0xFF000000),
title: Text(
item.label,
style: TextStyle(color: Colors.white),
),
);
},
itemCount: _data.length + (_allFetched ? 0 : 1),
),
onNotification: (scrollEnd) {
if (scrollEnd.metrics.atEdge && scrollEnd.metrics.pixels > 0) {
_fetchFirebaseData();
}
return true;
},
),
);
}
}
class ColorDetails {
final String label;
final int code;
ColorDetails(this.code, this.label);
factory ColorDetails.fromMap(Map<String, dynamic> json) {
return ColorDetails(json['color_code'], json['color_label']);
}
Map toJson() {
return {
'color_code': code,
'color_label': label,
};
}
}
In case you are interested, you can checkout the article I have written as well at https://blog.litedevs.com/infinite-scroll-list-using-flutter-firebase-firestore

flutter firestore get just last result

my friends I have a problem
Only the last id is displayed in the documents, not all of them
What is the problem with the code?
I linked the data in the form of One to Many, each user has a group of friends
You can see the code to solve the problem
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tawasul/components/myResponsiveLibrary.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
class MainDrawer extends StatefulWidget {
const MainDrawer({Key key}) : super(key: key);
#override
_MainDrawerState createState() => _MainDrawerState();
}
class _MainDrawerState extends State<MainDrawer> {
#override
void initState() {
super.initState();
Firebase.initializeApp();
getUsers();
}
final friendsRef = FirebaseFirestore.instance.collection('friends');
String id;
String getUsers() {
friendsRef.get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((DocumentSnapshot doc) {
setState(() {
id = doc.id;
return id;
});
});
});
}
#override
Widget build(BuildContext context) {
User user = FirebaseAuth.instance.currentUser;
final userID = user.uid;
return Drawer(
child: SafeArea(
child: FutureBuilder<DocumentSnapshot>(
future: friendsRef.doc(id).get(),
builder: (
BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot,
) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data.data();
if (data['user'] == "users/" + userID) {
return Container(
padding: EdgeInsets.only(top: MainModel().largePadding * 2),
color: MainModel().mainColor,
child: Column(
children: [
Container(
padding: EdgeInsets.only(
left: MainModel().middlePadding,
top: MainModel().middlePadding,
),
child: ListTile(
title: Text(
"${data['name']}",
style: TextStyle(
color: MainModel().whiteColor,
fontWeight: FontWeight.bold,
fontSize: 24),
),
subtitle: InkWell(
onTap: () {},
child: Container(
padding: EdgeInsets.only(top: 10),
child: Text(
"${data['phone']}",
style: TextStyle(
color: MainModel().whiteColor, fontSize: 14),
),
),
),
trailing: IconButton(
onPressed: () {
Navigator.of(context).pushNamed(
'/screens/MemberPage',
);
},
icon: Icon(Icons.info,
color: MainModel().thirdColor, size: 40),
),
),
),
Divider(
thickness: 3,
color: MainModel().thirdColor,
),
],
));
}
}
return Text("loading");
},
)));
}
}
I hope you get the problem right, thank you for your help
I think it's because you're using forEach and then assigning the value to a variable.
You call the function, assign the value then print the value hence always the last value is printed.

Resources