furkansarihan / firestore_collection Goto Github PK
View Code? Open in Web Editor NEWFirestore extension with cache-first pagination and stream builder support.
License: MIT License
Firestore extension with cache-first pagination and stream builder support.
License: MIT License
The document changes are not being reflected in real time, or am i missing something?
in case of live data enable and multi query feature enable the Listener will start for query instead of queryList
should we loop on queryList and create multiple Listener ? Like :
if (queryList?.isEmpty ?? true) {
collectionListener(query);
return;
}
for (var q in queryList) {
collectionListener(q);
}
Thanks
Add Example for better understanding.
I'm using firestore_collection on some views with queryOrder based on timestamp and it's work as expected
Now I'm trying to extend this package to other view, this new view contain the posts order by likeCount ( most liked posts should be on the top of List ), but this is not work as expected, I have try to override displayCompare and compare post timestamp if likeCount field is the same for 2 documents
FirestoreCollection fireCollection = FirestoreCollection(
collection: FirebaseFirestore.instance.collection('posts'),
initializeOnStart: true,
// first page will fetched immediately
offset: 15,
// page size
serverOnly: true,
// cache first
live: true,
// notifies to newest documents
query: FirebaseFirestore.instance.collection('posts'),
queryOrder:
QueryOrder(orderField: 'likeCount', displayCompare: comparePosts),
);
How to reproduce :
Can you please check why new documents are not added
NOTE : I'm using the last version 0.0.4
main file to reproduce :
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firestore_collection/firestore_collection.dart';
import 'package:flutter/cupertino.dart';
import "package:flutter/material.dart";
import 'package:flutter_icons/flutter_icons.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
title: 'My Flutter App',
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomeState();
}
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
final List<Widget> _children = [HomePage(), SearchPage(), ProfilePage()];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Profile'),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
class HomePage extends StatelessWidget {
HomePage();
@override
Widget build(BuildContext context) {
FirestoreCollection fireCollection = FirestoreCollection(
collection: FirebaseFirestore.instance.collection('posts'),
initializeOnStart: true,
// first page will fetched immediately
offset: 15,
// page size
serverOnly: true,
// cache first
live: true,
// notifies to newest documents
query: FirebaseFirestore.instance.collection('posts'),
queryOrder:
QueryOrder(orderField: 'likeCount', displayCompare: comparePosts),
);
int count = 0;
return Scaffold(
backgroundColor: Colors.white,
// No appbar provided to the Scaffold, only a body with a
// CustomScrollView.
body: CustomScrollView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
slivers: <Widget>[
// Add the app bar to the CustomScrollView.
SliverAppBar(
actions: <Widget>[
IconButton(
iconSize: 30.0,
icon: const Icon(CupertinoIcons.plus),
onPressed: () async {
count++;
await FirebaseFirestore.instance.collection('posts').add({
'fullName': 'user' + count.toString(),
'likeCount': 0,
'userPhotoUrl':
'https://static.thenounproject.com/png/15724-200.png',
'timestamp': Timestamp.now()
});
},
),
],
// Provide a standard title.
expandedHeight: 100.0,
floating: true,
),
StreamBuilder(
stream: fireCollection.stream,
builder: (BuildContext context,
AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
leading: InkWell(
onTap: () => {print('tap...')},
child: Ink(
height: 50,
width: 50,
child: CachedNetworkImage(
imageUrl:
snapshot.data[index].data()['userPhotoUrl'],
imageBuilder: (context, imageProvider) =>
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: imageProvider, fit: BoxFit.cover),
),
),
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(
FontAwesome5.user,
color: Colors.black12,
),
),
),
),
title: Text(snapshot.data[index].data()['fullName']));
},
childCount: snapshot.hasData ? snapshot.data.length : 0,
),
);
},
),
],
),
);
}
int comparePosts(DocumentSnapshot a, DocumentSnapshot b) {
dynamic fieldA = a?.data()["likeCount"];
dynamic fieldB = b?.data()["likeCount"];
if (fieldA == null) {
return 1;
}
if (fieldB == null) {
return -1;
}
int res = fieldB.compareTo(fieldA);
print('compare likes res : ' + res.toString());
if (res == 0) {
dynamic fieldA = a?.data()["timestamp"];
dynamic fieldB = b?.data()["timestamp"];
if (fieldA == null) {
return 1;
}
if (fieldB == null) {
return -1;
}
int res = fieldB.compareTo(fieldA);
print('compare timestamp res : ' + res.toString());
return res;
} else {
// Descending compare
return res;
}
}
}
class SearchPage extends StatelessWidget {
SearchPage();
@override
Widget build(BuildContext context) {
int count = 0;
return Scaffold(
backgroundColor: Colors.white,
// No appbar provided to the Scaffold, only a body with a
// CustomScrollView.
body: Container(),
);
}
}
class ProfilePage extends StatelessWidget {
ProfilePage();
@override
Widget build(BuildContext context) {
int count = 0;
return Scaffold(
backgroundColor: Colors.white,
// No appbar provided to the Scaffold, only a body with a
// CustomScrollView.
body: Container(),
);
}
}
This seems like a great package! Is there any chance firestore_collection will be updated to support the latest big update of Cloud Firestore 1.X.X (which at the moment is 1.0.4)? As you may know, this update of Cloud Firestore was substantial and so to use this package, one must downgrade and use the previous nomenclature and design patterns which will break much functionality in other parts of people's projects for the future. And related, is there a possibility that this package will be migrated to null safety? Thank you for this wonderful addition to the Flutter ecosystem and please let me know if these upgrades/migrations will occur anytime soon as I really would like to incorporate this into a project!
do you know why Ascending order is for TODO ? any reason ?
class QueryOrder {
QueryOrder({
required this.orderField,
this.lastValue,
// TODO: Ascending query support
// this.descending = true,
this.displayCompare,
});
final String orderField;
final dynamic lastValue;
final bool descending = true;
final int Function(DocumentSnapshot, DocumentSnapshot)? displayCompare;
}
Hi,
Thanks a lot for making this package, it's awesome. It is working as expected for my Firestore collection StreamBuilder in terms of pagination and adding new docs. However, when I remove a document, the stream doesn't trigger and don't update the StreamBuilder. I have two pages: one is the list of docs where I am using the StreamBuilder, and the second page is the doc detail screen where I can remove it, by using a button. Any idea how can I trigger the StreamBuilder update on docs removal? I also checked it with just a traditional Stream QuerySnapshot, and it is working as expected, but cannot get it working with the FirestoreCollection package. Appreciate your help on this. Thanks.
Hi,
after logout and when trying to logIn I got this error : Flutter : Bad state: Stream has already been listened to
calling dispose before logout and using BroadcastStream don't fix issue persist
@furkansarihan Fix is to use BehaviorSubject class from RxDart library
reference : https://stackoverflow.com/questions/53841750/flutter-stream-has-already-been-listened-to/55893532#55893532
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.