i have two collection,
users collection
like collection.
in my like i have the list of like with there id, i am now using stream to fetch the list like user but. so if the user change his profile image it cant be updated in the like collection, so what i did is after fetching the list, i pass the id to another stream to get upadate from Users collection. this alot of bioler plate code. i am looking for way to combine this stream together.
here is a sample code
StreamBuilder(
stream: firestore
.collection('Matches')
.doc(auth.currentUser.uid)
.collection('Match')
.orderBy('chatTimeStamp').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.data.docs.isEmpty) {
return Align(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RippleAnimation(
repeat: true,
color: Colors.white,
minRadius: 100,
ripplesCount: 6,
child: Material(
elevation: 8.0,
shape: CircleBorder(),
child: CircleAvatar(
backgroundColor: themedata.darkTheme
? Colors.black
: Colors.white,
child: Image.asset(
'assets/1.png',
),
radius: 80.0,
),
),
),
Flexible(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'No Message Yet!',
textAlign: TextAlign.center,
style: GoogleFonts.raleway(
textStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
),
],
),
);
}
return ListView.separated(
separatorBuilder: (_, index) => Padding(
padding:
EdgeInsets.only(left: 120.0, right: 40),
child: Divider(
color: Colors.white,
thickness: 1.0,
),
),
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final data =
snapshot.data.docs[index].data()['Match'];
//here is where it depend on the value
return StreamBuilder(
stream: firestore
.collection('users')
.doc(data['id'])
.snapshots(),
builder: (context, snapshotid) {
final active = snapshotid.data.data();
if (snapshotid.hasError) {
return Container();
}
if (snapshotid.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator());
}
if (snapshotid.connectionState ==
ConnectionState.none) {
return Container();
}
if (!snapshotid.hasData) {
return Container();
}
For combining two streams you can use combine2 method from rxdart library. this library has all of the stream class features + some extra functionality which is really useful. for more detail you can see the official documentation here.
note: for using this library methods and functionalities you should also define streams by this library classes.
for example:
Stream stream, may be PublishSubject stream.
Related
I am facing problem in using stream builder. Actually, I have a dropdown list and below it I have gridview to show product categories according to options chose in dropdown list. I have to use two stream builders, one for option(s) list in dropdown and other is for gridview. Dropdown is wroking fine but gridview is not changing data according to option choose in dropdown list.
Please help
My dropdown widget
Widget dropDownMenu({
required Size size,
BuildContext? context,}) {
return StatefulBuilder(builder: (context, setState) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('options').get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error while loading'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: SpinKitFadingCircle(
itemBuilder: (BuildContext context, int index) {
return DecoratedBox(
decoration: BoxDecoration(
color: index.isEven
? MyStyles.themeData().primaryColor
: Colors.green,
),
);
},
),
);
}
if (snapshot.hasData && snapshot.data!.docs.isEmpty) {
return Center(child: Text('No Data'));
}
return Container(
height: size.height * 0.100,
padding: EdgeInsets.all(20.0),
color: Colors.white,
child: DropdownButton<String>(
dropdownColor: Colors.white,
elevation: 1,
value: viewModel.dropDownValue!,
hint: Text('Choose one option'),
items:
snapshot.data!.docs.map<DropdownMenuItem<String>>((value) {
return DropdownMenuItem<String>(
value: value.get('options'),
child: Text(
value.get('options'),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: GoogleFonts.poppins(
color: MyStyles.themeData().primaryColor,
fontSize: size.height * 0.024,
fontWeight: FontWeight.w600,
),
),
);
}).toList(),
onChanged: (value) {
setState(() {
viewModel.dropDownValue = value;
});
}),
);
},
);
});}
My gridview Widget
Widget buildGridView({Size? size, BuildContext? context}) {
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('categories')
.where('options', isEqualTo: viewModel.dropDownValue)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error while loading'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: SpinKitFadingCircle(
itemBuilder: (BuildContext context, int index) {
return DecoratedBox(
decoration: BoxDecoration(
color: index.isEven
? MyStyles.themeData().primaryColor
: Colors.green,
),
);
},
),
);
}
if (snapshot.hasData && snapshot.data!.docs.isEmpty) {
return Center(child: Text('No Categories available'));
}
return GridView.builder(
padding: const EdgeInsets.only(bottom: 70),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 1,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
),
itemCount: snapshot.data!.size,
itemBuilder: (BuildContext ctx, index) {
return GestureDetector(
onTap: () {},
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 3.0),
height: size!.height * 0.200,
width: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Color(0x99C4C4C4),
blurRadius: 12,
offset: Offset(4, 4),
spreadRadius: 0,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: size.height * 0.120,
width: size.width * 0.120,
child: Image.network(
snapshot.data!.docs[index]['image'])),
SizedBox(height: size.height * 0.015),
Text(
snapshot.data!.docs[index]['name']!,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.poppins(
color: MyStyles.themeMode().textColor,
fontSize: size.height * 0.022,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
},
);
},
),
),
);}
I want to rebuild stream everytime I change options in dropdown. How can I achieve this?
I got a page in flutter than contains future builder, and the result of it displayed in list view builder. When I get small amount of data, flutter display the data without any problem. But when I get large amount of data, it display nothing.
What I get from this problem is when I get large amount of data, future builder hasn't finished get the data before the page is shown.
How can I make the page wait for the future finished to get the data before the page is shown?
Thanks
FutureBuilder<List<SalesTable>>(
future: SelectSalesTable.getSalesTable(widget.kodesales,
daterange1.format(widget.tgl1), daterange2.format(widget.tgl2)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return new Text('No Data..!!');
} else if (snapshot.connectionState == ConnectionState.waiting) {
return new Center(child: CircularProgressIndicator());
} else {
if (snapshot.hasData) {
List<SalesTable> data = snapshot.data;
return Container(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
child: Card(
shadowColor: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 10,
child: Padding(
padding:
const EdgeInsets.fromLTRB(5, 10, 5, 10),
child: ListTile(
title: Text(
data[index].name +
' | ' +
data[index].Category,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold)),
subtitle: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
'Date : ' +
dateformat3.format(
data[index]
.date1),
style: TextStyle(
fontWeight:
FontWeight.bold)),
)
],
),
],
),
onTap: () {},
),
),
),
),
],
);
},
itemCount: data.length,
),
],
));
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
}
return Center(child: CircularProgressIndicator());
},
)
I'm trying to display a list of employees in my app. Here's the code I got so far:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 20,top: 20),
child: Container(
height: 40,
width: 120,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.12),
borderRadius: BorderRadius.circular(8)),
child: Padding(
padding: const EdgeInsets.all(10),
child: Text("Employees",
style: TextStyle(
fontSize: 22,
color: Colors.black,
fontWeight: FontWeight.w600,
)),
),
),
),
StreamBuilder(
stream: Firestore.instance.collection("Employees").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: Text("Fetching data..."));
} else if(snapshot.connectionState == ConnectionState.done){
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data.documents[index]['name']));
},
);
} else {
return Text("hol up");
}
}),
],
);
When I run the app, it just shows 'Employees' and 'hol up' in a column. Why does the list of employees not show?
I solved the issue by simply adding shrinkWrap: true in my listview.
This should work. Also get the data from the indexed document.
ListTile(
title: Text(snapshot.data.documents[index].data['name']));
I want to display the data obtained from futureBuilder into the table, this is the code that I tried but when I run browse an error appears like this
FutureBuilder(
future: UserController.getActivity(_selectedUser),
builder: (context, snapshot) {
if (snapshot.hasData != null) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, position){
var item = snapshot.data.elementAt(position);
return Container(
child: Table(
children: List<TableRow>.generate(10, (i) {
return TableRow(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.5, color: Colors.grey))),
children: [
Container(
padding: EdgeInsets.all(18),
child: Text(
//data[i].author.toString(),
"${item["activity"]["project"]}",
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
),
Container(
padding: EdgeInsets.all(18),
child: Text(
//data[i].stars.toString(),
item["created_at"],
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
)
]);
})),
);
}
);
}
},
),
if I use listView in displaying data, it becomes ineffective because the data is a lot
Error is here at this line
itemCount: snapshot.data.length,
length is not found in the snapshot. You need to check the return type of future UserController what it's returning.
For being to verify your code that it's working fine you can set the default value
Just to verify that UI working fine
itemCount: 10
after that you need to check your return type.
I m using firestore for data and firestorage for files and images, so i m using StreamBuilder to load list as per firestore data and i want to download images as per firestore file name, but its not happening
I tried is, created a new method there I did code to get download url , but it is not working
child: StreamBuilder(
stream: Firestore.instance.collection('data').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(child: CircularProgressIndicator()));
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
children: <Widget>[
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
imageLoader(snapshot.data.documents[index].data['flie_ref'].toString()),
fit: BoxFit.fill,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
elevation: 0,
margin: EdgeInsets.all(10),
),
Padding(
padding: EdgeInsets.only(left: 6, right: 6),
child: Card(
elevation: 0,
child: ExpansionTile(
leading: CircleAvatar(
child: Text(snapshot
.data.documents[index].data['amount']
.toString()),
),
title: Text(
snapshot
.data.documents[index].data['desc']
.toString(),
overflow: TextOverflow.ellipsis,
),
children: <Widget>[
Text(snapshot
.data.documents[index].data['desc']
.toString()),
SizedBox(
height: 10,
),
],
),
),
),
],
),
);
});
},
),
Future imageLoader(String string) async {
var url =
await FirebaseStorage.instance.ref().child(string).getDownloadURL();
setState(() {
return url;
});
}
I expect output load respective image but the output is Error: The argument type 'Future' can't be assigned to the parameter type 'Widget'