Reputation: 21965
The getInitialLink() continues to give a valid dynamic link after the first retrieval. This leads to a loop where a user is continuously forced into a page associated with the deep link.
Running `flutter doctor --verbose` produced the below results.
[✓] Flutter (Channel stable, 1.20.4, on Mac OS X 10.15.6 19G2021, locale en-US)
• Flutter version 1.20.4 at /Users/sjsam/Documents/Developement/flutter
• Framework revision fba99f6cf9 (5 days ago), 2020-09-14 15:32:52 -0700
• Engine revision d1bc06f032
• Dart version 2.9.2
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
• Android SDK at /Users/sjsam/Library/Android/sdk
• Platform android-29, build-tools 28.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 11.7)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.7, Build version 11E801a
• CocoaPods version 1.9.0
[✓] Android Studio (version 3.6)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 44.0.2
• Dart plugin version 192.7761
• Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
[✓] Connected device (2 available)
• AOSP on IA Emulator (mobile) • emulator-5554 • android-x86 • Android 9 (API 28) (emulator) # No issues here
• Android SDK built for x86 (mobile) • emulator-5556 • android-x86 • Android 8.0.0 (API 26) (emulator) # Issue happens here
• No issues found!
My code sample (which is mostly of a copy of the code given in the documentation) is given below.
@override
void initState() {
super.initState();
this.initDynamicLinks();
}
void initDynamicLinks() async {
try {
FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData dynamicLink) async {
var deepLink = dynamicLink?.link?.toString() ?? null;
if (null != deepLink ) {
var lastIndex = deepLink.lastIndexOf(RegExp(r'/'));
assert(lastIndex != -1, "Problematic Link");
var feedId = deepLink.substring(lastIndex + 1);
// get the required data using feedID
try {
await Navigator.of(this.context).pushNamedAndRemoveUntil(
DeliveryTakeOrder.page,
ModalRoute.withName(PAGENAME),
arguments: argumentsConstructor(/*Arguments Here*/),
);
} catch (e) {
if (!kReleaseMode) {
debugPrint(e.toString());
}
}
}
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
} catch (e, s) {
print(s);
}
// await FirebaseDynamicLinks.instance.
final PendingDynamicLinkData dynamicLink = await FirebaseDynamicLinks.instance.getInitialLink();
var deepLink = dynamicLink?.link?.toString() ?? null;
if (null != deepLink ) {
// A bit of reverse engineering here
var lastIndex = deepLink.lastIndexOf(RegExp(r'/'));
assert(lastIndex != -1, "Problematic Link");
var feedId = deepLink.substring(lastIndex + 1);
// get the required data using feedID
try {
await Navigator.of(this.context).pushNamedAndRemoveUntil(
DeliveryTakeOrder.page,
ModalRoute.withName(PAGENAME),
arguments: argumentsConstructor(/*Arguments Here*/),
);
} catch (e) {
if (!kReleaseMode) {
debugPrint(e.toString());
}
}
}
}
In the inline documentation, I see the below comments.
/// Attempts to retrieve the dynamic link which launched the app.
///
/// This method always returns a Future. That Future completes to null if
/// there is no pending dynamic link or any call to this method after the
/// the first attempt.
Future<PendingDynamicLinkData> getInitialLink() async {
//..
}
So, on reaching getInitialLink()
a second time, it should return a null
which is not the case here. Any help is appreciated.
Note: This issue is observed until Android API Level 27.
Upvotes: 7
Views: 2156
Reputation: 1865
Here is an implemented solution inspired by @Pinolpier, with a listener on the dynamic link.
bool isOpening = false; // the local bool
@override
Widget build(BuildContext context) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
// * Open post on dynamic link event
FirebaseDynamicLinks.instance.onLink.listen((event) async {
print("listener event called"); // this is getting called multiple times
if (isOpening == false){
isOpening = true;
//do your operation here
await Future.delayed(Duration(seconds: 1));
isOpening = false;
}
},
);
});
return Center());
}
Upvotes: 1
Reputation: 21965
As of now, this is an ongoing issue as I could confirm from here I managed to work around this (temporarily) using the shared preferences module.
Inside main :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('just_opening', true);
runApp(YourAppName());
}
Inside initState()
, check for the persistent value just_opening
, call initDynamicLinks
and then set the just_opening
attribute to false
.
SharedPreferences.getInstance().then((prefs) async{
if(prefs.getBool('just_opening')){
this.initDynamicLinks();
}
await prefs.setBool('just_opening', false);
});
This certainly looks like an ugly hack, but it works on iOS and Android.
Edit
Looks like this issue is still in the wild as of March 13, 2022. A better alternative is to use a static variable to work around this as mentioned by @Pinolpier in this comment
Upvotes: 4