how can i compound firestore datas - flutter

I want to compound firestore datas. Which widget is useful for multiple data?
For instance, i have user datas and user post's datas. I want to show them in the same Card Widget, but their snapshots are different. How can i compound them ?
I write just like this, but it shows just 1 snapshot, i want to both futurebuilder and streambuilder snapshots:
note: i can't use user datas, in below it has final dataUser = snapshotUser.data;
but i can't use it, it gives an error
class HomePageBodyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collectionGroup('Posts').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView(
children: snapshot.data.docs.map((DocumentSnapshot docPost) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('Users')
.doc(docPost.reference.parent.parent.id)
.get(),
builder: (context, snapshotUser) {
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}
return homePageCard(context, dataPost: docPost, dataUser: snapshotUser.data);
},
);
}).toList());
});
}

i solved it, like this:
class HomePageBodyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) => StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collectionGroup('Posts').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final dataPost = snapshot.data.docs[index];
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('Users')
.doc(dataPost.reference.parent.parent.id)
.get(),
builder: (context, snapshotUser) {
final dataUser = snapshotUser.data;
if (!snapshotUser.hasData) {
return Center(child: CircularProgressIndicator());
}
return homePageCard(context,
dataPost: dataPost, dataUser: dataUser);
},
);
});
});
}

Related

Flutter Firestore streambuilder returning an error

I have a collection called Todos in Firestore with 3 possible properties (id, text and checked).
So far i have succeeded on creating and saving todo's. Now i want them in a Listview but it returns an error on hot restart:
════════ Exception caught by widgets library ═══════════════════════════════════
type 'Null' is not a subtype of type 'String'
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
My code for displaying the ListView:
final Stream<QuerySnapshot> _todostream =
FirebaseFirestore.instance.collection('Todos').snapshots();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Styles.bgColor,
floatingActionButton: FloatingActionButton(
onPressed: createNewTask,
child: const Icon(Icons.add),
),
body: StreamBuilder(
stream: _todostream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(title: Text(data['text']));
}).toList(),
);
},
),
);
}
}
I hoped to see a listview with the results of my collection, it contains 2 items.
It would be better to accept null and check if it contains data or not.
body: StreamBuilder(
stream: _todostream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasData) {
final data = snapshot.data?.docs.map((e) => e.data()).toList();
if (data == null) {
return Text("got null data");
}
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
final map = data[index] as Map?;
return ListTile(title: Text("${map?['text']}"));
});
}
return Text("NA");
},
),

displaying current logged user data

i need assistance on how i could possibly query a current logged in / signed in users data so i can display their specific data using the snippet of code below -
class AddStore extends StatelessWidget {
AddStore({Key? key}) : super(key: key);
final CollectionReference _user =
FirebaseFirestore.instance.collection("users");
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: _user.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: ((context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Column(
children: [
Text(documentSnapshot['fullName']),
],
);
}));
}
}));
}
}
You need to add the current user id i.e FirebaseAuth.instance.currentUser!.uid while querying the data from users collection. And this results in single document so you should avoid using ListView
Change the StreamBuilder to this
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
Map<String, dynamic> data =
snapshot.data!.data()! as Map<String, dynamic>;
return Text(data['fullName']);
},
)

convert this into streambuilder in flutter

I want to convert this function into Streambuilder, but somehow I could not figure out how I could do it. Any help would be greatly appreciated.
Future getReceiverChats() async {
var data = await FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.get();
setState(() {
_msgReceiverList =
List.from(data.docs.map((doc) => Message.fromMap(doc)));
});
}
Try this:
Stream<List<Message>> getReceiverChats(String id) {
return FirebaseFirestore.instance
.collection("message")
.doc(id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots()
.map((QuerySnapshot query) {
List<Message> dataList = [];
query.docs.forEach((doc) {
dataList
.add(Message.fromMap(doc));
});
return dataList;
});
}
Then:
StreamBuilder<List>(
stream: getReceiverChats(widget.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<Message>? dataList = snapshot.data;
if (dataList!.isEmpty) {
return Center(
child: Text('No results'),
);
}
return ListView.builder(
itemCount: dataList.length,
itemBuilder: (context, index) {
return MyWidget(dataList[index]);
});
}
if (snapshot.connectionState == ConnectionState.done) {
if (!snapshot.hasData) {
return Center(
child: Text('No results'),
);
}
}
return const Center(
child: CircularProgressIndicator(),
);
})
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text("Loading...");
default:
return ListView(
children: snapshot.data.docs.map((doc) {
return Message.fromMap(doc);
}).toList(),
);
}
},
),

Modify Future Builder inside Stream Builder to avoid widget flickering

I am using a FutureBuilder inside a StreamBuilder that updates the UI every time a document is added to the activity collection, to get some aditional data from Firestore. The problem is that the FutureBuilder returns a SizedBox widget while the ConnectionState is waiting causing the all the cards to dissapear for a second. I would like to avoid this flickering since it causes a bad ui experience for users.
Is there a way to query the required user data in the activity stream so it all returns at once that way I can remove the FutureBuilder?
If not ... what would be a solution for this?
activityStream() {
return FirebaseFirestore.instance
.collection('activity')
.orderBy('timestamp', descending: true)
.limit(55)
.snapshots();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
StreamBuilder<QuerySnapshot>(
stream: activityStream(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
final activityContent = snapshot.data?.docs
.map((data) => ActivityModel.fromFirestore(data))
.toList();
return Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: activityContent[i].players)
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
final users = snapshot.data!.docs.map((e) {
return UserModel.fromFirestore(e);
}).toList();
return MyWidget(
users: users,
);
},
);
},
),
);
}
},
);

snapshot.data.data() is null

class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
Future<DocumentSnapshot> getData() async {
return await FirebaseFirestore.instance
.collection('user')
.doc(FirebaseAuth.instance.currentUser.uid)
.get();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Profile'),
),
body: FutureBuilder(
future: getData(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.data() == null)
return Center(child: Text((snapshot.data.data()).toString()));
else
return ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data.data()['email']),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
} else
return CircularProgressIndicator();
},
),
);
}
}
This code is always showing 'null' . What is the reason for which snapshot.data.data() is null?
Im new to future builder and Im not understanding what could be the reason for this error.
just use snapshot.data==null or !snapshot.exists