sjsam
sjsam

Reputation: 21965

FirebaseDynamicLinks getInitialLink() fired multiple times

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

Answers (2)

Paul
Paul

Reputation: 1865

Using a local variable

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

sjsam
sjsam

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

Related Questions