i was doing deep linking for my flutter app. The dependency i am using is AppLinks. Everything is working perfectly fine for iOS but for android, it is only working when the app is running in the background. If we remove the app from the background, it's not getting opened or say not working. I am seeking a solution for that.
This is my flutter code below :-
`Future<void> initDeepLinks() async {
_appLinks = AppLinks();
final appLink = await _appLinks.getInitialAppLink();
if (appLink != null) {
refresh();
openAppLink(appLink);
print('getInitialAppLink: $appLink');
}
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
print('onAppLink: $uri');
update();
openAppLink(uri);
});
}
void openAppLink(Uri uri) {
if (uri.path.contains('activeOrder')) {
onBottomNavItemPressed(3);
Get.find<OrdersController>().tabController?.index = 0;
}
if (uri.path.contains('request')) {
onBottomNavItemPressed(3);
Get.find<OrdersController>().tabController?.index = 3;
}
if (uri.path.contains('orderReview')) {
Get.to(() => OrderReviewView());
}
if (uri.path.contains('payoutHistory')) {
Get.to(() => TransactionHistory());
}
if (uri.path.contains('payoutDetails')) {
var transactionDetailsController =
Get.put(TransactionHistoryController());
// print(uri.queryParameters['transaction_id']);
transactionDetailsController.getPaymentDataDetails(
trxId: uri.queryParameters['transaction_id'] ?? "");
Get.to(() => PayoutHistoryDetailPage());
}
}
`
This is the code for the same and working fine for iOS, but it's not working for Android.
have you added the deeplink configuration to the AndroidManifest.xml
?
this is the way i'm using deeplink in my app and it works fine.
first of all I use uni_links package:
and for android in manifest i add the code below in activity tag:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
"....the codes ...."
<!-- Deep Links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="exampleapp"
android:host="example"/>
</intent-filter>
</activity>
as you see the link that opens your app should be like this, for example you are increasing your wallet and after payment the link is :
exampleapp://example/payment?success
see the code below how i use to back to the app:
bool _initialURILinkHandledIncreaseBalance = false;
class IncreaseBalanceModal extends StatefulWidget {
const IncreaseBalanceModal({Key? key}) : super(key: key);
@override
State<IncreaseBalanceModal> createState() => _IncreaseBalanceModalState();
}
class _IncreaseBalanceModalState extends State<IncreaseBalanceModal> {
StreamSubscription? _streamSubscription;
TextEditingController amountTextController =
MaskedTextController(mask: '0', length: 50);
Future<void> _initURIHandler() async {
if (!_initialURILinkHandledIncreaseBalance) {
_initialURILinkHandledIncreaseBalance = true;
try {
final initialURI = await getInitialUri();
// Use the initialURI and warn the user if it is not correct,
// but keep in mind it could be `null`.
if (initialURI != null) {
debugPrint("Initial URI received $initialURI");
if (!mounted) {
return;
}
} else {
debugPrint("Null Initial URI received");
}
} on PlatformException {
// Platform messages may fail, so we use a try/catch PlatformException.
// Handle exception by warning the user their action did not succeed
debugPrint("Failed to receive initial uri");
} on FormatException catch (err) {
if (!mounted) {
return;
}
debugPrint('Malformed Initial URI received $err');
}
}
}
/// Handle incoming links - the ones that the app will receive from the OS
/// while already started.
void _incomingLinkHandler() {
if (!kIsWeb) {
// It will handle app links while the app is already started - be it in
// the foreground or in the background.
_streamSubscription = uriLinkStream.listen((Uri? uri) {
if (!mounted) {
return;
}
debugPrint('Received URI hello we are home: $uri');
var query = uri!.query;
if(query == 'failed'){
locator<NavigationService>().goBack();
snackBar(S.of(context).transactionWasUnsuccessfulText, context,color: brandMainColor);
}
else if(query == 'success'){
onTapBackToApp();
}
}, onError: (Object err) {
if (!mounted) {
return;
}
debugPrint('Error occurred: $err');
});
}
}
@override
void initState() {
super.initState();
_initURIHandler();
_incomingLinkHandler();
}
@override
void dispose() {
amountTextController.dispose();
if (!kIsWeb) _streamSubscription!.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
return Consumer<UserProvider>(
builder: (context,userProvider, child) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomAppBar(
title: S.of(context).increaseBalanceText,
hasBackButton: false,
color: primaryDark,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.04),
child: Column(
children: [
SizedBox(
height: width * 0.04,
),
TextFormFieldItem(
labelText: amountLabelText,
textController: amountTextController,
minHeight: width * 0.1333,
onChanged: (s) {
setState(() {});
}
),
],
),
),
Padding(
padding: EdgeInsets.all(width * 0.04),
child: RectAngleButton(
state: ViewState.ready,
nameOfButton: S.of(context).submitText,
color: amountTextController.text.trim().isNotEmpty
? brandMainColor
: secondaryDark,
height: width * 0.1493,
width: width,
onTap: amountTextController.text.trim().isNotEmpty
? () async {
var url = await UserService.createPaymentByUser(
Provider.of(context, listen: false),
Provider.of(context, listen: false),
URLs.connectToGateMutation(), {
"amount" : int.parse(amountTextController.text)
}
);
if (url.isNotEmpty) {
_launchURLBrowser(url);
} else {
snackBar(
S
.of(context)
.somethingWentWrongPleaseTryAgainText,
context);
}
}
: null,
),
),
],
)
);
});
}
void onTapBackToApp() async {
await UserService.getUser(Provider.of(context,listen: false), Provider.of(context,listen: false),Provider.of(context, listen: false),
Provider.of(context,listen: false), URLs.meQuery(),false);
locator<NavigationService>().goBack();
snackBar(S.of(context).transactionWasSuccessfulText, context,color: greenColor);
}
_launchURLBrowser(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: !kIsWeb
? defaultTargetPlatform == TargetPlatform.iOS
? false
: null
: null,
webOnlyWindowName: kIsWeb ? '_self' : '_blank');
} else {
throw 'Could not launch $url';
}
}
}
even if you close the app, the deeplink will open it again, and you can manage the navigation by the query of your link like what:
from this link exampleapp://example/payment?success
you can understand that it is coming to the app because of payment:
var type = uri.pathSegments.first;//payment
and the type
tells you to navigate to payment page from the beginning.
Edit: these explanation that I've said before is completely true, and when I checked your Manifest code I saw that you have multiple configs for your deeplink.
<!-- Deep linking -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<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:scheme="http" android:host="host.com" android:pathPrefix="/orderReview"/>
<data android:scheme="https" />
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/activeOrder"/>
<data android:scheme="https" />
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/request"/>
<data android:scheme="https" />
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/payoutHistory"/>
<data android:scheme="https" />
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/payoutDetails"/>
<data android:scheme="https" />
</intent-filter>
<!-- <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:scheme="http" android:host="host.com" android:pathPrefix="/request"/>
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/activeOrder"/>
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/payoutHistory"/>
</intent-filter>
<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:scheme="http" android:host="host.com" android:pathPrefix="/payoutDetails"/>
</intent-filter>
<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:scheme="https" android:host="host.com" android:pathPrefix="/orderReview"/>
</intent-filter>
<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:scheme="https" android:host="host.com" android:pathPrefix="/request"/>
</intent-filter>
<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:scheme="https" android:host="host.com" android:pathPrefix="/activeOrder"/>
</intent-filter>
<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:scheme="https" android:host="host.com" android:pathPrefix="/payoutHistory"/>
</intent-filter>
<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:scheme="https" android:host="host.com" android:pathPrefix="/payoutDetails"/>
</intent-filter> -->
it is not recommended to do add all the configs with all the pathPrefix in your app, if you have just one pathPrefix it will be ok to do that but please delete those lines that you've added before and please add these lines below instead:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="https"
android:host="host.app"/>
</intent-filter>
you should use main config in your manifest and check your pathPrefix
in your Flutter code.
happy coding...