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

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. :)

Related

How can I update note NoteScreen widget when I add an item in AddNote widget?

In set state I call _notes to fetch notes from firebase to be updated.
But when I add a note in the widget AddNote and press save button, and then go back to NoteScreen, it is not been updated automatically. I must tu swipe down to refresh, and then _notes get again called and the data is up to date.
And I also need feedback on my code, I am a beginner and an opinion about clean code practices will be of high value.
class NoteScreen extends StatefulWidget {
static const routeName = '/noteScreen';
#override
_NoteScreenState createState() => _NoteScreenState();
}
class _NoteScreenState extends State<NoteScreen> {
Widget currentPage;
String search;
bool isLoading = false;
Future<void> _notes() async {
try {
final authData = Provider.of<Auth>(context, listen: false);
await Provider.of<NoteList>(context, listen: false)
.fetchAndSetNotes(search, authData.userId);
print('Note Screen FetchAndSetNotes');
} catch (err) {
print(err.toString());
}
}
#override
void initState() {
_notes();
super.initState();
}
#override
Widget build(BuildContext context) {
final note = Provider.of<NoteList>(context);
print(note.items.length);
SystemChrome.setEnabledSystemUIOverlays([]);
return Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(alignment: Alignment.topCenter, children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.teal, Colors.purple])),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
Navigator.of(context).pushNamed(AddNote.routeName);
},
),
IconButton(
icon: Icon(Icons.logout),
onPressed: () {
Provider.of<Auth>(context, listen: false).logout();
})
],
title: Text(
'NoteApp',
style: TextStyle(fontSize: 20),
),
elevation: 0.0,
backgroundColor: Colors.transparent),
body: SingleChildScrollView(
child: Column(children: [
Align(
alignment: Alignment.topCenter,
child: Container(
height: MediaQuery.of(context).size.height * 0.10,
width: MediaQuery.of(context).size.width,
child: FloatingSearchBar(
borderRadius: BorderRadius.all(Radius.circular(20)),
hint: 'Search',
actions: [],
onQueryChanged: (query) {
setState(() {
try {
search = query;
} catch (err) {
print(err.toString());
}
_notes();
});
},
builder: (context, transition) {
return ClipRRect();
},
),
)),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height * 0.80,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
builder: (context, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? CircularProgressIndicator()
: RefreshIndicator(
onRefresh: () => _notes(),
child: ListView.builder(
padding: EdgeInsets.all(10),
itemCount: note.items.length,
itemBuilder: (context, i) => Column(
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
NoteDisplay(
noteItem: note.items[i],
),
),
);
},
child: note.items[i] != null
? Padding(
padding: EdgeInsets.all(5),
child: NoteItem(
note.items[i].content,
note.items[i].dateTime,
note.items[i].title,
note.items[i].id),
)
: Container(
child: Center(
child: Text(
'No notes Available'),
),
),
),
],
),
),
),
),
),
),
]),
),
)),
]),
);
}
}
**AddNote widget **
class AddNote extends StatefulWidget {
static const routeName = '/addNotes';
#override
_AddNoteState createState() => _AddNoteState();
}
class _AddNoteState extends State<AddNote> {
final _form = GlobalKey<FormState>();
TextEditingController titleControler = new TextEditingController();
var _newNotes = Notes(id: null, title: '', content: '', dateTime: '');
Future<void> _saveNote() async {
final isvalid = _form.currentState.validate();
if (!isvalid) {
return;
}
_form.currentState.save();
Navigator.of(context).pop();
try {
await Provider.of<NoteList>(context, listen: false).addNotes(_newNotes);
print('add_note');
} catch (err) {
print('add_note');
print(err.toString());
}
}
#override
Widget build(BuildContext context) {
final DateTime dateTime = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd – kk:mm').format(dateTime);
Size size = MediaQuery.of(context).size;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.teal, Colors.purple])),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Text('Add notes '),
actions: [IconButton(icon: Icon(Icons.save), onPressed: _saveNote)],
),
body: Center(
child: Form(
key: _form,
child: Container(
height: size.height * 0.7,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30))),
child: Card(
margin: EdgeInsets.all(30),
elevation: 20,
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter a Title';
}
return null;
},
controller: titleControler,
decoration: InputDecoration(
hintText: 'Title',
),
onSaved: (value) {
_newNotes = Notes(
title: value,
id: _newNotes.id,
content: _newNotes.content,
dateTime: formattedDate);
print(value.toString());
},
textInputAction: TextInputAction.next,
),
SizedBox(
height: 20,
),
TextFormField(
validator: (value) {
if (value.isEmpty) {
return "The content cannon't be empty";
}
return null;
},
onSaved: (value) {
_newNotes = Notes(
title: _newNotes.title,
content: value,
id: _newNotes.id,
dateTime: formattedDate);
print(value.toString());
},
maxLines: null,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(hintText: 'Note content'),
),
SizedBox(
height: 10,
),
Text('$formattedDate')
],
),
),
),
),
),
),
),
),
);
}
}
I believe what is want is a stream instead of a future. i don't know what your fetchAndSetNotes() function looks like but Instead of doing
FirebaseFirestore.instance.collection(collectionName).doc(id).get() you should do Firestore.instance.collection("collectionName").doc(id).snapshots() which will give you a stream. you use a StreamBuilder (considering you are using provider, you should use stream provider) instead of FutureBuilder.

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

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();

Error: The method 'round' was called on null. Receiver: null Tried calling: round(). Error thrown when building widget

In my program, I'm pulling values from cloud firestore as an initial value and want them to react on user input in case of the slider. However, the aforementioned error gets thrown. I haven't posted the entire class as it didn't let me. Just kept it to the essentials. Do let me know if you require further information. Thanks in advance!
Here's my code:
class _ListPlusScreenState extends State<ListPlusScreen> {
#override
double _progress;
String _name;
Widget _BuildListItem(BuildContext context, DocumentSnapshot doc) {
_progress = doc['Progress'];
final _name = doc['Name'].toString();
final _due = doc['Due'];
final _priority = doc['Priority'];
DocumentReference _listRef =
Firestore.instance.collection("List Items").document();
return Container(
padding: EdgeInsets.all(15),
width: 200,
height: 600,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Color(0xffacb3b8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
_name,
style: TextStyle(fontSize: 20),
),
Checkbox(value: false, onChanged: null)
],
),
Slider(
value: _progress,
min: 0.0,
max: 100.0,
divisions: 10,
label: _progress.round().toString(),
onChanged: (value) {
setState(
() {
_progress = value;
_listRef.updateData({'Progress': value});
},
);
},
),
],
),
);
}
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF6EC6CA),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20.0),
),
child: StreamBuilder(
stream: Firestore.instance.collection('List Items').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading..');
return ListView.builder(
itemExtent: 150,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_BuildListItem(context, snapshot.data.documents[index]),
);
}),
);
}
}
```
double _progress =0.0;
label: _progress?.round().toString()??"0.0",
Change two lines of code in your code

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