How to use google cloud functions in flutter? - flutter

Now this is the index.js file
import 'dart:math';
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.getRandomNumbers=functions.https.onCall((num){
const numbers=admin.firestore().collection('RandomNumbers').document('CurrentRandomNumber');
return numbers.add({
RandomNumber=new Random().nextInt(10);
});
});
and the function to do the task
onPressed: (){
CloudFunctions.instance.getHttpsCallable(
functionName: "getRandomNumbers",
);
},
Now as you can see I am trying to get a random number and add it to the firestore database but it is nor working neither showing any error. I am using android studio and I have installed all stuff as suggested in official doc.

The Flutter code you shared doesn't actually call the Cloud Function yet. What you have now merely defines a reference to that Cloud Function. To call it, you'd do something like:
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'getRandomNumbers',
);
dynamic response = await callable.call();
This is a pretty direct copy from the sample in the documentation on using Cloud Functions in Flutter, so I'd recommend keeping that handy while implementing it.
I noticed that your Cloud Function declaration looks unusual:
exports.getRandomNumbers=functions.https.onCall((num){
The examples in the Firebase documentation follow this pattern:
exports.addMessage = functions.https.onCall((data, context) => {
So the arguments are always data and context, and any parameters you pass from your flutter code will be in the data argument.
For your current code it makes no difference, since you're not using any of the arguments, but I do recommend addressing this going forward.

Related

How to make "compute()" function insert data to sqlite while in isolated process?

I'm working on flutter app that uses php apis for server and sqlite for local data.
The problem is with "compute()".
Here is the explanation :
I have three functions that receives data from api on the server, then add the data to my local database (sqlite) table.
First function to get data from server.
Future<List<Map<String, dynamic>>> getServerData(int vers)async {
//my code
}
Second function to insert data into local database:
Future<int> addNewData(List<Map<String, dynamic>>)async {
//my code
}
Third function to call the first and second function:
Future<bool> checkServerData(int vers)async {
List<Map<String, dynamic>> sdt= await getServerData(vers);
int res=await addNewData(sdt);
if(res>0) return true;
else return false;
}
I want to call the third function in a compute function:
compute(checkServerData, 2);
When did that I found this error:
null check operator used on null value.
Note*:
If I used it without calling local database it works good.
The error appears if I called the database to insert data into.
When I searched about this issue I found that it's not allowed to access any resources which generated in one thread from another thread. But I didn't understand exactly how to resolve it or how to use another way that do the same idea.
After searching about the issue specified, I found those workaround solutions:
1: if the process is very important to work in background, you can use the Isolate package classes and functions which allow all isolated processes or contexts to share data between them as messages sending and receiving. But it's something complex for beginners in flutter and dart to understand these things, except those who know about threading in another environments.
To now more about that I will list here some links:
Those for flutter and pub documentation:
https://api.flutter.dev/flutter/dart-isolate/dart-isolate-library.html
https://api.flutter.dev/flutter/dart-isolate/Isolate-class.html
https://pub.dev/packages/flutter_isolate
This is an example in medium.com website:
https://medium.com/flutter-community/thread-and-isolate-with-flutter-30b9631137f3
2: the second solution if the process isn't important to work on background:
using the traditional approaches such as Future.builder or async/await.
You can know more about them here:
https://www.woolha.com/tutorials/flutter-using-futurebuilder-widget-examples
https://dart.dev/codelabs/async-await
and you can review this question and answers in When should I use a FutureBuilder?

How can I delete an image I have stored in Google Coud Storage Bucket in Flutter? (Not Firebase Cloud Storage)

I have the image's public link. I'm thinking of the gcloud flutter package https://pub.dev/packages/gcloud but I can't really find anything that shows how to do exactly what I'm trying. Advanced Thanks.
PLEASE, I'm not talking about Firebase Cloud Storage.
To delete a file, first create a reference to that file. Then call the delete() method on that reference, which returns a Promise that resolves, or an error if the
Promise rejects.
import { getStorage, ref, deleteObject } from "firebase/storage";
const storage = getStorage();
// Create a reference to the file to delete
const desertRef = ref(storage, 'images/desert.jpg');
// Delete the file
deleteObject(desertRef).then(() => {
// File deleted successfully
}).catch((error) => {
// Uh-oh, an error occurred!
});

How to check whether the user is a new user or not in #assistant/conversation package

In the actions-on-google package I have used the following logic to check whether the user is a new user or not.
conv.user.last.seen
But in the new #assistant/conversation package i have used the same logic but it fails stating that the seen is not found. I tried the following logic which shows the current date and time which makes the logic to pass all time.
conv.user.lastSeenTime
Does anyone tried to show whether the user is a new or not in the new #assistant/conversation package?
I ‘m dealing with a similar issue of how to differentiate between new vs returning users with google console’s action builder. The Main invocation is the part of the conversation I'm using this in. (Brand new to the forum so forgive me if I didn’t grasp your question.) I used the same logic as you did, and it did deploy correctly for me using the cloud function's inline editor for the webhook. So I ‘m not entirely sure what’s going on but here are some resources. The following tutorial and code was helpful/ worked for me (https://youtu.be/nVbwk4UKHWw?t=700 )
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
const app = conversation({debug:true});
app.handle('greeting', conv => {
let message = 'Welcome to App';
if (conv.user.lastSeenTime) {
message = 'Welcome back to App!';
}
conv.add(message);
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

Flutter Firebase Cloud function can not be called

I am getting an error whilst using Firebase Cloud Functions when I try to call a callable function from Flutter.
flutter: caught generic exception
flutter: PlatformException(functionsError, Firebase function failed with exception., {message: NOT FOUND, code: NOT_FOUND})
Here is how I try to call the cloud function with using cloud_functions: ^0.4.2+3
import 'package:cloud_functions/cloud_functions.dart';
_check(String id) async {
HttpsCallable callable = CloudFunctions.instance
.getHttpsCallable(functionName: 'checkUserFavorites');
try {
final HttpsCallableResult result = await callable.call(
<String, dynamic>{
'id': id,
},
);
print(result.data);
} on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
}
I have experienced similar issues, and with few days of debugging and experimenting I found the solution only after studying the source code of Cloud Functions Plugin for Flutter.
When you deploy Firebase Cloud function, you can choose any region of preference (closer to your application the better). For example
// using DigitalOcean spaces
exports.generateCloudImageUrl = functions
.region('europe-west3')
.https.onCall((reqData, context) => {
...
}
When you want to call this function from Flutter app, you must specify the region, otherwise all goes to us-central1 which is default. See example code on how to use a function deployed in a specific region
final HttpsCallable generateCloudImageUrl = new CloudFunctions(region: "europe-west3")
.getHttpsCallable(functionName: 'generateCloudImageUrl');
// NB! if you initialize with 'CloudFunctions.instance' then this uses 'us-central1' as default region!
see cloud_function source for init.
Update, as of recent release, you can initialize as below;
FirebaseFunctions.instanceFor(region: "europe-west3").httpsCallable(
"generateCloudImageUrl",
options:
HttpsCallableOptions(timeout: const Duration(seconds: 30)));
Cloud functions are supported in the regions that you are currently running them, according to the Cloud Functions Location Documentation, but not in all regions.
According to what you shared in the comments, I would say that there are 3 cenarios to your issue:
europe-west1: The function is probably out of date, since you are getting an unespected data format error, which suggest that it expects different data/format than your default function.
europe-west2: The function is not deployed in this region, this is hinted in the error message message: NOT FOUND.
Default Function (unknown region): This is the most recent version of the function, on a region different than europe-west1 and europe-west2, and it accepts the call
with the data in the format that you are sending.
NOTE: You can check which regions you currently have your cloud function deployed on the cloud functions dashboard, as you can see on the example image below:
Also, I suspect that the default region you using is us-central1, since according to the documentation:
By default, functions run in the us-central1 region
To fix your issue, I suggest that you redeploy your current version of the function to the europe-west regions that you intend to use.
There are three reasons this error mostly happens:
1. Call the correct function:
Make sure to call the correct function in its full name (visible when you start a local emulator). Espacially if you have additional exports of files in your index.js file make sure to call the export name as well.
Syntax: serverLocation-optionalExportParent-yourFunction
Example: us-central1-post_functions-updateShare
Note that the server location can also be configured in your instance
2. Emulator: Same WIFI
Make sure to be connected to the same wifi, when using the emulator. Otherwise, any call will end in unvailablity resulting in
Unhandled Exception: [firebase_functions/unavailable] UNAVAILABLE
3. Emulator: Correct host configuration
To connect to a physical device the host at all emulators at your firebase.json must be configured: Simply add "host": "0.0.0.0".
Now the host in flutter needs to be your ip-adress of the computer. More on that here
In my case, in addition to the regional issue, what really solved to me was to include the script below in index.html:
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-functions.js"></script>

Firestore + Cloud Functions: Date behavior has changed, YOUR APP MAY BREAK [duplicate]

Yesterday, all my firebase functions started throwing the following warning:
The behavior for Date objects stored in Firestore is going to change
AND YOUR APP MAY BREAK. To hide this warning and ensure your app does
not break, you need to add the following code to your app before
calling any other Cloud Firestore methods:
const firestore = new Firestore();
const settings = {/* your settings... */ timestampsInSnapshots: true};
firestore.settings(settings);
With this change, timestamps stored in Cloud Firestore will be read
back as Firebase Timestamp objects instead of as system Date objects.
So you will also need to update code expecting a Date to instead
expect a Timestamp. For example:
// Old: const date = snapshot.get('created_at');
// New: const timestamp = snapshot.get('created_at'); const date =
timestamp.toDate();
Please audit all existing usages of Date when you enable the new
behavior. In a future release, the behavior will change to the new
behavior, so if you do not follow these steps, YOUR APP MAY BREAK.
Now I want to init my firestore correctly according to this warning. i'm using typescript.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
let fireStoreDB = fireStoreDB || admin.firestore()
the firestore() that return from admin doesn't have a .settings() method as described in the warning nor it gets an object in the constructor. (It gets an App Object).
so I have two questions:
How can init my firestore to get the settings object?
when I insert a date to a document or when I query a document do I also need to pass the Firestore.Timestamp object? or can i query/insert the normal JS Date object and it will get converted automatically?
Thanks.
EDIT:
i managed to solve it for http functions using :
if (!fireStoreDB){
fireStoreDB = admin.firestore();
fireStoreDB.settings(settings);
}
But it still is happening on firestore triggers.
anyone knows how to give default settings to admin on :
admin.initializeApp(functions.config().firebase);
so it will not happen on firestore triggers?
You are still receiving JS Date instead of Firestore Timestamp due to a bug... now fixed in Firebase Functions v2.0.2.
See: https://github.com/firebase/firebase-functions/releases/tag/v2.0.2.
For initialisation I've used admin.firestore().settings({timestampsInSnapshots: true}) as specified in warning message, so the warning has disappeared.
When you add a date to a Document, or use as a query parameter, you can use the normal JS Date object.
add below 2nd line code in your index.js firebase functions file
admin.initializeApp(functions.config().firebase);
admin.firestore().settings( { timestampsInSnapshots: true })
solved it the following way:
const settings = {timestampsInSnapshots: true};
if (!fireStoreDB){
fireStoreDB = admin.firestore();
fireStoreDB.settings(settings);
}
For firebase-admin version 7.0.0 or above
You should not be getting this error anymore.
In Version 7.0.0 - January 31, 2019 of firebase-admin there were some breaking changes introduced:
BREAKING: The timestampsInSnapshots default has changed to true.
The timestampsInSnapshots setting is now enabled by default so timestamp
fields read from a DocumentSnapshot will be returned as Timestamp objects
instead of Date. Any code expecting to receive a Date object must be
updated.
Note: As stated in the official docs, timestampsInSnapshots is going to be removed in a future release so make sure to remove it altogether.
For older versions of firebase-admin (6.5.1 and below)
This should do the work:
const admin = require('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();
// Add this magical line of code:
firestore.settings({ timestampsInSnapshots: true });
Then in your function use the firestore object directly:
firestore.doc(`/mycollection/${id}`).set({ it: 'works' })