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
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.
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();
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
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: ...
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(),
);
}),