Reputation: 91
I am using deeplinks to reset password. User will get an email from clicking reset button from email app should open. But instead it opening to browser. I am using app_links: ^6.3.3
package and I have my on routes class instead of GoRouter.
I have assetlinks.json
file uploaded in server and necessary parameters on manifest file.
class DeepLinkService {
// Handles incoming links while the app is already started (foreground or background).
static void handleIncomingLinks() {
log('called');
AppLinks().uriLinkStream.listen((Uri? uri) async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (uri != null) {
debugPrint('Incoming link: $uri');
_handleIncomingLink(uri);
}
});
}, onError: (Object err) {
FirebaseCrashlytics.instance.recordError(err, null);
CustomLogger.error('Error handling incoming link: $err');
});
}
// Handles the initial URI when the app is first started.
static Future<void> handleInitialUri() async {
try {
final uri = await AppLinks().getInitialLink();
log('$uri');
if (uri != null) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
debugPrint('Initial URI: $uri');
_handleIncomingLink(uri);
});
}
} on PlatformException catch (e, st) {
FirebaseCrashlytics.instance.recordError(e, st);
CustomLogger.error('Failed to get initial URI: $e');
} on FormatException catch (e, st) {
FirebaseCrashlytics.instance.recordError(e, st);
CustomLogger.error('Malformed initial URI: $e');
} catch (e, st) {
FirebaseCrashlytics.instance.recordError(e, st);
CustomLogger.error('Unknown error: $e');
}
}
// Parses the incoming URI and performs navigation.
static void _handleIncomingLink(Uri uri) {
try {
final token = uri.queryParameters['token'];
if (token != null && token.isNotEmpty) {
if (!navigatorKey.currentState!.canPop()) {
navigatorKey.currentState!.pushNamed(
AppRoutesNames.resetPasswordView,
arguments: {'token': token},
);
}
} else {
CustomLogger.error('Token is missing or empty in the URI.');
}
} catch (e, st) {
FirebaseCrashlytics.instance.recordError(e, st);
CustomLogger.error('Error handling incoming link: $e');
}
}
>! StartUpView will take user to appropriate screens based on login status
class StartupView extends ConsumerStatefulWidget {
const StartupView({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _StartupViewState();
}
class _StartupViewState extends ConsumerState<StartupView> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await ref.read(startupVMProvider).initialize();
});
}
@override
Widget build(BuildContext context) {
DeepLinkService.handleIncomingLinks();
DeepLinkService.handleInitialUri();
final startupVM = ref.watch(startupVMProvider);
if (!startupVM.isAuthStateLoaded) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
return StreamBuilder(
stream: ref.watch(authServiceProvider).userState,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: ErrorView(),
);
} else if (snapshot.hasData) {
if (ref.watch(authServiceProvider).isLogged) {
return const LocalAuthView();
} else if (ref.watch(authServiceProvider).isLogged &&
!ref.watch(authServiceProvider).hasAcceptedTermsAndConditions) {
return PrivacyPolicyView();
} else {
return const WelcomeView();
}
} else {
return const SizedBox();
}
},
);
}
}
>! For navigation
class AppRoutesNames {
static const String localAuthView = '/localAuthView';
static const String deactivePage = '/deactivePage';
static const String invitationView = '/invitationView';
static const String homeView = '/homeView';
static const String detailsView = '/detailView';
static const String errorView = '/errorView';
static const String registrationView = '/registrationView';
static const String loginView = '/loginView';
static const String emailOtpView = '/emailOtpView';
static const String resetPasswordView = '/resetPasswordView';
static const String recoverPasswordView = '/recoverPasswordView';
static const String welcomeView = '/welcomeView';
static const String allDoneSuccessView = "/allDoneSuccessView";
static const String resetPasscodeView = '/resetPasscodeView';
static const String noInternetView = '/noInternetView';
}
class AppRouter {
Route? onGenerateRoute(RouteSettings settings) {
switch (settings.name) {
case AppRoutesNames.homeView:
return MaterialPageRoute(
builder: (context) => const HomeView(),
);
case AppRoutesNames.resetPasswordView:
final args = settings.arguments as Map<String, dynamic>;
return MaterialPageRoute(
builder: (context) => ResetPasswordView(token: args['token']));
default:
return null;
}
}
}
//manifest -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host=hostName />
<data android:host=hostName" />
<data android:scheme="https" />
</intent-filter>
Upvotes: 2
Views: 48