Simple Deep Link Handling without Branch.io or Firebase

General Conversations

This setup covers a basic deep link requirement:

  • A user shares a link like: https://exampleapp.com/profile?id=123ABC

  • When someone clicks the link:

    • If the app is installed → open the profile in the app

    • If not → show manual buttons for App Store / Play Store (no auto redirect)

This is done without using Firebase Dynamic Links or Branch.io — just HTML + JavaScript.


🌐 Web Handler (HTML + JS)

You must have a domain (e.g. exampleapp.com) and create a folder named profile and a index.html file inside it.
ex.
exampleapp.com/profile/index.html

Paste the following code into index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>ExampleApp Profile Link</title>
  <style>
    body {
      font-family: sans-serif;
      background: #f5f5f5;
      text-align: center;
      padding: 50px 20px;
    }
    .container {
      background: white;
      max-width: 400px;
      margin: 0 auto;
      padding: 30px;
      border-radius: 10px;
      box-shadow: 0 0 10px rgba(0,0,0,0.1);
    }
    h1 {
      font-size: 22px;
      margin-bottom: 10px;
    }
    p {
      color: #666;
      margin-bottom: 30px;
    }
    button {
      background-color: #007bff;
      color: white;
      border: none;
      padding: 12px 24px;
      font-size: 16px;
      border-radius: 5px;
      cursor: pointer;
    }
    button:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>Opening Profile...</h1>
    <p>If the app doesn't open, click below to download it.</p>
    <button id="store-button">Download the App</button>
  </div>

  <script>
    window.onload = function () {
      const urlParams = new URLSearchParams(window.location.search);
      const profileId = urlParams.get('id');

      if (profileId) {
        const appUrl = `myapp://myapp.com/home?id=${profileId}`;
        const isAndroid = /Android/i.test(navigator.userAgent);
        const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);

        const storeUrlAndroid = 'https://play.google.com/store/apps/details?id=com.example.app';
        const storeUrlIOS = 'https://apps.apple.com/app/example-app/id1234567890';

        // Attempt to open the app
        if (isIOS) {
          window.location.href = appUrl;
        } else {
          const iframe = document.createElement('iframe');
          iframe.style.display = 'none';
          iframe.src = appUrl;
          document.body.appendChild(iframe);
        }

        // Manual store redirection on button click
        document.getElementById('store-button').onclick = () => {
          const storeUrl = isAndroid ? storeUrlAndroid : isIOS ? storeUrlIOS : storeUrlAndroid;
          window.location.href = storeUrl;
        };
      } else {
        window.location.href = 'https://exampleapp.com';
      }
    };
  </script>
</body>
</html>
  • Reads the id parameter from the URL (e.g., ?id=123ABC)

  • Attempts to open the app using a custom URL scheme like exampleapp://home?id=123ABC
    (To construct this, use your app’s deep link prefix from FlutterFlow → Settings → App Details → Routing & Deep Linking → URL Scheme. Hover over the little info (i) icon and copy the base URL. Replace the appUrl base with the coppied URL)

  • If the app is not installed, it displays a "Download the App" button

  • You should replace the Play Store and App Store URLs in the script with your actual app links

🔧 FlutterFlow Integration

By default, FlutterFlow loads the Logged In Page (You Setup In FF Settings) even if a deep link is meant for a different screen. So, if you try to deep link directly to a screen like ProfileView, it may:

  • Briefly flash and then

  • Automatically redirect to the Logged In Page.

If you’re seeing this issue, you’re not alone:
👉 Community Post Explaining the Problem


✅ My Solution

Instead of trying to open a specific page directly, I used the Logged In Page to handle the routing logic.

🧩 Steps:

  1. Keep the route as just the page name, e.g.:
    /home

    Don’t add parameters in the route itself like below.
    /home/:id

  2. On your Logged In Page (e.g., home):

    • Go to Page Parameters

    • Add a custom parameter, e.g., id

  3. On Page Load → Add Conditional Navigation Logic:

    • Check if the id is not empty.

    • If it has a value, navigate to the relevant page (like ProfilePage) and pass the ID.


✅ Result:

  • The link like https://exampleapp.com/profile?id=123ABC opens the app

  • FlutterFlow loads the Logged In Page

  • The Logged In Page checks the ID and redirects to the target page using FlutterFlow logic

  • You can save the following base URL in AppState and append the profile ID whenever a user wants to share their profile:
    https://exampleapp.com/profile?id=<PROFILE_ID>


    ⚠️ Cons of This Method

  • No post-install redirect – The app won’t remember the link if it's installed after clicking.

  • Browser issues – Some browsers (like Safari) may block or delay opening the app.

  • Manual fallback – Users must tap the store button; it doesn’t open automatically.

  • No tracking – You can’t measure clicks, installs, or app opens unless you build it yourself.

  • Limited flexibility – Only works for simple use cases like opening a profile.


This approach keeps everything smooth and native inside FlutterFlow — no need for external link services like Branch.io (which may charge after exceeding MAU limits) or Firebase, and no additional costs involved. For simple deep linking needs like opening a profile from a shared URL, this method is more than enough.

If you’ve used a similar method or have improvements in mind, feel free to share your thoughts.

4