I am trying to implement Firebase Dynamic links in a flutter app. When I click on the link it opens the app but doesn't call the listen functions.
I reconfigured step by step according to FlutterFire, so I don't think the issue is in configuration, but maybe in the way I'm using the plugin as there is no documentation on the last version of the plugin.
Firebase is correctly initialised in my app as I'm using other services.
I'm doing tests on android simulator
I'm trying to listen the dynamic link from a stateful widget with the following code
I'm first navigating to the page containing this widget, then I background the app, I click on the link, the app opens at the same place and nothing happens.
#override
void initState() {
super.initState();
initLink();
}
void initLink() {
FirebaseDynamicLinks.instance.onLink.listen((dynamicLinkData) {
print('dynamic link');
print(dynamicLinkData.toString());
// Navigator.pushNamed(context, dynamicLinkData.link.path);
}).onError((error) {
// Handle errors
});
}
There is an open issue here https://github.com/FirebaseExtended/flutterfire/issues/8261 where a few others are having the same problem including myself.
It seems for now the temporary solution to at least getting things working again is posted by odlund. If you make these changes the listener should work again until we have more of an official fix:
https://github.com/FirebaseExtended/flutterfire/commit/8bb4bee7e678241e75ab37a2bcfa0831426b91fa
Please update firebase_dynamic_links to 4.1.1. Seems to be an issue with the version 4.1.0 or earlier where FirebaseDynamicLinks.instance.onLink.listen doesn't work
You don't need to check if the app is in the background or resumed for this to work.
This Works Perfectly!
class _MainAppState extends State<MainApp> {
Future<void> initDynamicLinks() async {
print("Initial DynamicLinks");
FirebaseDynamicLinks dynamicLinks = FirebaseDynamicLinks.instance;
// Incoming Links Listener
dynamicLinks.onLink.listen((dynamicLinkData) {
final Uri uri = dynamicLinkData.link;
final queryParams = uri.queryParameters;
if (queryParams.isNotEmpty) {
print("Incoming Link :" + uri.toString());
// your code here
} else {
print("No Current Links");
// your code here
}
});
// Search for Firebase Dynamic Links
PendingDynamicLinkData? data = await dynamicLinks
.getDynamicLink(Uri.parse("https://yousite.page.link/refcode"));
final Uri uri = data!.link;
if (uri != null) {
print("Found The Searched Link: " + uri.toString());
// your code here
} else {
print("Search Link Not Found");
// your code here
}
}
Future<void> initFirebase() async {
print("Initial Firebase");
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// await Future.delayed(Duration(seconds: 3));
initDynamicLinks();
}
#override
initState() {
print("INITSTATE to INITIALIZE FIREBASE");
super.initState();
initFirebase();
}
Related
I was doing a project and I encounter a problem. I want the app changes page if the user is logged in or not even if it's starting the app. I came up with an idea to have a widget under the root called Wrapper that has a function to change each time the authentication state changes.
But it doesn't work...
Could you guys help me with that?
The Wrapper class:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
if(updateUserStatus())
return Home();
else
return Authenticate();
}
bool updateUserStatus() {
bool isLogged = false;
FirebaseAuth.instance.authStateChanges().listen((User user) {
if (user == null) {
isLogged = false;
print("User Not logged");
} else {
isLogged = true;
print("User logged");
}
});
return isLogged;
}
}
The output on the Debug console is right but, the page doesn't change.
Sorry, brother for not answering in details. But I had encountered the same problem, to solve this issue I watched the following playlist in youtube, it gives solution to your problem, but you have to watch it till the end.This is a link https://www.youtube.com/watch?v=u_Lyx8KJWpg&list=PLNnAcB93JKV_NIGSneTazb9yMpILapEjo
My app is built with a combination of Provider and Bloc patterns, but moving towards full BLoC. I'm looking for the best way to architect a solution, and basically just where to put this code and whats best practice.
I'm building a booking app, and my user has a field called currentBookingId. When this value is null, the user is left free to use the app completely. However, when the user makes a booking, I'm sending them to a details page.
Using Firebase and streams, where is the best place to listen for changes to this field, and to navigate to the appropriate page? They also need to go straight to this page when opening the app.
At the moment, It's just in my root HomeScreen widget which adds bloat and complexity. This is how I've made it:
#override
void initState() {
super.initState();
final database = Provider.of<DatabaseService>(context, listen: false);
_userStream = database.userStream.listen((user) {
final wasOnTrip = [some logic];
final isOnTrip = [some other logic];
setState(() => _user = user);
if (!wasOnTrip && isOnTrip) {
_navigateToCurrentTrip();
} else if (wasOnTrip && !isOnTrip) {
_navigateHome();
}
});
}
void _navigateToCurrentTrip() {
Navigator.of(context).pushNamedAndRemoveUntil(tripDetailsRoute,
(route) => route.isCurrent && route.settings.name == tripDetailsRoute
? false : true);
}
void _navigateHome() {
Navigator.of(context).popUntil((route) => route.isFirst);
}
I should have dived into the docs a little deeper. Adding a Bloc Listener on both pages works perfectly and is simple to implement.
https://bloclibrary.dev/#/recipesflutternavigation?id=ui-layer-1
I am using Firestore in flutter application. Each time user launch the application it retrieves some data from Firestore Cloud.
QuerySnapshot dataSnapshot = await Firestore.instance
.collection('/data')
.getDocuments();
When user opens the application on first time, it required from him to connect online, to get the data, and as Firebase documents say
For Android and iOS, offline persistence is enabled by default. To disable persistence, set the PersistenceEnabled option to false.
So, it should save the data that application have been read before to retrieve it while the device is offline; so user can access application at anytime with the same data that have been read.
The problem is: it takes too long time to retrieve the data while the device is offline, with the same codes and nothing changed!.
I tried to configure how much time it takes? On offline, it takes about 8 minutes and 40 seconds. But while on online, it takes just 10 seconds, maybe less.
So how can I solve this problem?
============
UPDATE
I manged to get more logs about this problem, which after take a lot of time, and will start application with the offline saved data, it prints this log
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
And then take 3 second for example (not much time) and continue with the next works.
I did open a new issue in GitHub too.
Is there a way to limit the time it takes?
And finally, with the help of diegoveloper comment in GitHub issue, I have reached the solution.
This comment
await Firestore.instance
.collection("Collection")
.getDocuments(source: source)
was a good solution if I decided to check source each time and then use it or I can use it in starting of a new Flutter project, but now I already have a lot of codes that need a better solution. So I decided to fork the cloud_firestore package and edit it.
You can find it here: https://github.com/ShadyBoshra2012/flutterfire/tree/master/packages/cloud_firestore
What I have edited:
firestore.dart
// The source of which the data will come from.
static Source _source = Source.serverAndCache;
static Source get source => _source;
Future<void> settings(
{bool persistenceEnabled,
String host,
bool sslEnabled,
bool timestampsInSnapshotsEnabled,
int cacheSizeBytes,
Source source}) async {
await channel.invokeMethod<void>('Firestore#settings', <String, dynamic>{
'app': app.name,
'persistenceEnabled': persistenceEnabled,
'host': host,
'sslEnabled': sslEnabled,
'timestampsInSnapshotsEnabled': timestampsInSnapshotsEnabled,
'cacheSizeBytes': cacheSizeBytes,
});
if (source != null) _source = source;
}
query.dart
source = Firestore.source; Line 92
document_reference.dart
source = Firestore.source; Line 83
How you can use it?
So you can use my forked repository in this way with using connectivity package from Google : https://pub.dev/packages/connectivity .
Add my forked repository in pubspec.yaml file
cloud_firestore:
git:
url: https://github.com/ShadyBoshra2012/flutterfire.git
path: packages/cloud_firestore
Then in your first screen or main
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await Firestore.instance.settings(source: Source.cache);
} else {
await Firestore.instance.settings(source: Source.serverAndCache);
}
and if you want to refresh the source when change the connection state:
StreamSubscription subscription;
void initState() {
super.initState();
// Check the internet connection after each change
// of the connection.
subscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
// Check the internet connection and then choose the appropriate
// source for it.
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await Firestore.instance.settings(source: Source.cache);
} else {
await Firestore.instance.settings(source: Source.serverAndCache);
}
});
}
#override
void dispose() {
super.dispose();
subscription.cancel();
}
So I hope it works with everyone see it, and waiting for Flutter Team to code a better and better solution. Thanks for everyone has participated.
In addition to Shady Boshra's answer you can use FirebaseFirestore.instance.disableNetwork() functionality so your code will look like this:
StreamSubscription subscription;
void initState() {
super.initState();
// Check the internet connection after each change
// of the connection.
subscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
// Check the internet connection and then choose the appropriate
// source for it.
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) {
await FirebaseFirestore.instance.disableNetwork();
} else {
await FirebaseFirestore.instance.enableNetwork();
}
});
}
When I open app by tapping on FCM push notification, The API service calls I am making by using await keyword those are not working. Entire app not returning data.
Code for API calling
var result = await objHomework.GetHomeWorksForStudentPagesAsync(studentId.ToString());
result returning null. if app already open, everything working fine. See the Image below screenshot of app
Notification messages are delivered to OnMessageReceived callback only when the app is in the foreground.
Override the HandleIntent Method of the FirebaseMessageService to work for background as well
public override void HandleIntent(Intent intent)
{
try
{
if (intent.Extras != null)
{
var builder = new RemoteMessage.Builder("MyFirebaseMessagingService");
foreach (string key in intent.Extras.KeySet())
{
builder.AddData(key, intent.Extras.Get(key).ToString());
}
this.OnMessageReceived(builder.Build());
}
else
{
base.HandleIntent(intent);
}
}
catch (Exception)
{
base.HandleIntent(intent);
}
}
Actually, I was missing some keys which is necessary for service call authentication in my project. I am getting those keys in MaiActivity but notification click even starting app from somewhere else therefore keys values was null and service calls was not happening.
Is there a strategy that would work within the current Firebase offering to detect if the server connection is lost and/or regained?
I'm considering some offline contingencies for mobile devices and I would like a reliable means to determine when the Firebase data layer is available.
This is a commonly requested feature, and we just released an API update to let you do this!
var firebaseRef = new Firebase('http://INSTANCE.firebaseio.com');
firebaseRef.child('.info/connected').on('value', function(connectedSnap) {
if (connectedSnap.val() === true) {
/* we're connected! */
} else {
/* we're disconnected! */
}
});
Full docs are available at https://firebase.google.com/docs/database/web/offline-capabilities.
Updated:
For many presence-related features, it is useful for a client to know when it is online or offline. Firebase Realtime Database clients provide a special location at /.info/connected which is updated every time the client's connection state changes. Here is an example:
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
I guess this changed in the last couple of months. Currently the instructions are here:
https://firebase.google.com/docs/database/web/offline-capabilities
In summation:
var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");
and:
var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", function(snap) {
if (snap.val() === true) {
alert("connected");
} else {
alert("not connected");
}
});
I'll admit I don't know a lot about how references are set, or what that means (are you making them out of thin air or do you have to have already created them beforehand?) or which one of those would trigger something on the server as opposed to something on the front end, but if the link is still current when you read this, a little more reading might help.
For android you can make user offline by just a single function called onDisconnect()
I did this in one of my chat app where user needs to get offline automatically if network connection lost or user disconnected from Internet
DatabaseReference presenceRef = FirebaseDatabase.getInstance().getReference("USERS/24/online_status");
presenceRef.onDisconnect().setValue(0);
On disconnecting from network Here I am making online_status 0 of user whose Id is 24 in firebase.
getReference("USERS/24/online_status") is the path to the value you need to update on offline/online.
You can read about it in offline capabilities
Note that firebase takes time around 2-10 minutes to execute onDisconnect() function.
firebase for web
firebase.database().ref(".info/connected").on("value",(snap)=> {});
The suggested solution didn't work for me, so I decided to check the connection by writing and reading 'health/check' value. This is the code:
const config = {databaseURL: `https://${projectName.trim()}.firebaseio.com/`};
//if app was already initialised delete it
if (firebase.apps.length) {
await firebase.app().delete();
}
// initialise app
let cloud = firebase.initializeApp(config).database();
// checking connection with the app/database
let connectionRef = cloud.ref('health');
connectionRef.set('check')
.then(() => {
return connectionRef.once("value");
})
.then(async (snap) => {
if (snap.val() === 'check') {
// clear the check input
await connectionRef.remove();
// do smth here becasue it works
}
});