Collection per Users FireStore Flutter App - firebase

Im trying to create a collection of users in my flutter application.
The application has a stock collection with items which are documents and those documents, like plates or bags, has 2 fields
I create a root users collection and that collection documents which identified the used with their user ID, one document for each user ID (the one you get from FirebaseAuth).
Ive been told that I have to set the access rules in Firebase to allow to each user to have their own stock collection.
Github https://github.com/juancarlosjr97/flutter_ims
Each user should have their own collection
My database the own it works looks like this:
and the other collection with a collection of users looks like this:
This is my code where I get access to the stock collection
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class StockWidget extends StatefulWidget {
#override
StockWidgetApp createState() => new StockWidgetApp();
}
class StockWidgetApp extends State<StockWidget> {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('stock').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 20.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.item),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
child: ListTile(
title: Text(record.item),
trailing: Text(record.instock.toString()),
onTap: () =>
record.reference.updateData({'instock': record.instock + 1}),
onLongPress: () =>
record.reference.updateData({'instock': record.instock - 1}),
),
),
);
}
}
class Record {
final String item;
final int instock;
final DocumentReference reference;
#override
String toString() => "Record<$item:$instock>";
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['item'] != null),
assert(map['instock'] != null),
item = map['item'],
instock = map['instock'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
}

Use this collection for stock :
final userId = [get the user id from auth]
final col = Firestore.instance.collection("users").document(userId).collection("stock");
//add stock
col.add(yourstock);

Related

How to sort a list of users through cloud firestore that contain a uid with flutter?

I am trying to make a leaderboard system for a game that I am coming up within flutter and I can't seem to find a way to sort the different users depending on their high score due to the fact that high score data is stored inside the document which is the uid?
Right now I have it set up to just display the different users in the order at which they sign in at. Thanks for all the help in advance!
// My home page
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
final HighscoreData highscoreData;
Home({Key key, this.highscoreData}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamProvider<List<HighscoreData>>.value(
value: DatabaseService().brews,
child: Scaffold(
backgroundColor: Colors.brown[50],
body: HighscoreList(),
),
);
}
}
// List of different players highscores
class HighscoreList extends StatefulWidget {
#override
_HighscoreListState createState() => _HighscoreListState();
}
class _HighscoreListState extends State<HighscoreList> {
#override
Widget build(BuildContext context) {
final differentHighScores = Provider.of<List<HighscoreData>>(context) ?? [];
return ListView.builder(
itemCount: differentHighScores.length,
itemBuilder: (BuildContext context, int index){
return PlayerHighscoreTile(highscoreData: differentHighScores[index]);
},
);
}
}
// The template tile for each different highscore
class PlayerHighscoreTile extends StatelessWidget {
final HighscoreData highscoreData;
PlayerHighscoreTile({ this.highscoreData });
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 8.0),
child: Card(
margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
child: ListTile(
leading: CircleAvatar(
radius: 25.0,
backgroundColor: Colors.brown,
),
title: Text(highscoreData.name),
trailing: Text(highscoreData.score),
),
),
);
}
}
Here is my Database class if it helps at all
class DatabaseService {
final String uid;
DatabaseService({ this.uid });
// Collection reference
final CollectionReference<Map<String, dynamic>> brewCollection = FirebaseFirestore.instance.collection('brews');
Future updateUserData( String name, score) async {
return await brewCollection.doc(uid).set({
'score' : score,
'name' : name,
});
}
// Brew list from snapshot
List<HighscoreData> _brewListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc){
return HighscoreData(
name: doc.get('name') ?? '',
score: doc.get('score') ?? '0'
);
}).toList();
}
// Get brews stream
Stream<List<HighscoreData>> get brews {
return brewCollection.snapshots().map(_brewListFromSnapshot);
}
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
name: snapshot.get('name'),
score: snapshot.get('score'),
);
}
// Get user document
Stream<UserData> get userData {
return brewCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}
I just found out how to do this by adding a sortBy() function as it grabs the different data from firebase.
Stream<List<HighscoreData>> get brews {
return brewCollection.orderBy('').snapshots().map(_brewListFromSnapshot);
}

Flutter Firebase RealTime Databasee not ordering properly with OrderByChild() [duplicate]

This question already has an answer here:
Flutter: Firebase Real-Time database orderByChild has no impact on query result
(1 answer)
Closed 2 years ago.
I'm creating a simple application with Firebase Realtime database where a user inputs a text and it gets added to a list of chats.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _firebaseRef = FirebaseDatabase().reference().child('chats');
TextEditingController _txtCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
var comments = _firebaseRef.orderByChild('time').limitToLast(10);
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
children: <Widget>[
Container(
child: Row(children: <Widget>[
Expanded(child: TextField(controller: _txtCtrl)),
SizedBox(
width: 80,
child: OutlineButton(
child: Text("Add"),
onPressed: () {
sendMessage();
}))
])),
StreamBuilder(
stream: comments.onValue,
builder: (context, snap) {
if (snap.hasData &&
!snap.hasError &&
snap.data.snapshot.value != null) {
Map data = snap.data.snapshot.value;
List item = [];
data.forEach(
(index, data) => item.add({"key": index, ...data}));
return Expanded(
child: ListView.builder(
itemCount: item.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(item[index]['message']),
);
},
),
);
} else
return Center(child: Text("No data"));
},
),
],
),
),
),
);
}
sendMessage() {
_firebaseRef.push().set({
"message": _txtCtrl.text,
'time': DateTime.now().millisecondsSinceEpoch
});
}
}
It stores and retrieves data perfectly. But when I try adding data, the new items are placed at random points in the list.
For example, in the image below, the last item I placed into the list was 'Nine'. But it was put in the center of the list:
I've tried sorting the list by timestamps, but it did nothing.
What could be causing this issue? And how can I fix it?
When you call snap.data.snapshot.value; the data in the snapshot (which is ordered) is converted to a Map<String, Object> which isn't ordered. To maintain the order, you'll want to listen to onChild... instead.
Note that FlutterFire has a convenient firebase_list library that handles most of the heavy lifting of onChild... for you.
Also see:
Flutter Firebase Database wrong timestamp order
Flutter sort Firebase snapshot by timestamp
Flutter: Firebase Real-Time database orderByChild has no impact on query result
This might work:
use a Query
Query comments = _firebaseRef.orderByChild('time').limitToLast(10);

Flutter: Items in StreamBuilder(Using firebase realtime database) are sorted randomly [duplicate]

This question already has an answer here:
Flutter: Firebase Real-Time database orderByChild has no impact on query result
(1 answer)
Closed 2 years ago.
I'm creating a simple application with Firebase Realtime database where a user inputs a text and it gets added to a list of chats.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _firebaseRef = FirebaseDatabase().reference().child('chats');
TextEditingController _txtCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
var comments = _firebaseRef.orderByChild('time').limitToLast(10);
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
children: <Widget>[
Container(
child: Row(children: <Widget>[
Expanded(child: TextField(controller: _txtCtrl)),
SizedBox(
width: 80,
child: OutlineButton(
child: Text("Add"),
onPressed: () {
sendMessage();
}))
])),
StreamBuilder(
stream: comments.onValue,
builder: (context, snap) {
if (snap.hasData &&
!snap.hasError &&
snap.data.snapshot.value != null) {
Map data = snap.data.snapshot.value;
List item = [];
data.forEach(
(index, data) => item.add({"key": index, ...data}));
return Expanded(
child: ListView.builder(
itemCount: item.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(item[index]['message']),
);
},
),
);
} else
return Center(child: Text("No data"));
},
),
],
),
),
),
);
}
sendMessage() {
_firebaseRef.push().set({
"message": _txtCtrl.text,
'time': DateTime.now().millisecondsSinceEpoch
});
}
}
It stores and retrieves data perfectly. But when I try adding data, the new items are placed at random points in the list.
For example, in the image below, the last item I placed into the list was 'Nine'. But it was put in the center of the list:
I've tried sorting the list by timestamps, but it did nothing.
What could be causing this issue? And how can I fix it?
When you call snap.data.snapshot.value; the data in the snapshot (which is ordered) is converted to a Map<String, Object> which isn't ordered. To maintain the order, you'll want to listen to onChild... instead.
Note that FlutterFire has a convenient firebase_list library that handles most of the heavy lifting of onChild... for you.
Also see:
Flutter Firebase Database wrong timestamp order
Flutter sort Firebase snapshot by timestamp
Flutter: Firebase Real-Time database orderByChild has no impact on query result
This might work:
use a Query
Query comments = _firebaseRef.orderByChild('time').limitToLast(10);

Getter uid being called on null in flutter fire

I am trying to concatenate two UID's in order to create a chatroom. One uid is being read from firebase while the other is read from the FirebaseAuth.instance.
The clientUID is being assigned as it should, as I am passing it to another page on a Text widget. However the chatroom is not being created in the firestore tree so I assume this should be because of the instructor uid.
Maybe I am not calling the FirebaseAuth.instance as it should?
Code:
class ClientiPage extends StatefulWidget {
static const String id = 'CLIENTI';
#override
_ClientiPageState createState() => _ClientiPageState();
}
class _ClientiPageState extends State<ClientiPage> {
String chatRoomID;
String clientUID;
Firestore db = Firestore.instance;
String instructor;
void getInstructorId() async {
instructor = (await FirebaseAuth.instance.currentUser()).uid;
}
void saveChatRoom() {
getInstructorId();
DocumentReference chatroomIdRef = db.collection('instructori').document(instructor).collection("chatrooms").document(chatRoomID);
if (chatroomIdRef == null) {
db.collection('instructori').document(instructor).collection("chatrooms").document(chatRoomID);
}
}
void createChatRoom() {
getInstructorId();
chatRoomID = clientUID + instructor;
if(chatRoomID != null) {
saveChatRoom();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatPage(
chatRoomID: chatRoomID,
clientUID: clientUID,
),
),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: db.collection('clienti').snapshots(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
clientUID = snapshot.data.documents[index]["UID"];
return Column(
children: <Widget>[
Divider(
height: 10.0,
),
new ListTile(
onTap: createChatRoom,
title: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(
snapshot.data.documents[index]["numar_telefon"],
style: new TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
),
],
);
},
);
}
},
),
);
}
}
Error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
instructor is a instance variable in the class ClientiPage, thats why you can access it using the property widget. But it seems you are not initializing it correctly.
The uid will retrieve the currently logged in user id, you dont have to pass it inside a constructor or from a different screen, therefore you can do the following:
void saveChatRoom() async {
String userId = (await FirebaseAuth.instance.currentUser()).uid;
DocumentReference chatroomIdRef = db.collection('instructori').document(userId).collection("chatrooms").document(chatRoomID);
if (chatroomIdRef == null) {
db.collection('instructori').document(userId).collection("chatrooms").document(chatRoomID);
}
}
As long as the user is logged in, you can retrieve the uid using the following code (await FirebaseAuth.instance.currentUser()).uid. There is no need to pass it from screen to screen.
https://pub.dev/packages/firebase_auth

Save Bookmark Article in Firebase Flutter

In Flutter app I want to fetch data list
I want to save bookmark any article from article list in Fire store data base but when bookmark button tapped the same article save in the database every time. I want that article should save in database for the first time
Does anyone lead me to the correct way? Any help is highly appreciated!
My code
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp( MyHomePage());
class MyHomePage extends StatefulWidget {
#override
_MyHomePage createState() => _MyHomePage();
}
class _MyHomePage extends State<MyHomePage> {
String title;
String subtitle;
int id;
Firestore firestore = Firestore.instance;
DocumentSnapshot document;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('jdj'),
),
body: Container(
child: ListView(
children: <Widget>[
stremBuilder(),
Container(
height: 310,
color: Colors.amber,
)
],
),
));
}
Widget stremBuilder() {
return Container(
height: 200,
child: StreamBuilder(
stream: Firestore.instance.collection("User").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(
child: Text("Loding"),
);
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return listItem(context, snapshot.data.documents[index]);
},
);
},
),
);
}
Widget listItem(BuildContext context, DocumentSnapshot document) {
return ListTile(
title: Text(document["title"]),
subtitle: Text(document["subtitle"]),
trailing: GestureDetector(
child: Icon(Icons.bookmark),
onTap: () {
setState(() {
saveData(id, document);
});
}),
);
}
Map<String, dynamic> savedata = {};
saveData(int id, DocumentSnapshot document) {
Map<String, dynamic> savedata = {
"id": id,
"saveTitle": document["title"],
'saveSubtitle': document["subtitle"]
};
Firestore.instance.collection("savedata").add(savedata);
}
}
It looks like on the right track. Checking the code, the tapped List item should be saved. If what you're looking for is to save the "bookmark" only once, and clicking on it again should remove the saved bookmark. Then you can delete the document upon pressing again.
await FirebaseFirestore.instance.collection('savedata').doc(docId).delete();
Make sure to keep track of the id of the document that you'd like to delete to be used as reference.

Resources