Flutter/Firebase - Merge 2 streams and utilise result in PageView Builder - firebase

I am trying to take two streams of data from firebase and merge them into one, then use in my PageView builder in my flutter app. I have managed to get my first stream working (default occasions) but I now need to add another. Here are the two streams:
Stream<QuerySnapshot> getDefaultOccasions(BuildContext context) async*{
yield* Firestore.instance.collection('datestoremember').document('default').collection('Dates_to_Remember').snapshots();
}
Stream<QuerySnapshot> getPersonalOccasions(BuildContext context) async*{
final uid = await Provider.of(context).auth.getCurrentUID();
yield* Firestore.instance.collection('datestoremember').document(uid).collection('Dates_to_Remember').snapshots();
}
I'm not sure the best way to merge the two streams together and then use the result in a Page View Builder:
child: StreamBuilder(
stream: getDefaultOccasions(context),
builder: (context, snapshot) {
if(!snapshot.hasData) return const Text("Loading...");
return new PageView.builder(
itemCount: snapshot.data.documents.length,
controller: PageController(viewportFraction: 0.5),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.5,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.data.documents[i]['Date'].toDate().day.toString()),
Text(DateFormat.MMMM()
.format(
formatter.parse(snapshot.data.documents[i]['Date'].toDate().toString()))
.toString()),
Padding(
padding: const EdgeInsets.only(
left: 8.0, right: 8.0),
child: FittedBox(
fit: BoxFit.contain,
child: Text(
snapshot.data.documents[i]['Title'],
overflow: TextOverflow.ellipsis,
),
),
)
],
),
),
);
},
);},
),
Here is all the code:
class AccountPage extends StatefulWidget {
#override
_AccountPageState createState() => _AccountPageState();
}
class _AccountPageState extends State<AccountPage> {
List<Category> _categories = [
Category('My History', Icons.history, MyHistory()),
Category('Dates to Remember', Icons.event_note, DatesToRemember()),
Category('Terms and Conditions', Icons.assignment, TermsandConditions()),
Category('Privacy Notice', Icons.security, PrivacyNotice()),
Category('Rate us', Icons.stars, RateUs()),
Category('Send us Feedback', Icons.feedback, GiveUsFeedback())
];
DateFormat formatter = DateFormat('dd-MM-yyyy');
int _index = 0;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
child: SizedBox(
height: 75, // card height
child: StreamBuilder(
stream: getDefaultOccasions(context),
builder: (context, snapshot) {
if(!snapshot.hasData) return const Text("Loading...");
return new PageView.builder(
itemCount: snapshot.data.documents.length,
controller: PageController(viewportFraction: 0.5),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.5,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(snapshot.data.documents[i]['Date'].toDate().day.toString()),
Text(DateFormat.MMMM()
.format(
formatter.parse(snapshot.data.documents[i]['Date'].toDate().toString()))
.toString()),
Padding(
padding: const EdgeInsets.only(
left: 8.0, right: 8.0),
child: FittedBox(
fit: BoxFit.contain,
child: Text(
snapshot.data.documents[i]['Title'],
overflow: TextOverflow.ellipsis,
),
),
)
],
),
),
);
},
);},
),
),
),
// SizedBox(height: 100.0,),
Container(
// Page Options
height: MediaQuery
.of(context)
.size
.height * 0.7,
child: ListView.builder(
itemCount: _categories.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
ListTile(
leading: Icon(
_categories[index].icon,
color: Colors.black,
),
title: Text(_categories[index].name),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () =>
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
_categories[index].route)),
),
Divider(), // <-- Divider
],
);
}),
),
],
),
);
}
}
Stream<QuerySnapshot> getDefaultOccasions(BuildContext context) async*{
yield* Firestore.instance.collection('datestoremember').document('default').collection('Dates_to_Remember').snapshots();
}
Stream<QuerySnapshot> getPersonalOccasions(BuildContext context) async*{
final uid = await Provider.of(context).auth.getCurrentUID();
yield* Firestore.instance.collection('datestoremember').document(uid).collection('Dates_to_Remember').snapshots();
}

You can Merge your two Streams like this:
StreamGroup.merge([getDefaultOccasions(context), getPersonalOccasions(context)]).asBroadcastStream();

Related

my value becomes null in streambuilder listview.builder when getting data from firebase

I see that the stream builder returns ${globals.statusKey} as null, but I don't know why, because I can print the value out well, and the FriendStatus code is also a widget that I called well in another part of the app(which was drawer).
I can see these kinds of problem occurring frequently in stream builder but cannot find any solution.
There is an error like below
'package:cloud_firestore/src/firestore.dart': Failed assertion: line 63 ps 7:1 '! collectionPath.contains('//'):a collection path must not contain "//"
And here is the code.
body: SafeArea(
child: Container(
padding: EdgeInsets.fromLTRB(10, 5, 0, 0),
// child: SingleChildScrollView(
// scrollDirection: Axis.vertical,
child: Column(children: [
tapableDate(),
const SizedBox(
height: 10,
),
Container(
height: 220,
width: 330,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
),
),
child: FriendStatus(),
),
Text(
"See \n statusKey: ${globals.statusKey} \n count: \n",
),
// Provider(create: (context) => TimerService(), child: EventList()),
// ),
]),
// ),
),
),
Code of FriendStatus.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:gw/globals.dart' as globals;
class FriendStatus extends StatefulWidget {
const FriendStatus({Key? key}) : super(key: key);
#override
State<FriendStatus> createState() => _FriendStatusState();
}
class _FriendStatusState extends State<FriendStatus> {
FirebaseFirestore firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
padding: EdgeInsets.fromLTRB(20, 10, 20, 0),
child: Column(
children: [
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('user/${globals.currentUid}/friends')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>>
snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final docs = snapshot.data!.docs;
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: docs.length,
itemBuilder: (context, index) {
return Container(
//padding:,
child: ListTile(
leading: Icon(
Icons.circle,
color: Colors.grey[850],
),
title: Text(docs[index]['name']),
));
},
);
},
)
],
)),
);
}
}

How to use a stack correctly?

IM trying to use a stack where I displaying a listview and a
This is my code
#override
Widget build(BuildContext context) {
final user = Provider.of<Userforid>(context);
_deviceHeigth = MediaQuery.of(context).size.height;
_deviceWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
title: Text(this.widget._receivername),
),
body: StreamBuilder<UserData>(
stream: DatbaseService(uid: user.uid).userData,
builder: (context, snapshot) {
return _conversationPageUI();
},
),
);
And then the _conversationPageUI(); is this here
Widget _conversationPageUI() {
return Builder(builder: (BuildContext _context) {
return Stack(
overflow: Overflow.visible,
children: <Widget>[
_messageLisView(),
Align(
alignment: Alignment.bottomCenter,
child: _messageField(_context),
),
],
);
});
}
And then this each widgets are here
Widget _messageLisView() {
final user = Provider.of<Userforid>(context);
return Container(
width: _deviceWidth,
child: StreamBuilder<Conversation>(
stream: DatbaseService.instance
.getConversation(this.widget._convertsationID),
builder: (BuildContext _context, _snapshot) {
if (_listViewController.hasClients) {
_listViewController.animateTo(
_listViewController.position.maxScrollExtent,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut);
}
var _conversationData = _snapshot.data;
if (_conversationData != null) {
if (_conversationData.messages != null) {
return ListView.builder(
controller: _listViewController,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
itemCount: _conversationData.messages.length,
itemBuilder: (BuildContext _context, int _index) {
var _message = _conversationData.messages[_index];
bool _isOwnMessage = _message.senderID == user.uid;
return _messageListViewChild(_isOwnMessage, _message);
},
);
} else {
return Align(
alignment: Alignment.center,
child: Text("Let's start a Conversation"),
);
}
} else {
return Center(
child: Container(
child: CircularProgressIndicator(),
));
}
},
),
);
}
Widget _messageField(BuildContext _context) {
return Container(
height: _deviceHeigth * 0.07,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(100),
border: Border.all(color: Colors.black, width: 2)),
margin: EdgeInsets.symmetric(
horizontal: _deviceWidth * 0.04, vertical: _deviceHeigth * 0.03),
child: Form(
key: _formKey,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
_messageTextField(),
_sendMessageButton(_context),
_videoMessageButton(),
],
),
),
);
}
The _messageField is just a Textformfield with a hintext and 2 icons . Heres how it looks
So as you can see I cannot see the last listview item correctly and thats my question how can I fix that ?
Padding(
padding: const EdgeInsets.only(bottom:MediaQuery.of(context).size.height * 0.1),
child: ListView.builder(
controller: _listViewController,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
itemCount: _conversationData.messages.length,
itemBuilder: (BuildContext _context, int _index) {
var _message = _conversationData.messages[_index];
bool _isOwnMessage = _message.senderID == user.uid;
return _messageListViewChild(_isOwnMessage, _message);
},
),
)
//OR
sizedBox(
height: MediaQuery.of(context).size.height * 0.80,
child: ListView.builder(
controller: _listViewController,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
itemCount: _conversationData.messages.length,
itemBuilder: (BuildContext _context, int _index) {
var _message = _conversationData.messages[_index];
bool _isOwnMessage = _message.senderID == user.uid;
return _messageListViewChild(_isOwnMessage, _message);
},
),
)
just add padding to the bottom of listview:
Widget _messageLisView() {
final user = Provider.of<Userforid>(context);
return Container(
padding: EdgeInsets.only(bottom: 100),
width: _deviceWidth,
I think passing
fit: StackFit.expand,
to your stack widget will fix your problem.

Flutter Strean builder runs initialay and show error The getter 'length' was called on null.but after gesturedetector clicks it runs correctly

1.This is the first page i need to run This code runs initially and give
The 'length' was called on null.
Receiver: null
Tried calling: length
Type error but after clicks gesture it runs perfectly i need run this code before gesture detector calls
I tried without gesture detector but it not works.
class ProductPage extends StatefulWidget {
#override
_ProductPageState createState() => _ProductPageState();
}
class _ProductPageState extends State<ProductPage> {
String id=' RN62p5KZAtk48mGL2n66';
int selectedIndex = 0;
String district;
bool loading = false;
_ProductPageState();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 30.0,),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: SizedBox(
height: 25,
child:StreamBuilder<List<CategoryModel>>(
stream: DatabaseService().getCategories(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => buildCategory(
index,
snapshot.data[index].categoryName,
snapshot.data[index].id
)
);
},
),
),
),
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('district').snapshots(),
builder: (context, snapshot) {
return Container(
padding: EdgeInsets.only(bottom: 16.0),
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
padding: EdgeInsets.fromLTRB(12.0, 10.0, 10.0, 10.0),
child: Text(
"District",
),
)),
new Expanded(
flex: 4,
child: DropdownButton(
value: district,
isDense: true,
onChanged: (valueSelectedByUser) {
setState(() {
this.district = valueSelectedByUser;
});
print(district);
},
hint: Text('Sort By District'),
items: snapshot.data.docs
.map((DocumentSnapshot document) {
return DropdownMenuItem<String>(
value: document.data()['Name'],
child: Text(document.data()['Name'] ),
);
}).toList(),
),
),
],
),
);
}),
district == null ? Expanded(
flex: 1,
child: StreamBuilder<List<SubCategoryModel>>(
stream:DatabaseService().getSubCategories(id),
builder: (context,snapshot){
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(),
);
}else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return PostContainer(
id: snapshot.data[index].listId,
likes: snapshot.data[index].likes,
views: snapshot.data[index].views,
imageUrl: snapshot.data[index].imageUrl,
longitude: snapshot.data[index].longitude,
latitude: snapshot.data[index].latitude,
providerName: snapshot.data[index].providerName,
providerTel: snapshot.data[index].providerTel,
units: snapshot.data[index].units,
description: snapshot.data[index].description,
unitPrice: snapshot.data[index].unitPrice,
name: snapshot.data[index].name,
date: snapshot.data[index].date,
providerId: snapshot.data[index].providerId,
categoryId: snapshot.data[index].categoryId,
providerImage: snapshot.data[index].providerImage,
district: snapshot.data[index].district,
address: snapshot.data[index].address,
);
}
);
}
},
),
):
Expanded(
flex: 1,
child: StreamBuilder<List<SubCategoryModel>>(
stream: DatabaseService().getSortedSubCategories(id, district),
builder: (context,snapshot){
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(),
);
}else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return PostContainer(
id: snapshot.data[index].listId,
likes: snapshot.data[index].likes,
views: snapshot.data[index].views,
imageUrl: snapshot.data[index].imageUrl,
longitude: snapshot.data[index].longitude,
latitude: snapshot.data[index].latitude,
providerName: snapshot.data[index].providerName,
providerTel: snapshot.data[index].providerTel,
units: snapshot.data[index].units,
description: snapshot.data[index].description,
unitPrice: snapshot.data[index].unitPrice,
name: snapshot.data[index].name,
date: snapshot.data[index].date,
providerId: snapshot.data[index].providerId,
categoryId: snapshot.data[index].categoryId,
providerImage: snapshot.data[index]
.providerImage,
district: snapshot.data[index].district,
address: snapshot.data[index].address,
);
}
);
}
},
),
),
],
)
);
}
Widget buildCategory(int index,String categoryName,String catId) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
id= catId;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
categoryName,
style: TextStyle(
fontWeight: FontWeight.bold,
color: selectedIndex == index ? Color(0xFF535353) : Color(0xFF535353),
),
),
Container(
margin: EdgeInsets.only(top: 20 / 4), //top padding 5
height: 2,
width: 30,
color: selectedIndex == index ? Colors.black : Colors.transparent,
)
],
),
),
);
}
Future<void> _shoeerror() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Thank you For Join with Us !'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Row(
children: [
Text('Select the Category to proceed '),
],
)
],
),
),
actions: <Widget>[
TextButton(
child: Text('OK'),
onPressed: () {
setState(() {
id=' RN62p5KZAtk48mGL2n66';
});
},
),
],
);
},
);
}
}
2.this is the database call function
Stream<List<SubCategoryModel>> getSubCategories(docId) {
return _db.collection('categories').doc(docId).collection('subCategories').snapshots().map((snapshot) =>
snapshot.docs.map((doc) => SubCategoryModel.fromJson(doc.data())).toList());
}
I suspect this is being executed on each build with a side effect... perhaps recreating the stream back to its initial state:
stream: DatabaseService().getCategories()
For the "Builder" widgets expecting a future or a stream, you need to create what they are watching as a value outside the build() method... perhaps in an initState, or my favorite, in a Riverpod provider.
Also, you don't seem to have any conditional based on the non-null value of snapshot.data, so it will blow up because of .length. This is expected behavior. :)

How to manage two ListView with data in firestore in the same screen in Flutter

I am building a Flutter app and I have a screen with 2 ListViews. The data source is a firestore database and it is the same for both list however one list presents only an image while the other list presents the image PLUS other information.
I have managed to found a solution to display both List however it does not seem the most effective because I believe I am downloading the same data 2 time. Do you have any suggestion on how to make it better???
See my code below
final _firestore = Firestore.instance;
class ItemPage extends StatefulWidget {
#override
_ItemPageState createState() => _ItemPageState();
}
class _ItemPageState extends State<ItemPage> {
bool showSpinner = false;
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
heroTag: 'itemppage',
transitionBetweenRoutes: false,
middle: Text(
appData.categoryName,
style: kSendButtonTextStyle,
),
),
child: Scaffold(
backgroundColor: kColorPrimary,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
children: <Widget>[
Expanded(
child: ItemList(),
),
Container(
height: 80.0,
child: ItemListBottomScroll(),
)
],
),
),
),
);
}
}
class ItemList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('books')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text(
'Loading...',
style: kSendButtonTextStyle,
);
default:
return new PageView(
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.all(10.0),
child: Stack(
children: <Widget>[
CachedNetworkImage(
imageUrl: document['url'],
placeholder: (context, url) =>
new CircularProgressIndicator(),
errorWidget: (context, url, error) =>
new Icon(Icons.error),
width: MediaQuery.of(context).size.width - 20,
height:
(MediaQuery.of(context).size.width - 20),
fit: BoxFit.cover),
Positioned(
bottom: 0.0,
right: 0.0,
child: IconButton(
color: kColorAccent.withOpacity(0.8),
iconSize: 50.0,
icon: Icon(Icons.add_circle),
onPressed: () {
print(document['name'] +
document.documentID +
' clicked');
},
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, bottom: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: Text(
document['name'],
style: kSendButtonTextStyle,
),
flex: 3,
),
Expanded(
child: Text(
appData.currency + document['price'],
textAlign: TextAlign.right,
style: kDescriptionTextStyle,
),
flex: 1,
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: Container(
child: Text(
document['description'],
style: kDescriptionTextStyle,
),
width: double.infinity,
),
)
],
),
);
}).toList(),
);
}
},
);
}
}
class ItemListBottomScroll extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('books')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return Stack(
children: <Widget>[
Container(
height: 80.0,
width: 90.0,
padding: EdgeInsets.only(left: 10.0),
child: GestureDetector(
onTap: () {
print(document['name'] +
document.documentID +
' bottom clicked');
},
child: new CachedNetworkImage(
imageUrl: document['url'],
placeholder: (context, url) =>
new CircularProgressIndicator(),
errorWidget: (context, url, error) =>
new Icon(Icons.error),
fit: BoxFit.cover),
),
),
],
);
}).toList(),
);
}
},
);
}
}
In order to read both info from the same Stream you need to expose it as a broadcast stream first:
final streamQuery = _firestore
.collection('books')
.snapshots()
.asBroadcastStream();
return StreamBuilder<QuerySnapshot>(
stream: streamQuery,
builder: ...

Stream builder from firestore to flutter

I am wondering how to get data from firestore to flutter app using the streambuilder. I created the necessary Boilerplate code I have the widget built and working and in the below code
headimageassetpath is nothing but a URL string which exists in the firestore.
#override
Widget build(BuildContext context) {
return Scaffold(
body:
new StreamBuilder(
stream: Firestore.instance.collection('Items').snapshots(),
builder: (_, AsyncSnapshot<QuerySnapshot> snapshot) {
var items = snapshot.data?.documents ?? [];
return new Lost_Card(
headImageAssetPath : snapshot.data.documents.map()(['url'],)
);
},
)
My firestore:
full code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class LostPage extends StatefulWidget {
#override
_LostPage createState() => new _LostPage();
}
class _LostPage extends State<LostPage> {
//temp vars
final String firebasetest = "Test";
//firestore vars
final DocumentReference documentReference =
Firestore.instance.document("Items/Rusty");
//CRUD operations
void _add() {
Map<String, String> data = <String, String>{
"name": firebasetest,
"desc": "Flutter Developer"
};
documentReference.setData(data).whenComplete(() {
print("Document Added");
}).catchError((e) => print(e));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:
new StreamBuilder(
stream: Firestore.instance.collection('Items').snapshots(),
builder: (_, AsyncSnapshot<QuerySnapshot> snapshot) {
var items = snapshot.data?.documents ?? [];
return new Lost_Card(
headImageAssetPath : snapshot.data.documents.map()(['url'],)
);
},
)
/*new Lost_Card(
headImageAssetPath: "https://i.imgur.com/FtaGNck.jpg" ,
title: "Mega Dish",
noro: "old",
)*/,
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: _add),
);
}
}
class Lost_Card extends StatelessWidget
{
//All the card variables
final String headImageAssetPath;
final IconData icon;
final Color iconBackgroundColor;
final String title;
final String noro;
final int price;
final ShapeBorder shape;
Lost_Card({
this.headImageAssetPath, //used
this.icon,
this.iconBackgroundColor,
this.title, //used
this.noro, //used
this.price,
});
#override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
children: <Widget>[
Card(
child: Column(
// mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height / 4,
width: MediaQuery.of(context).size.height / 2.5,
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
headImageAssetPath),
fit: BoxFit.cover),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: FractionalOffset.topLeft,
child: CircleAvatar(
backgroundColor: Colors.redAccent,
radius: 15.0,
child: Text(
noro,
textScaleFactor: 0.5,
),
),
),
),
Align(
alignment: FractionalOffset.topRight,
child: Container(
color: Colors.blueAccent,
height: 35.0,
width: 35.0,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.account_circle),
Text(
"1P",
textScaleFactor: 0.5,
),
],
),
),
),
),
],
),
),
Center(
child: Container(
padding: const EdgeInsets.all(8.0),
alignment: FractionalOffset.bottomCenter,
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w700,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
FlatButton(
child: Text(
"Add To Cart",
style: TextStyle(color: Colors.grey[500]),
),
onPressed: () => null,
),
Text(
"\$5",
style: TextStyle(color: Colors.grey[500]),
)
],
)
],
),
),
],
);
}
}
Actual App
Please shed some light on this. Tks.
This should work for one item
body: new StreamBuilder(
stream: Firestore.instance.collection("collection").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text(
'No Data...',
);
} else {
<DocumentSnapshot> items = snapshot.data.documents;
return new Lost_Card(
headImageAssetPath : items[0]["url"]
);
}
If you want to create list builder from many documents use it like this
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.documents[index];
return new Lost_Card(
headImageAssetPath : ds["url"];
);
Accessing documents using StreamBuilder in Flutter 2
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot doc = snapshot.data!.docs[index];
return Text(doc['name']);
});
} else {
return Text("No data");
}
},
)
As per new changes 2021 in Firebase FireStore you can retrieve data from collection using StreamBuilder as below
final _mFirestore = FirebaseFirestore.instance;
return StreamBuilder<QuerySnapshot>(
stream:
_mFirestore.collection(kFirebaseCollectionName).snapshots(),
builder: (context, snapshots) {
if (!snapshots.hasData) {
return Center(
child: Text('Data not available',),
);
}
final messages = snapshots.data.docs;
List<Text> textWidgets = [];
messages.forEach((element) {
final messageText = element['text'];
final messageSender = element['sender'];
final textWidget = Text('$messageText, $messageSender');
textWidgets.add(messageBubbleWidget);
});
},
);
Card buildItem(DocumentSnapshot doc) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'name: ${doc.data['name']}',
style: TextStyle(fontSize: 24),
),
Text(
'todo: ${doc.data['todo']}',
style: TextStyle(fontSize: 20),
),
Text(
'Age: ${doc.data['age']}',
style: TextStyle(fontSize: 10),
),
SizedBox(
height: 12,
),
],
)
],
),
),
); }
For other persons who will face the same problem, the card and stream builder will represent a solution. The Widget has the Card just before it declaration and has inside the body the next part:
body: ListView(
padding: EdgeInsets.all(8),
children: <Widget>[
Form(
key: _formKey,
child: buildTextFormField(),
),
StreamBuilder<QuerySnapshot>(
stream: db
.collection('CRUD')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return SizedBox();
}
},
)
],
),
Update for 2022, Flutter 2.10, cloud_firestore: ^3.1.11. You can retrieve data from collection using StreamBuilder
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
StreamBuilder<QuerySnapshot>(
builder: (context, snapshot) {
if (snapshot.hasData) {
final messages = snapshot.data!.docs;
List<Text> messageWidgets = [];
for (var element in messages) {
final messageText = element['text'];
final messageSender = element['sender'];
final messageWidget =
Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
return Column(
children: messageWidgets,
);
}
return const Text('Error');
},
stream:collectionStream),
StreamBuilder<List<UData>>(
stream: AdminData().getDrivers,
builder: (context, snapshot) {
return ListView(
children: snapshot.data.map((document) {
return hadCard(
widget: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
hadText(title: document.name),
hadText(title: document.phone),
hadText(title: document.Driver),
],
),
);
}).toList(),
);
}),

Resources