Firestore security rules Flutter app denied permissions - flutter

I'm currently attempting to connect my Firebase backend to my Flutter app. Upon creating a simple get() request to Firestore I keep coming across the same error every single time:
[cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
The security rules that I've attempted:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.time < timestamp.date(2020, 11, 30);
}
}
}
allow read, write;
allow read, write: if true;
allow read, write: if request.auth.uid != null;
allow read, write: if request.auth != null;
allow read, write: if auth != null ;
I've literally tried everything under the sun with no success at reading or writing to my Database which leads me to believe that the fault lies somewhere in my code. I've checked to make sure that Firebase is correctly initialized, Firebase Core and Firebase Firestore have both been correctly initialized as well.
Is this a Firebase issue or is there a specific way that calls to Firestore need to be performed?
Get request:
fetchChatMessages() async {
var messageSnapShots =
await FirebaseFirestore.instance.collection('topicofday').get();
print('Messages: $messageSnapShots');
}
}
Initialize Firebase:
class MyApp extends StatelessWidget {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
FutureBuilder build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return buildMaterialApp(context);
}
return Center(
child: Container(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor,
),
),
);
},
);
}
Additional info:
Not sure if this would affect the request at all but I'm using Provider for State Management.

#DougStevenson thank you man! Turns out I was attempting to pull data from the wrong project within my Firebase console. Strange scenario but your comment encouraged me to think outside of the box.
The cause was placing the wrong GoogleService-Info.plist file from a different project inside of my Runner file.

Related

Issue in firestore database

I want to show current user data in my flutter app. But it print on screen " No data found".
This my database data
That error also happened error
My security rule
enter image description here
Here is my code
Container(
child: StreamBuilder(
stream: FirebaseFirestore.instance.collection("user3").where("id",isEqualTo:FirebaseAuth.instance.currentUser!.uid).snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot){
if(!snapshot.hasData){
return Text("Loading please wait........");
}
if (snapshot.hasData && snapshot.data!.docs.length > 0) {
DocumentSnapshot userData = snapshot.data!.docs[0];
// Build the widget using the userData
} else {
return Center(child: Text("No data found"));
}
return Container();
},
),
),
The long numeric values (e.g. "167582...") in your database screenshot do not look like a UID that any of the Firebase Authentication providers would generate.
Add this code right before you query the database:
print(FirebaseAuth.instance.currentUser!.uid)
This will show you the value that you're querying for, which (given my opening statement) probably looks quite different from the value in your database.
If that is indeed the case, the problem starts when you write the document. At that point you'll want to make sure that you write the value of FirebaseAuth.instance.currentUser!.uid to the id field.

Flutter getx: How to send data between pages from FirestoreQueryBuilder

I'd Like to get data in home screen of my flutter app, where I have list of OfferCards, these are generated from firestore via FirestoreQueryBuilder in my homeView like this
FirestoreQueryBuilder<OfferData>(
pageSize: 10,
query: FirebaseFirestore.instance
.collection('Offers')
.orderBy('CreatedAt', descending: true)
.withConverter<OfferData>(
fromFirestore: ((snapshot, options) =>
OfferData.fromJson(snapshot.data()!)),
toFirestore: (value, options) => value.toJson()),
builder: (context, snapshot, _) {
if (snapshot.isFetching) {
return const Center(
child: CircularProgressIndicator(color: Colors.greenAccent),
);
} else if (snapshot.hasError) {
return const Center(
child: Text('Server error'),
);
} else if (snapshot.docs.isEmpty) {
return const Center(
child: Text('No offers'),
);
} else {
return ListView.builder(
itemBuilder: (context, index) {
final hasReachEnd = snapshot.hasMore &&
index + 1 == snapshot.docs.length &&
!snapshot.isFetchingMore;
if (hasReachEnd) {
snapshot.fetchMore();
}
final post = snapshot.docs[index].data();
homeController.offers[index] = post;
return OfferCardView();
},
itemCount: snapshot.docs.length);
}
},
)
As on the end of this example, inside HomeController I have Map of int and UserData, which is filled with all offers. Each offerCardView has Get.find to HomeController to have access to this map. And here's my question, how do I determine inside of OfferCardView and later in OfferView(after tapping on given OfferCardView) which entry from map is being clicked on/view filled with. I don't know how to acomplish this, I'm aware that using Map here is bad decision, but I don't have clue how this should be done
The better practice is passing each document data with its index to the OfferView() constructor, so for every OfferCardView() that will be clicked, OfferView() will be opened with that data.
This ensures that your data will not rely on the GetxController availability, since depending on GetxController to exchange data like this could simply break.
For example :
While your app is growing and somewhere the controller is deleted either by Getx or manually using Get.delete() ( or you needed to call multiple controllers with different tags ), then Get.find() will not find that controller or mistake it, this leads to unexpected behaviors, which will put you in a hard time to find out what went wrong in your project.
Using GetPage, if you're required to assign the model data property, you could make a placeholder model for that data by default where we would say like :
There is no data so we showed you that placeholder alternative data page with this data.
This gives the user at least an overview of what's happening, not just a direct crash for the app.
I would say it's a good practice for the user experience.
You can share variables from other controllers onto another controller by using GetX Dependency Injection
On binding , add the controller you want to add as a dependency
Get.lazyPut<OfferCardsController>(() => OfferCardsController());
then in the controller
var offerCardsController = Get.find<OfferCardsController>();
you can now access variables from the OfferCardsController onOfferController
e.g
offerCardsController.variableFromCardsController;

How to set the object variable permanently in Flutter?

I am trying to build an app with Flutter and Dart. Here, I want to retrieve data from my Firebase, and I was able to do so and put it in the replies variable in each Message object that I have as an array type in the Firebase by using a for loop. However, when I try to access the replies variable again, it becomes empty.
I tried using setState, but that just causes the replies variable to keep resetting. Why this is the case, and how can I fix it?
StreamBuilder<List<Message>> pickMessage(
Stream<List<Message>> list, BuildContext context) {
return StreamBuilder(
stream: list,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong!');
} else if (snapshot.hasData) {
final message = snapshot.data!;
for (var msg in message) {
FirebaseFirestore.instance.collection("messages").doc(msg.msgID).get().then((value){
msg.replies = value.get("replies");
});
}
If You Need Some Variable to Initialize Once Put in Init Function or In Builder Function, outside the Scope of StreamBuilder, hope it helps

flutter: how to use 'await for' to wait for other BLoC event to finish

on screen init, I am loading my data via an externalData_bloc. On the same screen I am also rendering another widget controlled with internalData_bloc for user input which depends on the number of imported records (how many rows are needed). Of course, the rendering is faster than the data load, so I get null rows needed.
I found this nice question & answer, however, I do not get it to work. The solution says
Future loginProcess() async {
await for (var result in _userBloc.state) {
if (result.status == Status.finished) {
return;
}
}
}
I am within my repository. In here, I am also storing the external data in a variable. So within the repository class I have my function to get the number of records (properties are stored in the repository, and I want to return its length).
Future<int> getNoOfProperties(int problem) async {
LogicGraphsPStmntBloc bloc = LogicGraphsPStmntBloc();
Future propertiesLoad() async {
await for (var s in bloc) {
if (s == LogicGraphsPStmntLoadSuccess) {
return;
}
}
}
propertiesLoad();
return properties.length;
}
But no matter what I try to put in for await for (var result in XXXX) or if (s.YYY == ZZZ), it doesn't work. My understanding is, XXXX needs to be a stream which is why I used bloc = LogicGraphsPStmntBloc(). But I guess this creates another instance than the one used in my widgets. LogicGraphsPStmntBloc doesn't work either, nor do states. bloc at least doesn't throw an error already in the editor, but besides the instance issue, the if statement throws an error, where in cubit.dart StreamSubscription<State> listen(... is called with cancelOnError . Anyone having some enlightenment?
Bloc uses Stream and has a continuous flow of data. You might want to listen for changes in data instead of a finished task. Using a StreamBuilder is one way of doing this.
StreamBuilder<User>(
stream: bloc.userState, // Stream from bloc
builder: (context, AsyncSnapshot<State> snapshot) {
if (snapshot.hasData) {
// check if auth status is finished
}
}
)

How to combine multiple providers in Flutter

I am new to Flutter development.
I am building application where once users login they are shown list of posts.
If the user is not login still they are shown some random post.
I got parts of the application various posts in the internet.
This is what I did
class TestApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthBase>(
create: (context) => Auth(),
),
StreamProvider<User>.value(
value: Auth().onAuthStateChanged,
),
ProxyProvider<User, Database>(
update: (BuildContext context, User user, Database db) {
return user == null
? FirestoreDatabase(uid: null)
: FirestoreDatabase(uid: user.uid);
}),
ChangeNotifierProxyProvider<Database, PostProvider>(
create: (context) => PostProvider(),
update: (ctx, database, previousProvider) {
print("at ChangeNotifierProxyProvider $previousProvider");
return previousProvider.update(database);
},
),
],
child: MaterialApp(
title: 'Test Project',
home: LandingPage(),
),
);
}
My idea is;
if the user is logged in StreamProvider<User>.value will give that user to
ProxyProvider<User, Database> and it will create database with that user.
And that database in turn will be used by ChangeNotifierProxyProvider<Database, PostProvider> to create the provider that actually get posts. It uses the database to get the posts.
I noticed even if the user is logged in at the start I get a null value for the user then immediately i get the actual user.
In the landing page, I only have the following line and it generate an error
final provider = Provider.of<PostProvider>(context);
I noticed the cause for the error was this line;
previousProvider.update(database);
First time time Postprovider constructor and update methods get called without any problem. (here we get null user)
When the FirestoreDatabase get created with the actual user "previousProvider" is null.
This is the reason for the error.
Not sure why this is happening or how to fix it.
Is there a better way of doing this?
Initially getting a null value even when there is a logged in user may be the reason. How to prevent it?
I will try to explain your code a bit so we are on the same page
Provider<AuthBase>(
create: (context) => Auth(), //this subscribe an Auth Class to the provider
),
StreamProvider<User>.value(
value: Auth().onAuthStateChanged,
/// This subscribe a stream from another Auth Class instance
/// Unless Auth its a singleton class, this is not related to the previous Provider
/// and changes to Provider<AuthBase> won't affect this Stream
),
I noticed even if the user is logged in at the start I get a null
value for the user then immediately i get the actual user.
That happens when using Stream, while the first value is captured (sometimes is delayed a tick) its null, you can use initialData parameter if you know the first value while the stream subscription is done
Finally
ChangeNotifierProxyProvider<Database, PostProvider>(
create: (context) => PostProvider(),
update: (ctx, database, previousProvider) {
print("at ChangeNotifierProxyProvider $previousProvider");
return previousProvider.update(database);
},
),
update expects to return a value of type PostProvider, but you're returning whatever the method previousProvider.update(database) returns (which I think it just updates some inner variable or something, I believe it's some void method maybe?)
Change the update like this
update: (ctx, database, previousProvider) {
print("at ChangeNotifierProxyProvider $previousProvider");
return previousProvider..update(database);
/// It's the same as writing this
/// previousProvider..update(database);
/// return previousProvider;
},