Flutter : Dynamic Link not launching the app - firebase

I am using firebase dynamic links to open the email verification link in my app, but unfortunetly the link doesn't launch the app when tapped.
What I've done so far
When a new user is created, a link is sent by email to be verified :
if(firebaseUser != null && !firebaseUser.emailVerified){
await createUserInDatabaseIfNew(firebaseUser);
var actionCodeSettings = auth.ActionCodeSettings(
url: 'https://muslimcoloc.page.link/?email=${firebaseUser.email}',
dynamicLinkDomain: "muslimcoloc.page.link",
androidInstallApp: true,
androidMinimumVersion: "12",
androidPackageName: "com.app.muslim_coloc",
iOSBundleId: "com.muslim_coloc.ios",
handleCodeInApp: true,
);
await firebaseUser.sendEmailVerification(actionCodeSettings);
}
I got the dynamicLinkDomain in the firebase console :
Then, I handle the reception of the link in my main.dart file, with the firebase dynamic links package :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
MyApp({Key key, }) : super(key: key);
#override
Widget build(BuildContext context) {
return AppView();
}
}
class AppView extends StatefulWidget {
const AppView({
Key key,
}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
this.initDynamicLinks();
}
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
FirebaseAuth auth = FirebaseAuth.instance;
//Get actionCode from the dynamicLink
var actionCode = deepLink.queryParameters['oobCode'];
try {
await auth.checkActionCode(actionCode);
await auth.applyActionCode(actionCode);
// If successful, reload the user:
auth.currentUser.reload();
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-action-code') {
print('The code is invalid.');
}
}
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(...)
}
When I tap the link of the email, the app doesn't start nor does the browser. Here's what happens :
It tries to launch something on the browser, but then comes back to gmail.
However if I click on the link in a desktop browser, it works fine, the email is validated.
I'm having a hard time understanding what it going on. Is there something wrong about how I did things ?

You should write a function to handle your dynamic links, as per the documentation, and this is working for me in an app being used currently:
void handleDynamicLinks() async {
///To bring INTO FOREGROUND FROM DYNAMIC LINK.
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLinkData) async {
await _handleDeepLink(dynamicLinkData);
},
onError: (OnLinkErrorException e) async {
print('DynamicLink Failed: ${e.message}');
return e.message;
},
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
}
// bool _deeplink = true;
_handleDeepLink(PendingDynamicLinkData data) async {
final Uri? deeplink = data.link;
if (deeplink != null) {
print('Handling Deep Link | deepLink: $deeplink');
}
}
and in initState:
#override
void initState() {
handleDynamicLinks();
super.initState();
}
write this logic in your home page. Not in void(main..etc)
But in your first widget after that, and it should work.
Also, be sure to double check your package name, i.e com.example.yourAwesomeApp123, it's what lets the whole system know what app is to be opened when the dynamic link is pressed.

Related

Flutter programmatically building a referral system with deep link

I need to implement a deep link or referral system with my flutter application. The theory is
Singup and Signin will be handled by custom backend and not firebase
After a user signs up to my application he will be able to refer the app to others and if others install the app the referrer will gain some points.
Most work in this process will be handled by our custom backend. What I need is when someone uses my referral code I want that code during his/her signup.
So this is the service layer I created:
class DynamicLinkService {
final dynamicLink = FirebaseDynamicLinks.instance;
handleDynamicLink() async {
await dynamicLink.getInitialLink();
// dynamicLink.onLink(onSuccess: (PendingDynamicLinkData data) async {
// // something
// },
// onError: (OnLinkErrorException e) async {
// // something
// },
// );
}
Future<String> createDynamicLink() async {
User user = Store.instance.getUser();
String userId = user.id;
print("User id = $userId");
final DynamicLinkParameters dynamicLinkParameters = DynamicLinkParameters(
uriPrefix: 'https://shoppydev.page.link',
link: Uri.parse(
'https://shoppydev.page.link/?invitedBy=$userId',
),
androidParameters: AndroidParameters(
packageName: 'co.company.app',
minimumVersion: 0,
),
iosParameters: IOSParameters(
bundleId: 'co.company.app',
minimumVersion: '0.0.1',
),
socialMetaTagParameters: SocialMetaTagParameters(
title: 'Refer A friend',
description: 'Refer and earn points',
),
);
final ShortDynamicLink shortDynamicLink = await dynamicLink.buildShortLink(
dynamicLinkParameters,
);
final Uri dynamicUrl = shortDynamicLink.shortUrl;
print(dynamicUrl.toString());
return dynamicUrl.toString();
}
void handleSuccessfulLinking(PendingDynamicLinkData? data) async {
final Uri? deepLink = data!.link;
print(deepLink.toString());
if (deepLink != null) {
var isRefer = deepLink.toString().contains('invitedBy');
if (isRefer) {
var code = deepLink.toString().split('invitedBy=')[1];
print(code);
if (code != null) {
// code contains the referrer's user id
// signup with the referrer's id
}
}
}
}
}
As you can see I tried to create a unique referral link with the user id for now. But most guides I am following as well as some github repos did something like this for handling dynamic link:
dynamicLink.onLink(onSuccess: (PendingDynamicLinkData data) async {
// something
},
onError: (OnLinkErrorException e) async {
// something
},
);
Which throws: The expression doesn't evaluate to a function, so it can't be invoked.
Other notes that might help:
Inside my app.dart I have:
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
initDynamicLinks(context);
}
#override
Widget build(BuildContext context) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'App Name',
theme: ThemeData(
primarySwatch: Colors.blue,
),
onGenerateRoute: buildRouter,
locale: provider.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
}
/*
Dynamic Links
*/
void initDynamicLinks(BuildContext context) async {
final PendingDynamicLinkData? data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? link = data?.link;
if (link != null) {
Navigator.pushNamed(context, link.path);
}
}
}
Issues I have faced till now:
I still haven't found a solid documentation on how to get the referral code(which is need for rewarding the referrer).
I have already checked out this two posts on stack:
Implementing referral rewards in Flutter
Flutter - How to pass custom arguments in firebase dynamic links for app invite feature?
In short, I want to create a unique refer link with my user id. Share the user id with someone else and when he/she registers to my app I want to get the referral code attached to the link.
Example: https://app.page.link/?invitedBy=$userId
When someone installs and registers I want the userId so I can pass it to the invitedBy property of SignUpRequest.
Edit: I think I didn't clarify my question enough. So I will set it up with an example:
I want an unique referral link on my phone which I can give to my friend John. And once he downloads and registers the app I want to get some reward points.
So when he sends his SignUpRequest to the Backend I want my referral code to go with that request, so the request will look like:
SignUpRequest()
..name = "John Doe",
..email = "john#gmail.com"
..invitedBy = "...my referral code goes here"
All the other validation and point giving process will be done in the BE
Put all of the below code in the App.dart or Splash screen, basically the first screen
initState
#override
void initState() {
super.initState();
_initDynamicLinks();
}
_initDynamicLinks - this is from where the dynamic link will be launched
Future<void> _initDynamicLinks() async {
final PendingDynamicLinkData data = await instance.getInitialLink();
final Uri deepLink = data?.link;
_handleDynamicLink(deepLink);
FirebaseDynamicLinks.instance.onLink.listen((dynamicLink) {
final uri = dynamicLink.link;
_handleDynamicLink(uri);
}).onError((e) {
print('onLinkError');
print(e.message);
});
}
_handleDynamicLink - this is where you handle the link and parse it
void _handleDynamicLink(Uri deepLink) async {
if (deepLink != null) {
final url = deepLink.toString();
var isRefer = url.contains('invitedBy');
if (isRefer) {
var code = url.split('invitedBy=')[1];
print(code);
if (code != null) {
// code contains the referrer's user id
// signup with the referrer's id
}
}
}
}
I think this way will be more clean
first add this widget
class DynamicLinksWidgetHandler extends StatefulWidget {
const DynamicLinksWidgetHandler({
super.key,
required this.child,
});
final Widget child;
#override
State<DynamicLinksWidgetHandler> createState() =>
_DynamicLinksWidgetHandlerState();
}
class _DynamicLinksWidgetHandlerState extends State<DynamicLinksWidgetHandler> {
#override
void initState() {
super.initState();
_initDynamicLinks();
}
// _initDynamicLinks - this is from where the dynamic link will be launched
Future<void> _initDynamicLinks() async {
final data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri? deepLink = data?.link;
_handleDynamicLink(deepLink);
FirebaseDynamicLinks.instance.onLink.listen((dynamicLink) {
final uri = dynamicLink.link;
_handleDynamicLink(uri);
}).onError((e) {
print('onLinkError');
print(e.message);
});
}
// _handleDynamicLink - this is where you handle the link and parse it
void _handleDynamicLink(Uri? deepLink) async {
log('_handleDynamicLink:$deepLink');
final code = deepLink?.queryParameters['invitedby'];
if (code == null) return;
// save code to backend
log(code);
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
and then wrap it on your app widget like this
runApp(
const DynamicLinksWidgetHandler(
child: MyApp(),
),
);

Firebase query Stream with filters not updating when document is added: Flutter

I am working on the chat section of my flutter application. I have a chat_messages collection which contains all of the messages sent by users for all chats in the application. Below is the structure of a chat_messages document:
Here user is the sender of the message. I would like to display the number of unread messages (where the message.seen==false) for a specific user hence i used the query below to get a stram of all messages which where not seen by the user and i listen to that stream for any new messages sent:
unreadMessagesStream = queryChatMessagesRecord(queryBuilder: (query)
{
return query
.where('chat_users', arrayContains: currentUserReference)
.where('user', isNotEqualTo: currentUserReference)
.where('seen', isEqualTo: false);
});
unreadMessagesStream.listen((msgs) {
if (mounted)
setState(() {
unreadMessagesCount = msgs?.length ?? 0;
});
});
Unfortunately, this stream only produces a value once when the app is run, but later on when any new message is sent, new values are not received in the stream and the number of unread messages remain the same.
NB: If I remove the filters and query the whole collection is works perfectly fine.
I give you a snippet of my code to get it done faster: The code juste below is for each user. It's a subcollection of the chat. So a user could have a chatMembre for each chat.
import 'package:cloud_firestore/cloud_firestore.dart';
enum IsDoing { reading, notReading, writing, recording }
class ChatMembre {
final String id;
final DateTime lastReading;
final DateTime lastReceived;
final IsDoing isDoing;
final bool hasSubscribeToTopic;
ChatMembre(
{required this.id,
required this.lastReading,
required this.lastReceived,
required this.isDoing,
required this.hasSubscribeToTopic});
Map<String, dynamic> toMap() {
return {
'id': id,
'lastReading': lastReading == DateTime.now()
? FieldValue.serverTimestamp()
: DateTime.now(),
'lastReceived': lastReceived == DateTime.now()
? FieldValue.serverTimestamp()
: DateTime.now(),
'isDoing':
isDoing.toString().substring(isDoing.toString().indexOf(".") + 1),
'isSubscribeToTopic': hasSubscribeToTopic
};
}
factory ChatMembre.fromMap(Map<String, dynamic>? map) {
if (map == null || map.isEmpty) {
return ChatMembre(
id: '',
lastReading: DateTime.now(),
lastReceived: DateTime.now(),
hasSubscribeToTopic: false,
isDoing: IsDoing.notReading);
}
IsDoing isDoing;
switch (map["isDoing"]) {
case "reading":
isDoing = IsDoing.reading;
break;
case "writing":
isDoing = IsDoing.writing;
break;
case "recording":
isDoing = IsDoing.recording;
break;
default:
isDoing = IsDoing.notReading;
break;
}
return ChatMembre(
id: (map['id'] ?? '') as String,
lastReading:
((map['lastReading'] ?? Timestamp.now()) as Timestamp).toDate(),
lastReceived:
((map['lastReceived'] ?? Timestamp.now()) as Timestamp).toDate(),
isDoing: isDoing,
hasSubscribeToTopic: (map['isSubscribeToTopic'] ?? false) as bool);
}
#override
String toString() {
return 'ChatMembre{id: $id, lastReading: $lastReading, lastReceived: $lastReceived, isDoing: $isDoing, hasSubscribeToTopic: $hasSubscribeToTopic}';
}
}
And under it's to look for the state of the chat page.
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constants/credentials.dart';
import 'package:customer/constants/firestore_path.dart';
import 'package:customer/domain/repositories/my_chat_repository.dart';
import 'package:customer/services/firestore_service.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;
class ChatRoomLifeCycle extends StatefulWidget {
final Widget child;
final MyChatRepository chatRepo;
final String chatId;
final String? token;
final String? idTo;
final Timestamp? lastReceivedOfFriend;
const ChatRoomLifeCycle(
{Key? key,
required this.chatId,
required this.chatRepo,
required this.child,
this.token,
this.idTo,
this.lastReceivedOfFriend})
: super(key: key);
#override
_ChatRoomLifeCycleState createState() => _ChatRoomLifeCycleState();
}
class _ChatRoomLifeCycleState extends State<ChatRoomLifeCycle>
with WidgetsBindingObserver {
late GlobalKey<AnimatedListState> listKey;
bool hasSentFcm = false;
#override
void initState() {
super.initState();
sendPushMessage();
WidgetsBinding.instance!.addObserver(this);
widget.chatRepo.setIsReading();
}
#override
void dispose() {
widget.chatRepo.setIsNotReading(isFromDispose: true);
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.paused:
widget.chatRepo.setIsNotReading();
break;
case AppLifecycleState.resumed:
widget.chatRepo.setIsReading();
break;
case AppLifecycleState.inactive:
widget.chatRepo.setIsNotReading();
break;
case AppLifecycleState.detached:
widget.chatRepo.setIsNotReading();
break;
}
}
Future<void> sendPushMessage() async {
if (hasSentFcm || widget.idTo == null || widget.token == null) {
return;
}
FirestoreService.instance.updateData(
path: MyPath.myUserStatus(uid: widget.idTo!), data: {'isLogin': false});
try {
await http
.post(
Uri.parse('https://fcm.googleapis.com/fcm/send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$serverToken',
},
body: constructFCMPayload(widget.token!),
)
.catchError((onError) {});
hasSentFcm = true;
} catch (e) {
Fluttertoast.showToast(msg: e.toString());
}
}
// Crude counter to make messages unique
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String token) {
return jsonEncode(<String, dynamic>{
'data': <String, dynamic>{
'test': 'check online',
'chatId': widget.chatId,
'idTo': widget.idTo
},
'to': token,
});
}
}

Flutter GetX package Firebase Auth and FireStore "null check operator used on a null value"

First of all, I'm asking a question for the first time, I'm sorry if I'm asking incorrectly and incompletely, I will give you the information you want immediately.
Hey, I was following the tutorial in this video but I am getting an error like this.
video tutorial: https://www.youtube.com/watch?v=BiV0DcXgk58&t=314s&ab_channel=TadasPetra
Error is this:
Error simulator picture
I am getting this error, but the account is created in firebase Auth and the data I want is saved in FireStore database.
Flutter version: 2.2.3
pubspec.yaml
cloud_firestore: ^0.13.5
firebase_core: ^0.4.5
firebase_storage: ^3.1.6
get: ^4.3.8
Error debug console text
[GETX] Instance "AuthController" has been created
[GETX] Instance "AuthController" has been initialized
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
════════ Exception caught by widgets library ═══════════════════════════════════
The following _CastError was thrown building Root:
Null check operator used on a null value
The relevant error-causing widget was
Root
lib/main.dart:20
When the exception was thrown, this was the stack
#0 GetXState.initState
package:get/…/rx_flutter/rx_getx_widget.dart:78
#1 StatefulElement._firstBuild
package:flutter/…/widgets/framework.dart:4711
#2 ComponentElement.mount
package:flutter/…/widgets/framework.dart:4548
#3 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3611
#4 Element.updateChild
package:flutter/…/widgets/framework.dart:3363
All files
Main.dart code
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
initialBinding: AuthBinding(),
theme: ThemeData(scaffoldBackgroundColor: bgColor),
home: Root(),
);
}
}
Root.dart code
class Root extends GetWidget<AuthController> {
#override
Widget build(BuildContext context) {
return GetX(
initState: (_) async {
Get.put<UserController>(UserController());
},
builder: (_) {
if (Get.find().user?.uid != null) {
return SMBottomNavBar();
} else {
return SignInScreen();
}
},
);
}
}
userController.dart
class UserController extends GetxController {
Rx<UserModel> _userModel = UserModel().obs;
UserModel get user => _userModel.value;
set user(UserModel value) => this._userModel.value = value;
void clear() {
_userModel.value = UserModel();
}
}
authController.dart
class AuthController extends GetxController {
FirebaseAuth _auth = FirebaseAuth.instance;
Rxn<FirebaseUser> _firebaseUser = Rxn<FirebaseUser>();
FirebaseUser get user => _firebaseUser.value;
#override
onInit() {
_firebaseUser.bindStream(_auth.onAuthStateChanged);
}
void createUserAccount(String userName, String email, String password) async {
try {
AuthResult _authResult = await _auth.createUserWithEmailAndPassword(
email: email.trim(), password: password);
//create user in database.dart
UserModel _user = UserModel(
userID: _authResult.user.uid, userName: userName, userEmail: email
);
if (await DatabaseServices().createNewUser(_user)) {
Get.find<UserController>().user = _user;
Get.back();
}
} catch (e) {
Get.snackbar(
"Error creating Account",
e.message,
);
}
}
void logInUser(String email, String password) async {
try {
AuthResult _authResult = await _auth.signInWithEmailAndPassword(
email: email.trim(), password: password);
Get.find<UserController>().user =
await DatabaseServices().getUser(_authResult.user.uid);
} catch (e) {
Get.snackbar(
"Error signing in",
e.message,
);
}
}
void logOutUser() async {
try {
await _auth.signOut();
Get.find<UserController>().clear();
} catch (e) {
Get.snackbar(
"Error signing out",
e.message,
);
}
}
}
authBinding.dart
class AuthBinding extends Bindings {
#override
void dependencies() {
Get.put<AuthController>(AuthController(), permanent: true);
}
}
user.dart(Model)
class UserModel {
String userID;
String userEmail;
String userName;
String userDisplayName;
String userProfilePhotoURL;
String userBioText;
int userScore;
UserModel(
{this.userID,
this.userEmail,
this.userName,
this.userDisplayName,
this.userProfilePhotoURL,
this.userBioText,
this.userScore});
UserModel.fromDocumentSnapshot(DocumentSnapshot doc) {
userID = doc.documentID;
userEmail = doc['userEmail'];
userName = doc['userName'];
userDisplayName = doc['userDisplayName'];
userProfilePhotoURL = doc['userProfilePhotoURL'];
userBioText = doc['userBioText'];
userScore = doc['userScore'];
}
}
You need to insert the type into your GetX widget.
return GetX<AuthController>( // add AuthController here
initState: (_) async {
Get.put<UserController>(UserController());
},
...
Your other issue is here
if (Get.find().user?.uid != null) {
return SMBottomNavBar();
}
If you use Get.find, same thing, always provide the type.
Get.find<AuthController>().user?.uid
But since you're using GetWidget<AuthController> you can do this
if (controller.user?.uid != null) {
return SMBottomNavBar();
}
...

How to initialize firebase crashlytics in Flutter?

I have already implemented firebase crashlytics to my Flutter project through dependency in pubspec.yaml and also in Gradle files and able to see the crashlytics dashboard in the firebase console.
Now my question is how can I initialize crashlytics in main.dart file and how to write log and catch error or crash for a particular page(say Home page).
I have tried from this link: https://pub.dev/packages/firebase_crashlytics/example
main.dart
final _kShouldTestAsyncErrorOnInit = false;
// Toggle this for testing Crashlytics in your app locally.
final _kTestingCrashlytics = true;
main() {
WidgetsFlutterBinding.ensureInitialized();
runZonedGuarded(() {
runApp(MyApp());
}, (error, stackTrace) {
print('runZonedGuarded: Caught error in my root zone.');
FirebaseCrashlytics.instance.recordError(error, stackTrace);
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "My App",
debugShowCheckedModeBanner: false,
home: MainPage(),
theme: ThemeData(
accentColor: Colors.blue
),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Future<void> _initializeFlutterFireFuture;
Future<void> _testAsyncErrorOnInit() async {
Future<void>.delayed(const Duration(seconds: 2), () {
final List<int> list = <int>[];
print(list[100]);
});
}
// Define an async function to initialize FlutterFire
Future<void> _initializeFlutterFire() async {
// Wait for Firebase to initialize
await Firebase.initializeApp();
if (_kTestingCrashlytics) {
// Force enable crashlytics collection enabled if we're testing it.
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
} else {
// Else only enable it in non-debug builds.
// You could additionally extend this to allow users to opt-in.
await FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(!kDebugMode);
}
// Pass all uncaught errors to Crashlytics.
Function originalOnError = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails errorDetails) async {
await FirebaseCrashlytics.instance.recordFlutterError(errorDetails);
// Forward to original handler.
originalOnError(errorDetails);
};
if (_kShouldTestAsyncErrorOnInit) {
await _testAsyncErrorOnInit();
}
}
#override
void initState() {
super.initState();
_initializeFlutterFireFuture = _initializeFlutterFire();
Firebase.initializeApp().whenComplete(() {
print("completed");
setState(() {});
});
checkLoginStatus();
}
}
Is it correct or any otherway to initialize crashlytics in flutter?
If i have to check whether there is any crash in HomePage, then how can i get that crash from home page and will show it in firbase crashlytics?
Yes, your configuration of the crashlytics is ok.
If you are using Firebase Auth, you can add the following code in order to have the ability to track crashes specific to a user:
FirebaseAuth.instance.onAuthStateChanged.listen((firebaseUser) {
if (firebaseUser != null && firebaseUser?.email != null) {
Crashlytics.instance.setUserEmail(firebaseUser.email);
}
if (firebaseUser != null && firebaseUser?.uid != null) {
Crashlytics.instance.setUserIdentifier(firebaseUser.uid);
}
if (firebaseUser != null && firebaseUser?.displayName != null) {
Crashlytics.instance.setUserName(firebaseUser.displayName);
}
});
Also, don't forget to track specific exceptions in catch of the try-catch block like this:
try {
//some code here...
} catch (e, s) {
Crashlytics.instance.recordError(e, s, context: "an error occured: uid:$uid");
}

How does firebase passwordless authentication work with dynamic link for a flutter app?

When I click on verification link from my email , it opens my app running in background but didChangeAppLifecycleState method returns data as null and deepLink as set in firebase instead of the whole emailLink from my email, resulting in SignInWithEmailAndLink to fail as its supposed to match email address entered in widget from the link recieved.
Here's the code taken from this article https://medium.com/firebase-developers/dive-into-firebase-auth-on-flutter-email-and-link-sign-in-e51603eb08f8 :-
1.didChangeAppLifecycleState method
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
if (state == AppLifecycleState.resumed) {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
if( data?.link != null ) {
handleLink(data?.link);
}
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink `enter code here`= dynamicLink?.link;
handleLink(deepLink);
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
}
}
handleLink method
void handleLink(Uri link) async {
if (link != null) {
final User user = (await _auth.signInWithEmailAndLink(
email: _userEmail,
link: link.toString(),
))
.user;
if (user != null) {
setState(() {
_userID = user.uid;
_success = true;
});
} else {
setState(() {
_success = false;
});
}
} else {
setState(() {
_success = false;
});
}
setState(() {});
}
main method (initializing firebase)
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
Note: - Deeplink is a concept still new to me in flutter, so I have set it randomly in firebase. My register page has a WidgetsBindingObserver to help resume app lifecycle state. My signup and sign in code is in an email widget and I'm not using forms to validate my textfields.
it seems that after you signin with email link the firebase instance has been not updated before checking auth.SigninWithEMailLink() you need to update it in firebase instance like this
var user= await auth.currentUser();
await user.reload();
user=await auth.currentUser();
if still you are facing problem please provide some more code to let me understand your issue properly

Resources