Flutter - how to write data in Firebase (realtime ddb) : permission denied - android

I would like to write data in Firebase and I get a permission error, here is what I tried :
void initState() {
super.initState();
testFirebase();
}
Future testFirebase() async {
initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);
database.reference().child('counter').push().set(<String, String>{
'var': 'test'
});
}
Future initUser() async {
googleUser = await _ensureLoggedInOnStartUp();
if (googleUser == null) {
setState(() {
state.isLoading = false;
});
} else {
var firebaseUser = await logIntoFirebase();
}
}
Here is my Firebase rules :
The google-services.json file is added to the app root directory :
Result :
I get the following error message :
I tried also with :
push().setValue('2')
but it doesn't work and that makes me crazy, I don't understand...
Any idea?

Quick initial check is that you need to await initUser(). So:
Future testFirebase() async {
await initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
Without that I'd expect the calls to the database to start before auth has finished.
Update: I just verified that is indeed the case with this simple code:
void _signin() async {
print("Calling __actuallySignin");
__actuallySignin();
print("called __actuallySignin and waited");
}
void __actuallySignin() async {
print("Calling signIn...");
await FirebaseAuth.instance.signInAnonymously();
print("called signIn... and waited");
}
This prints:
flutter: Calling __actuallySignin
flutter: called __actuallySignin and waited
flutter: Calling signIn...
...
flutter: called signIn... and waited
So the __actuallySignin method is done before the sign in is done. To make the calling code wait for the result you add await:
await __actuallySignin();
Which outputs:
flutter: Calling __actuallySignin
flutter: Calling signIn...
flutter: called signIn... and waited
flutter: called __actuallySignin and waited

Just adding await to initUser() didn't work, the database was also not correctly adressed.
Solution :
FirebaseDatabase.instance.reference().child ...
instead of :
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);

I was able to fix this problem by not accessing the database reference using the method listed in the plugin documentation:
final FirebaseDatabase database = FirebaseDatabase(app: app);
this.databaseRef = database.reference();
But instead making use of this approach:
this.databaseRef = FirebaseDatabase.instance.reference();
When you work with multiple pieces of Flutter documentation, you will see that many approaches work for reading public information from the Real Time Database, but only one method worked for reading information that depends on the auth variable. This was my experience.
Working method: https://marcinszalek.pl/flutter/firebase-database-flutter-weighttracker/
Non-working method was associated with the plugin documentation: https://pub.dartlang.org/packages/firebase_database#-example-tab-
Also, my project was configured using the main Firebase Flutter documentation for linking my app to Firebase: https://firebase.google.com/docs/flutter/setup

Related

Flutter Firebase Functions: An error occured while calling function

I recently started developing an app using Flutter and Firebase. I use Firebase Emulator to test Authentication and Cloud Functions. Most of my code is in the Firebase Cloud Functions which I use for all CRUD for Firestore and RTDB. While adding some new features, I got this error in my app. I tried searching a lot but could not find any solution. The following is the error is receive:
An error occured while calling function profile-get
Error Details: null
Message: An internal error has occurred, print and inspect the error details for more information.
Plugin: firebase_functions
Stacktrace: null
My API class in Flutter:
class Api {
Api(this.functions);
final FirebaseFunctions functions;
static Api init() {
FirebaseFunctions functions = FirebaseFunctions.instance;
if (emulator) functions.useFunctionsEmulator(origin: host);
return Api(functions);
}
Future<ApiResult> call(String name, {
Map<String, dynamic> parameters,
}) async {
try {
HttpsCallable callable = functions.httpsCallable(name);
HttpsCallableResult results = await callable.call(parameters);
return ApiResult(new Map<String, dynamic>.from(results.data));
} on FirebaseFunctionsException catch (e) {
print('An error occurred while calling function $name.');
print('Error Details: ${e.details}\nMessage: ${e.message}\nPlugin: ${e.plugin}\nStacktrace: ${e.stackTrace}');
return ApiResult({
'status': 'error',
'message': 'An error occured',
'code': 'unknown'
});
}
}
static String get host => Platform.isAndroid ? 'http://10.0.2.2:2021' : 'http://localhost:2021';
}
I tried running the functions directly from their local URL and they work fine.
As mentioned in the comments defore you are reating a cloud function with onRequest. Those are not callable using an SDK but only trough https URL.
To create a callable function that you can call trough Firebase SDKs you would need to refactor your functions to use the onCall.
It should look something like this:
exports.yourFunctionName= functions.https.onCall((data, context) => {
// receive the data
const text = data.text;
// return a response
return {
test:'test'
}
});
Here you have more information how the callable functions work.
Are you using a different region than the standard us-central1? This is often the case, so you need to change the region you are calling from
HttpsCallable callable = FirebaseFunctions.instanceFor(region:"your_region").httpsCallable(name);

How to call MethodChannel from an isolate created by android_alarm_manager?

I tried to schedule background service. For that i used https://pub.dev/packages/android_alarm_manager. It works well.
For my example, I tried to get from my isolate (android_alarm_manager's callback) the battery level following the flutter tutorial : https://flutter.dev/docs/development/platform-integration/platform-channels?tab=android-channel-java-tab.
If I call manualy my callback it works (so I well do the Android part). If android_alarm_manager call it, I got the following error appear :
Unhandled Exception: MissingPluginException(No implementation found for method getBatteryLevel on channel net.example.com/battery)
It's weird because, from an other isolate where I used https://pub.dev/packages/flutter_downloader to download file, this plugin used MethodChannel...
Here is my code for android_alarm_manager :
import 'package:flutter/services.dart';
class AndroidManagerCallBack {
static Future<void> main() async {
_AndroidManagerCallBack test = _AndroidManagerCallBack();
await test.getBatteryLevel();
}
}
class _AndroidManagerCallBack {
static const platform = const MethodChannel('net.example.com/battery');
Future<void> getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
print(batteryLevel);
}
}
I simply call the callback like :
AndroidAlarmManager.periodic(
Duration(seconds: 20),
0,
AndroidManagerCallBack.main(),
rescheduleOnReboot: true,
wakeup: false,
);
In android_alarm_manager's callback, I can call plugins which used somee MethodChannel but when I tried with my MethodChannel, I got errors...
Someone can guide me :) ?
It seem impossible to call directly MethodChannel through an isolate.
But, with the creation of a plugin i can achieve what i want. So the solution is to create a plugin :) !

Flutter UnitTest: Two different mocks for the same class

currently I am writing Unit tests for my mobile web application (Android app). The unit tests refer to my login system built upon Google Firebase Auth.
With one of the latest releases of the firebase authentication plugins, the return type of some functions have been changed. For example, the method firebaseAuth.signInWithEmailAndPassword has been changed from FirebaseUser to AuthResult
I have a login function in a AuthService.dart with the following content:
Future<bool> login(LoginUserData userData) async {
try {
_checkUserDataValidity(userData);
final AuthResult authResult= await firebaseAuth.signInWithEmailAndPassword(email: userData.email, password: userData.password);
return authResult.user != null;
} catch (exception) {
return false;
}
}
So, as you can see I am only checking whether the data of the user is correct or not - I am returning a boolean to check this in another file (if true is returned, the Navigator routes to another site, if wrong is returned an error message pops up).
My Unit tests for example checks for a few cases:
Correct data
Incorrect data (userData is not empty or null)
Empty data
Example code of a test:
test('Login should work with valid user data', () async {
when(
firebaseAuthMock.signInWithEmailAndPassword(
email: testValidUserData.email,
password: testValidUserData.password,
)
).thenAnswer(
(_) => Future<AuthResultMock>.value(firebaseAuthResultMock)
);
final bool actionSuccess = await authService.login(testValidUserData);
verify(
firebaseAuthMock.signInWithEmailAndPassword(
email: testValidUserData.email,
password: testValidUserData.password,
)
).called(1);
expect(actionSuccess, true);
});
To cover case one and two of my list above, I'd like to use two different Mocks. One Mock to contain a user, the other mock has no user in it.
Current issue:
(_) => Future<AuthResultMock>.value(firebaseAuthResultMock)
in my test is in conflict with this code from the AuthServie.dart
return authResult.user != null;
The mock is having null as user. How can I change this? :) Bt.w I am using Mockito.
My idea was:
For correct data, to return a mock, that contains a user.
For invalid data, to return a mock, that has null as user.
Thank you so much. :)
I was just recently looking for the answer and stumbled across your question. But in the meanwhile, I found a solution. I hope this works for you.
I found that there are a bunch of flutter packages for mocking firebase, the one for Firebase Auth is this one: https://pub.dev/packages/firebase_auth_mocks.
Also if you don't want to import more packages into your code you can create a simpler version of them. Like so:
class MockFirebaseAuth extends Mock implements FirebaseAuth {}
class MockAuthResult extends Mock implements AuthResult {
FirebaseUser user = MockFirebaseUser();
}
class MockFirebaseUser extends Mock implements FirebaseUser {
#override
String get displayName => 'Test Name';
#override
String get uid => 'abc1234';
}

Firebase Performance Monitoring React Native integration

I have install Firebase Performance monitoring on my React Native app
and integrate it successfully. After i want to track my network requests performance and go through as documentation.
const trackRequest = async (url,method) => {
// Define the network metric
const metric = await perf().newHttpMetric(url, method);
// Define meta details
metric.putAttribute('testAttr', 'testValue');
// Perform a HTTP request and provide response information
const response = await fetch(url);
metric.setHttpResponseCode(response.status);
metric.setResponseContentType(response.headers.get('Content-Type'));
metric.setResponsePayloadSize(response.headers.get('Content-Length'));
// Stop the trace
await metric.stop();
return response.json();
};
I use this function from documentation and call it every network requests time
fetch("www.example.com")
trackRequest("www.example.com","GET")
Can anyone explain me what i were doing wrong ?
It looks like you're not using the API correctly. There appears to be a good example in the documentation. You need to call start() on the metric before calling stop(). Also, the example shows that you should use await on each method call, but I don't really know if that's necessary.
const trackRequest = async (url,method) => {
const metric = await perf().newHttpMetric(url, method);
// !!! Don't forget to call start()
await metric.start();
await metric.putAttribute('testAttr', 'testValue');
const response = await fetch(url);
await metric.setHttpResponseCode(response.status);
await metric.setResponseContentType(response.headers.get('Content-Type'));
await metric.setResponsePayloadSize(response.headers.get('Content-Length'));
// Stop the trace only after you started it
await metric.stop();
return response.json();
};

Get document id from firebase flutter

I trying to get the new created document id after data has been stored to firebase database, but get error
E/flutter (20333): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The method 'listen' was called on null.
E/flutter (20333): Receiver: null
E/flutter (20333): Tried calling: listen(Closure: (String) => void)
send_data_bloc
_repository
.addOrder(order)
.listen((documentId) => print(documentId));
repository
#override
Observable<String> addOrder(Order order) {
var a = endpoints.collectionEndpoint.add(order.toJson());
a.then((val) {
return Observable.fromFuture(val.documentID());
});
endpoints
#override
get collectionEndpoint => _firestore
.collection(collectionName)
.document(this.id)
.collection(orderCollectionName);
Ideally you should return the future from the repository and await for the future on the bloc. Let me try to give a full code snippet here. It would be something like this:
send_data_bloc
final documentId = await _repository
.addOrder(order);
print(documentId);
return documentId;
repository
#override
Future<String> addOrder(Order order) {
return endpoints.collectionEndpoint.add(order.toJson());
endpoints
#override
get collectionEndpoint => _firestore
.collection(collectionName)
.document(this.id)
.collection(orderCollectionName);
Here
a.then((val) {
return Observable.fromFuture(val.documentID());
});
you are returning the observable within the then function, i believe this is not the expected behavior.
One thing you should do to improve your code quality and readability is to just user async/await. The function on the repository can be rewrited like that:
#override
Observable<String> addOrder(Order order) async {
var documentID = await endpoints.collectionEndpoint.add(order.toJson());
return Observable.fromFuture(val.documentID());
Try this. This should do the trick.
Whats the reason why you are using Observables? Is this a firebase thing?
You could adjust to:
final var documentId = await _repository.addOrder(order);
print(documentId)
i had the same problem here is a snippet of how i approached it
//notice im using add while referencing the document reference
final DocumentReference documentReference=await Firestore.instance.collection('jobs').add({
'jobid':"",
});
then get your id from documentReference
final String jobIdd=documentReference.documentID;
after getting the id now you can add your document to cloud firestore
Firestore.instance.collection('jobs').document(jobIdd).setData({
'category': category,
'description': description,
'datePosted': formattedDate,
'postedby': userid,
'jobid':jobIdd,
});

Categories

Resources