Is there any problem in this code while implementing ListView.builder() - firebase

Why ListView.builder only showing 2 data, I have so many documents in cloud_firestore and I want to retrieve those data and show it in the ListView but exactly what is happening is it doesn't matter how many documents are there it is returning all the documents but when I am using ListView.builder to show that data using widgets it is showing only 2 Widget.
Future<List<DocumentSnapshot>> getData() async {
var firestore = Firestore.instance;
QuerySnapshot qn =
await firestore.collection("LiveProducts").getDocuments();
return qn.documents;
}
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: getData(),
builder: (_, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Padding(
padding: const EdgeInsets.only(
top: 295,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Center(
child: SpinKitCircle(
color: Color.fromRGBO(91, 74, 127,10),
size: 50.0,
),
),
],
),
);
} else {
return ListView.builder(
// title: Text(snapshot.data[index].data["ProductName"]),
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
// if (snapshot.data[index].data["live"] == true) {
print(snapshot.data.length);
return Container(
margin: EdgeInsets.all(15),
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
child: Center(child: Text('$index'),),
);
// }
},
);
}
}));
}
One horrible thing is hapenning i am using Text Widget instead of Container Widget it is showing perfectly all the documents. like this
return Center(child:Text(snapshot.data[index].data["ProductName"])),

Try the following:
else if(snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
// title: Text(snapshot.data[index].data["ProductName"]),
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
// if (snapshot.data[index].data["live"] == true) {
print(snapshot.data.length);
return Container(
margin: EdgeInsets.all(15),
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
child: Center(child: Text('$index'),),
);
// }
},
);
}
done → const ConnectionState
Connected to a terminated asynchronous computation.

Related

Get URl from cloud firestore and use Gesture Detector to redirect to the link?

In my project I am making an flutter app which fetch images from cloud firestore.
Right now i also want to call URL from cloud firestore so that whenever someone Tap on a particular image they redirect to specific link provided for images.
I have a collection name most and within collection i have 7 documents and within documents i have fields Image and its unique url
And i have this code for fetching images which is working absolutely fine:
FutureBuilder(
future: getMost(),
builder:(BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Lottie.asset('animations/loading.json');
} else {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return GestureDetector( onTap: null,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
height: 150.0,
width: 130.0,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage((snapshot.data[index].data()['image']),),fit: BoxFit.cover),
borderRadius:
BorderRadius.circular(10),
),
)
),
),
);
}
);
}
}
)
Use the flutter package url_launcher if you want to launch any kind of urls.
link : https://pub.dev/packages/url_launcher/
In pubsec.yaml
dependencies:
url_launcher: ^6.0.18
Actual code:
openUrl(url) async {
if (await canLaunch(url)){
await launch(url);
}
else {
// error
}
}
FutureBuilder(
future: getMost(),
builder:(BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Lottie.asset('animations/loading.json');
} else {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
return GestureDetector( onTap: () => openUrl(snapshot.data[index].data()['url']),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: SizedBox(
height: 150.0,
width: 130.0,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage((snapshot.data[index].data()['image']),),fit: BoxFit.cover),
borderRadius:
BorderRadius.circular(10),
),
)
),
),
);
}
);
}
}
)

How can I update Listview in Streambuilder in flutter

I have a streambuidler widget that contains a listview to display whom the current user has recently chatted with. When the current user receives a message that message should be pushed to the top of the listview, however that message is always display at the bottom of the list view, it's only display on the top if I refresh my screen.
NoSearchResultScreen() {
final Orientation orientation = MediaQuery.of(context).orientation;
print("hasAlreadyChatWithSomeone: $hasAlreadyChatWithSomeone");
return hasAlreadyChatWithSomeone
? StreamBuilder<QuerySnapshot>(
stream: (FirebaseFirestore.instance
.collection("user")
.where("id", isEqualTo: currentUserId)
.snapshots()),
builder: (context, snapshot) {
List<ProfileChatWith> chatWithList = [];
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: circularProgress(),
);
}
if (snapshot.hasData) {
final chatWithSnapshot = snapshot.data?.docs.first['chatWith'];
//print("chatWithSnapshot: $chatWithSnapshot");
for (var userChatWith in chatWithSnapshot) {
final user = ProfileChatWith(
userId: userChatWith,
currentUserId: currentUserId,
);
chatWithList.add(user);
//print("I have chatted with: $userChatWith");
}
return Container(
width: MediaQuery.of(context).size.width,
child: ListView(
//shrinkWrap: true,
children: chatWithList,
),
);
} else {
return Center(
child: circularProgress(),
);
}
},
)
: Container(
child: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
Icon(
Icons.group,
color: Colors.greenAccent,
size: 200.0,
),
Text(
"Search Users",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.greenAccent,
fontSize: 50.0,
fontWeight: FontWeight.bold),
)
],
),
),
);
}
Try reverse: true,
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView(
reverse: true,
children: chatWithList,
),
);
Use Listview.builder for performance optimization
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView.builder(
reverse: true,
itemBuilder: (BuildContext context, int index) => chatWithList[index],
),
);
The solution which worked is as below, #Leo Tran own words
I found a way to solve my question is that I will rebuild my widget whenever the data got updated.

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

Cant get StreamBuilder to display data from cloud firestore

I know I have a connection to the database and no errors are appearing so I'm pretty confused. The title and code should summarize the problem fairly well. Think I'm missing something?
here is the main code that should be displaying cards with titles from firebase
mainList() {
StreamBuilder(
stream: Firestore.instance.collection('Events').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading');
} else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot userPost = snapshot.data.documents[index];
return Container(
width: MediaQuery.of(context).size.width,
height: 350.0,
child: Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Material(
elevation: 14.0,
shadowColor: Color(0x802196F3),
child: Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: 200.0,
child: Text(
'${userPost['title']}',
))
],
),
),
))),
);
},
);
}
});
}
and here is where the function is called:
lass MyAppmain extends State<MyApp> {
#override
Widget build(BuildContext context) {
var listView = ListView.builder(
itemCount: local.length,
itemBuilder: (BuildContext cnxt, int index) {
return new Text(local[index]);
});
return MaterialApp(
home: PageView(
controller: controller,
children: <Widget>[
//home page---------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Events',
),
elevation: 20,
),
//main list view for the cards
//think I use streambuilder for this so google before starting
body: mainList(),//RIGHT HERE
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context, NewEventTransition());
},
mini: true,
),
),
//Profile Page-------------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Profile',
),
elevation: 20,
),
),
],
));
}
}
Want a listview of cards holding the titles from firebase (will soon be more than titles but want to get this working first)
This is a common problem.
return ListView.builder(
itemCount: snapshot.data.documents.length, // this line is the culprit!
itemBuilder: (context, index) {
print(snapshot.data.documents.length); // it will print null
.......
}
See, It takes some time to fetch data from firebase. When ListView.builder is called the value of snapshot.data.documents.length is actually null. Tho after few seconds it gets data but till then ListView had built the UI and that's why it's blank. To check the value, you can add a Print statement like shown above.
Now there are few ways to solve this problem:
Make an int variable say totalLength, make a function say setTotalLength which makes a call to Firebase/Firestore database and use setState to assign this value to totalLength and then change that code to this:
itemCount: totalLength,
You should Call setTotalLength in your initState method.
Or, you can change your code to this, But I'm NOT 100% sure that this will work:
itemCount: snapshot.data.documents.length ?? 0 // returns 0 if the value is null, may reload UI again when data comes

Flutter: Do FutureBuilder Loads everything at the same time?

My app has a book summaries Feature, Now since I don't have money to host an API, I am using Firebase/Firestore database where I add summaries manually and then retrieve data from firebase to App.
I am using FutureBuilder for it.
Now say I have 10 summaries will FutureBuilder first load all 10 of them and then display data on screen(which is a ListView.builder and can show only 2 summaries without scrolling) or it will load only the data which need to be painted on the screen just like simple ListView.builder.
// code to retrieve data:
Future<QuerySnapshot> getFeedsfromFb() async {
var data = await Firestore.instance
.collection('feedItem')
.orderBy('feedId', descending: true).getDocuments();
return data;
}
// building widget:
Widget feed() {
return Container(
width: deviceWidth,
height: deviceHeight / 3,
child: FutureBuilder(
future: getFeedsfromFb(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data.documents.length > 10
? 10
: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: deviceWidth / 2.5,
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FeedIntro(
snapshot.data.documents[index]['feedId'])));
},
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
// width: 150,
height: 150,
foregroundDecoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
snapshot.data.documents[index]['feedImage'],
),
fit: BoxFit.fill)),
),
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(snapshot.data.documents[index]['title']),
)),
],
)),
),
);
},
);
} else if (snapshot.hasError) {
return Center(child: Text('Sorry Something went wrong!'));
} else {
return Center(
child: SizedBox(
child: CircularProgressIndicator(),
width: 50,
height: 50,
),
);
}
}),
);
}
Your FutureBuilder will load all items at the same, but only needed data by ListView.builder will be painted on the screen.

Resources