Flutter programmatically building a referral system with deep link - firebase

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(),
),
);

Related

How to update boolean value in Firestore from a switch tile?

My goal is to have a userNotifications collection in Firestore that is used to track user notification preferences. The user can toggle between true and false based on tapping a switch tile. With the code below, Firestore is immediately and correctly updating the boolean value based on user interaction and the user interface is updating and reflecting the changes. If the user logs out and logs back in, the boolean value is accurately reflected in the user interface.
Before I apply this approach more broadly in the code I was hoping that someone can comment and let me know if my approach to updating the boolean value in Firestore is valid or point me in a better direction so I can improve my code. Links to SO posts or documentation are fine as I am more than willing to read and learn. Thanks in advance for any help.
class NotificationsMessagesTile extends StatefulWidget {
const NotificationsMessagesTile({
Key? key,
}) : super(key: key);
#override
State<NotificationsMessagesTile> createState() =>
_NotificationsMessagesTileState();
}
class _NotificationsMessagesTileState extends State<NotificationsMessagesTile> {
bool notificationsActive = false;
final String? currentSignedInUserID = Auth().currentUser?.uid;
Future<void> updateNotifications() async {
if (!notificationsActive) {
notificationsActive = true;
FirebaseFirestore.instance
.collection('userNotifications')
.doc(currentSignedInUserID)
.update({
'messages': false,
});
} else {
notificationsActive = false;
FirebaseFirestore.instance
.collection('userNotifications')
.doc(currentSignedInUserID)
.update({
'messages': true,
});
}
setState(() {});
}
#override
Widget build(BuildContext context) {
return SwitchListTileSliver(
icon: Provider.of<NotificationsPageProvider>(context).areMessagesTurnedOn
? Icons.notifications_active
: Icons.notifications_off,
onChanged: (bool value) {
final provider = Provider.of<NotificationsPageProvider>(
context,
listen: false,
);
provider.updateMessagesSettings(isOn: value);
updateNotifications();
},
subTitle:
Provider.of<NotificationsPageProvider>(context).areMessagesTurnedOn
? const Text(
SettingsPageString.messagesOn,
)
: const Text(
SettingsPageString.messagesOff,
),
title: SettingsPageString.messages,
value:
Provider.of<NotificationsPageProvider>(context).areMessagesTurnedOn,
);
}
}
You could improve your updateNotifications() function to not have duplicate code:
Future<void> updateNotifications() async {
await FirebaseFirestore.instance
.collection('userNotifications')
.doc(currentSignedInUserID)
.update({
'messages': notificationsActive,
});
setState(() {
notificationsActive = !notificationsActive;
});
}
And also I would suggest to you to listen to your Firestore collection and update UI on change. You can look up on how to do that here.

Handling different scenarios for app redirection using Firebase Dynamic Links

I am trying to create a streaming app.
I am integrating Firebase Dynamic links, so hosts can share a link to join their session.
But I am kinda lost on how to implement it, and I would like to know your insights about this.
So let me start with the structure, this is the main pages:
MainApp
Authenticate
LoginPage
HomePage
Profile
JoinPage
This is how I setup my app, they are all wrap to Authenticate widget, this widget determines if user should be redirected to login or the home page. Pretty simple eh. :)
Then here comes the Dynamic links.
My dynamic links has a query params on it to determine which session to join. It looks like this:
https://my.app.link/join-session?sessionId=\<some random keys>
And this is what I want to handle it.
If (user is not logged in ) {
// save the sessionId to a singleton (or somewhere that persists along the app lifecycle, I use Provider here)
// redirects user to login page
// user logged in
// upon going to home page, will retrieve the saved sessionId and redirect user to the session using the sessionId
} else {
// retrieve sessionId
// redirect user to the session using the sessionId
}
This is how my code looks:
MainApp.dart logic
....
if (state == PageLoadState.DONE) {
return MultiProvider(
providers: [
Provider<SampleAppAuthProvider>(
create: (_) => SampleAppAuthProvider(FirebaseAuth.instance),
),
Provider<SampleAppConfigProvider>(
create: (_) => SampleAppConfigProvider(sharedPrefs)
),
StreamProvider(
initialData: null,
create: (context) => context.read<SampleAppAuthProvider>().authState,
),
Provider<DynamicLinkService>(create: (_) => DynamicLinkService(instance: FirebaseDynamicLinks.instance)),
ChangeNotifierProvider(create: (_) => UsersApi(repo: Repository('users')), lazy: true),
ChangeNotifierProvider(create: (_) => SessionsApi(repo: Repository('sessions')), lazy: true),
ChangeNotifierProvider(create: (_) => MessagesApi(repo: Repository('messages')), lazy: true),
],
child: MaterialApp(
title: 'SampleApp!',
theme: defaultTheme(),
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
onGenerateRoute: router.generateRoute,
home: Authenticate(),
),
);
}
......
And this is my Authenticate Page:
class Authenticate extends StatelessWidget {
#override
Widget build(BuildContext context) {
context.read<DynamicLinkService>().handleDynamicLinks();
final firebaseUser = context.read<User>();
if (firebaseUser != null) {
return HomePage();
}
return LoginPage();
}
}
I have also create a separate service for handling the dynamic links:
DynamicLinkService.dart
class DynamicLinkService {
DynamicLinkService({#required this.instance});
final FirebaseDynamicLinks instance;
String _sessionIdToJoin = '';
get sessionIdToJoin => _sessionIdToJoin;
void clearSessionId () => _sessionIdToJoin = '';
Future handleDynamicLinks() async {
final PendingDynamicLinkData data =
await instance.getInitialLink();
_handleDeepLink(data);
instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
_handleDeepLink(dynamicLink);
}, onError: (OnLinkErrorException e) async {
print('Link Failed: ${e.message}');
});
}
void _handleDeepLink(PendingDynamicLinkData data) {
final Uri deepLink = data?.link;
if (deepLink != null) {
print('_handleDeepLink | deeplink: $deepLink');
bool isJoiningSession = deepLink.pathSegments.contains('session');
if(isJoiningSession) {
String sessionId = deepLink.queryParameters['sessionId'];
if (sessionId != null) {
_sessionIdToJoin = sessionId;
}
}
}
}
}
I also have another UtilService that generates dynamic links:
Utils.dart
class Utils {
....
static Future<String> generateLink(String sessionId) async {
final DynamicLinkParameters parameters = DynamicLinkParameters(
uriPrefix: AppConfig.dynamicLinkBaseUrl,
link: Uri.parse('${AppConfig.dynamicLinkBaseUrl}/join-session?sessionId=$sessionId'),
dynamicLinkParametersOptions: DynamicLinkParametersOptions(
shortDynamicLinkPathLength: ShortDynamicLinkPathLength.unguessable
),
androidParameters: AndroidParameters(
packageName: 'com.sample.app',
minimumVersion: 0,
),
);
final Uri dynamicUrl = await parameters.buildUrl();
final ShortDynamicLink shortenedLink =
await DynamicLinkParameters.shortenUrl(
dynamicUrl,
DynamicLinkParametersOptions(
shortDynamicLinkPathLength: ShortDynamicLinkPathLength.unguessable),
);
final Uri shortUrl = shortenedLink.shortUrl;
return AppConfig.dynamicLinkBaseUrl + shortUrl.path;
}
...
}
On my home page I have this method that will should be called whenever user lands in the homepage:
HomePage.dart:
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
....
_navigateToDestination() {
DynamicLinkService _service = context.select((DynamicLinkService service) => service);
String sessionToJoin = _service.sessionIdToJoin;
User user = context.read<User>();
if(sessionToJoin != '') {
_service.clearSessionId();
Navigator.pushNamed(context,
AppRoutes.joinStream,
arguments: StreamLiveArguments(
channelName: sessionToJoin,
user: AppUtil.getName(user),
role: ClientRole.Audience
)
);
}
}
#override
initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_navigateToDestination();
});
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(final AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_navigateToDestination();
}
}
....
}
The issue on this implementation is that sometimes it works and sometimes it does not work and I am not sure why. :(
Should I just use a simple class instead of a provider? If yes, how can I persists the sessionId?
Other Scenarios that I want to handle is when user is in different page like the Profile Page.
At this point, user can minimize the app and can click the dynamic link from other sites or from messenger.
When the app resumes, it will open in the ProfilePage where it doesn't have handling for dynamic links (only HomePage and LoginPage has handling on it). How can I remedy this? Should I add handling of dynamic links to all my pages? Or perhaps can I create a dynamic link that can redirect to a certain page in my app? If yes, how can I do that?
I am kinda new in implementing the dynamic links (as well as Providers) and would like your opinion on this issue.Thanks in advance and I hope someone can help me or give me some insights on best practices on how to do this.

Flutter : Dynamic Link not launching the app

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.

Flutter - Firebase Cloud Messaging Navigation in onLaunch doesn't work

I am building an app which receives push notifications using FCM.
I want to route to a specific screen when a notification is clicked (for example, the user's profile).
On Android, it works perfectly fine when the app is just closed (and not "killed"), but when the app is terminated ("killed") it is not working.
On iOS, it doesn't work at all.
I am implementing it life this:
NotificationsHandler:
class NotificationsHandler {
static final NotificationsHandler instance = NotificationsHandler();
final _fcm = FirebaseMessaging();
void onBackgroundNotificationRecevied({Function onReceived}) {
_fcm.configure(
onResume: (message) => onReceived(message),
onLaunch: (message) => onReceived(message),
);
}
}
myMainScreen's initState:
#override
void initState() {
NotificationsHandler.instance.onBackgroundNotificationRecevied(
onReceived: (message) async {
final userId = message['data']['userId'];
final user = this.users.firstWhere((currentUser) => currentUser.id == userId);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfileScreen(
user,
),
),
);
}
);
super.initState();
}
Code for sending the notifications (through an external React admin panel):
const payload = {
notification: {
title: `myTitle`,
body: `My message`,
sound: "default",
badge: "1",
click_action: "FLUTTER_NOTIFICATION_CLICK",
},
data: {
click_action: 'FLUTTER_NOTIFICATION_CLICK',
userId: myUserId,
},
};
const options = {
priority: 'high',
timeToLive: 60 * 60 * 24
};
admin.messaging().sendToTopic('myTopic', payload, options);
Does anyone know why it isn't working?
Thank you!
You can try to use getInitialMessage instead of onLaunch. I believe this will do what you want as documentation indicated the following lines:
This should be used to determine whether specific notification interaction should open the app with a specific purpose (e.g. opening a chat message, specific screen etc).
#override
void initState() {
super.initState();
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage message) {
if (message != null) {
Navigator.pushNamed(context, '/message', arguments: MessageArguments(message, true));
}
});
}
I assume that you're using firebase_messaging package.
iOS
If you're testing it on simulator, it won't work. It's stated in the documentation that:
FCM via APNs does not work on iOS Simulators. To receive messages & notifications a real device is required.
Android
On Android, if the user force quits the app from device settings, it must be manually reopened again for messages to start working.
More info here.
Based on my experience, I remember that onLaunch Callback function fires right after execute main function, even before the initstate method.
What I did was locate service class using service locator(e.g get_it) at main function before runApp() then onLaunch Callback set initial configuration so that your App can use it's value.
For example
final getIt = GetIt.instance;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
getIt.registerSingleton<Configurator>(Configurator());///start configuration service
FirebaseMessagingService.initialise()///start firebase messaging service
runApp();
}
...
class FirebaseMessagingService {
final FirebaseMessaging _fcm;
FirebaseMessagingService.initialise() : _fcm = FirebaseMessaging() {
if (Platform.isIOS) {
_fcm.requestNotificationPermissions(IosNotificationSettings());
}
_fcm.configure(
...
onLaunch: _launchMessageHandler,
);
}
}
//top-level function or static method
_launchMessageHandler(Map<String, dynamic> message) async {
//some parsing logic
...
getIt<Configurator>().setInitialConfig(parsed_data);
}
...
//then
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final config = getIt<Configurator>().config;
//do something
}};
You will have to implement those whole settings but it's flow is like above roughly.
I assume your trouble is more towards navigating to another screen upon clicking the notification.
If that is the case create a class for routing.
an example would be as below:
class Navigator{
GlobalKey<NavigatorState> _navigator;
/// Singleton getter
static Navigator get instance => _instance ??= Navigator._();
/// Singleton Holder
static Navigator _instance;
/// Private Constructor
Navigator._() {
_navigator = GlobalKey<NavigatorState>();
}
GlobalKey<NavigatorState> get navigatorKey => _navigator;
Future<dynamic> navigateTo(String routeName, [dynamic arguments]) =>
navigatorKey.currentState.pushNamed(routeName, arguments: arguments);
Now comes the screen/pages
class CustomRoutes {
const CustomRoutes._();
factory CustomRoutes() => CustomRoutes._();
static const String HomeRoute = 'HomeRoute';
...
...
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case CustomRoutes.HomeRoute:
return MaterialPageRoute(builder: (_) => HomePage());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(child: Text('No path for ${settings.name}'))));
}
}
}
So if u wish to go to HomePage you can just invoke
await Navigator.instance.navigateTo(CustomRoutes.HomeRoute, someArguments)
Do remember to register the globalkey to your materialapp
MaterialApp(
...
...
navigatorKey: Navigator.instance.navigatorKey
...);

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");
}

Resources