app_links

repository·main·Indexed 43 days ago

https://github.com/llfbandit/app_links

A Flutter plugin for handling deep linking and app activation across Android, iOS, Windows, macOS, Linux, and Web. It supports both custom URL schemes and web-to-app (HTTPS) protocols, providing a singleton AppLinks class to retrieve initial and latest links via getInitialLink(), getLatestLink(), and streams like uriLinkStream and stringLinkStream.

Tokens
8.5K
Snippets
31
Records
37
Agent score
52%

What's inside app_links

  1. Customize URL event propagation on iOS

    main

    By default, app_links propagates events to other plugins. If you need to prevent event propagation to other plugins relying on the same events, you can use semi-automatic handling.

    Flutter's iOS embedder loops through plugins; plugins return a boolean to indicate if an event was handled (stopping propagation) or if it should continue.

    Options for customization:

    • Set defaultUrlHandling to .availability to propagate globally only if no URL is provided.
    • Set urlHandledCallBack to react to each URL. Returning true stops propagation; returning false continues it.
    import app_links
    
    class MyClass {
      // ...
    
      // Global configuration
      AppLinks.shared.defaultUrlHandling = .availability
    
      // ... or local callback
      AppLinks.shared.urlHandledCallBack = { (_ url: URL) -> Bool in
        // return true to stop propagation (handled), otherwise false to propagate
        return false
      }
    }
  2. Configure Windows runner for app link forwarding

    main

    To ensure that app links are forwarded to an existing instance of your application instead of launching a new one, you must modify your Windows runner's entry point.

    1. Add the app_links/app_links_plugin_c_api.h include at the top of <PROJECT_DIR>\windows\runner\main.cpp.
    2. Call SendAppLinkToInstance() at the beginning of wWinMain. If it returns true, the link has been forwarded and the current process should exit.
    #include "app_links/app_links_plugin_c_api.h"
    
    int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                          _In_ wchar_t *command_line, _In_ int show_command) {
      // Forward the link to an existing instance, if any, then exit.
      // You may ignore the result if you need to create another window.
      if (SendAppLinkToInstance()) {
        return EXIT_SUCCESS;
      }
    
      // ... rest of your wWinMain implementation
    }
  3. Configure AppDelegate for app_links on iOS

    main

    If you have a custom AppDelegate that overrides application(_:willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions:), you must ensure they call super and return true to maintain the default workflow.

    If you cannot follow this pattern or need custom behavior, you must manually intercept the link using AppLinks.shared.getLink(launchOptions:) and propagate it via AppLinks.shared.handleLink(url:) within your didFinishLaunchingWithOptions implementation.

    import app_links
    
    @main
    @objc class AppDelegate: FlutterAppDelegate {
      override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
    
        // Retrieve the link from parameters
        if let url = AppLinks.shared.getLink(launchOptions: launchOptions) {
          // We have a link, propagate it to your Flutter app or not
          AppLinks.shared.handleLink(url: url)
          return true // Returning true will stop the propagation to other packages
        }
    
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
      }
    }
  4. Configure custom URL schemes on macOS

    main

    To support custom URL schemes (e.g., sample://) on macOS, you must register the URL type in your project's Info.plist file. Add the CFBundleURLTypes key inside the main <dict> block of macos/Runner/Info.plist.

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <!-- abstract name for this URL type (you can leave it blank) -->
            <string>sample_name</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <!-- your schemes -->
                <string>sample</string>
            </array>
        </dict>
    </array>
  5. Platform setup requirements for AppLinks

    main

    Before using the plugin, you must perform platform-specific configurations to enable deep linking and custom URL schemes. The setup requirements vary by platform:

    • Android: Requires configuration for App Links and Deep Links.
    • iOS: Requires configuration for Universal Links and Custom URL schemes.
    • Windows, macOS, Linux: Requires setup for custom schemes and/or web-to-app registration.
    • Web: No specific setup is required; the plugin only provides the initial link upon loading.
  6. How to use AppLinks to handle incoming links

    main

    To handle incoming links (both HTTPS URLs and custom schemes), instantiate the AppLinks singleton early in your application lifecycle. This ensures you catch the initial link if the app is launched from a cold state. You can subscribe to the uriLinkStream to listen for all incoming URI events, including the initial link and subsequent links while the app is running.

    final appLinks = AppLinks(); // AppLinks is singleton
    
    // Subscribe to all events (initial link and further)
    final sub = appLinks.uriLinkStream.listen((uri) {
        // Do something (navigation, ...)
    });
  7. Register protocols using MSIX (Recommended for Packaged Apps)

    main

    For packaged applications, the recommended way to register protocols (both custom schemes like sample:// and web-to-app https://) is via the msix package. This method automatically creates the required manifest and ensures clean uninstallation.

    Note: This method only works for packaged apps and will not work during debugging.

    • Use protocol_activation to list the schemes you want to activate.
    • Use app_uri_handler_hosts specifically for web-to-app (https) registration. You cannot use patterns in the hosts list.
    msix_config:
      display_name: app_links_example
      msix_version: 1.0.0.0
      protocol_activation: https, sample # Add the protocols to activate the app
      app_uri_handler_hosts: www.example.com, example.com # Add the app uri handler hosts. You can't use patterns here.
  8. Configure GTK Application for App Links on Linux

    main

    To support app links (both custom schemes and web-to-app) in a Linux application using GTK, you must modify your application's initialization and activation logic to handle existing windows and command-line arguments.

    Key requirements:

    1. Update Application Flags: Change the application flags to include G_APPLICATION_HANDLES_COMMAND_LINE and G_APPLICATION_HANDLES_OPEN.
    2. Handle Activation: In your activate callback, check if windows are already present using gtk_application_get_windows. If they exist, present the existing window instead of creating a new one.
    3. Handle Command Line: Ensure your command-line handler returns FALSE to allow the application to proceed with standard activation logic.
    // 1. Update flags in your application initialization
    MyApplication* my_application_new() {
      return MY_APPLICATION(g_object_new(my_application_get_type(),
                                         "application-id", APPLICATION_ID,
                                         "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
                                         nullptr));
    }
    
    // 2. Update activate to handle existing windows
    static void my_application_activate(GApplication* application) {
      MyApplication* self = MY_APPLICATION(application);
    
      GList* windows = gtk_application_get_windows(GTK_APPLICATION(application));
      if (windows) {
        gtk_window_present(GTK_WINDOW(windows->data));
        return;
      }
    
      GtkWindow* window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
      // ... rest of window setup
    }
    
    // 3. Ensure command line handler returns FALSE
    static gboolean my_application_local_command_line(GApplication* application, ...) {
      g_application_activate(application);
      *exit_status = 0;
      return FALSE;
    }
  9. Set up Universal Links on macOS

    main

    Universal links cannot be configured directly via the plugin embedder. You must manually intercept the user activity in your macOS AppDelegate using the app_links package.

    Override the application(_:continue:restorationHandler:) method to extract the URL from the NSUserActivity using AppLinks.shared.getUniversalLink(userActivity) and then pass it to the plugin via AppLinks.shared.handleLink(link:).

    import app_links
    
    public override func application(_ application: NSApplication,
                                     continue userActivity: NSUserActivity,
                                     restorationHandler: @escaping ([any NSUserActivityRestoring]) -> Void) -> Bool {
    
      guard let url = AppLinks.shared.getUniversalLink(userActivity) else {
        return false
      }
      
      AppLinks.shared.handleLink(link: url.absoluteString)
      
      return false // Returning true will stop the propagation to other packages
    }
  10. Configure iOS requirements and disable default Flutter deep linking

    main

    To use app_links on iOS, ensure you meet the following requirements:

    Crucial Step: Since Flutter 3.24, you must explicitly disable default Flutter deep linking to avoid conflicts.

    1. Open ios/Runner/Info.plist.
    2. Add the FlutterDeepLinkingEnabled key set to false inside the <dict> tag.
    <key>FlutterDeepLinkingEnabled</key>
    <false/>
  11. Configure SceneDelegate for app_links on iOS

    main

    For apps using a SceneDelegate (Scene-based apps), you must manually handle both initial connection options and subsequent URL/User Activity updates to ensure links are forwarded to the Flutter app.

    1. In scene(_:willConnectTo:options:), call self.scene(scene, openURLContexts: connectionOptions.urlContexts) and iterate through connectionOptions.userActivities using self.scene(scene, continue: userActivity).
    2. Implement scene(_:openURLContexts:) to handle Custom URL schemes by calling AppLinks.shared.handleLink(url: context.url).
    3. Implement scene(_:continue:) to handle Universal Links by checking userActivity.webpageURL and calling AppLinks.shared.handleLink(url: url).
    import app_links
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
      // Check for initial link
      func scene(_ scene: UIScene,
                 willConnectTo session: UISceneSession,
                 options connectionOptions: UIScene.ConnectionOptions
      ) {
        // ...
    
        self.scene(scene, openURLContexts: connectionOptions.urlContexts)
    
        for userActivity in connectionOptions.userActivities {
          self.scene(scene, continue: userActivity)
        }
      }
    
      // Check for further Custom URL schemes
      func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        for context in URLContexts {
          AppLinks.shared.handleLink(url: context.url)
        }
      }
    
      // Check for further Universal Links
      func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        if let url = userActivity.webpageURL {
          AppLinks.shared.handleLink(url: url)
        }
      }
    }