Overview of app_links_web
mainapp_links_web package is the web platform implementation of the app_links plugin. It enables web applications to handle deep linking and app links within a web environment.repository·main·Indexed 43 days ago
https://github.com/llfbandit/app_linksA 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.
app_links_web package is the web platform implementation of the app_links plugin. It enables web applications to handle deep linking and app links within a web environment.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:
defaultUrlHandling to .availability to propagate globally only if no URL is provided.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
}
}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.
app_links/app_links_plugin_c_api.h include at the top of <PROJECT_DIR>\windows\runner\main.cpp.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
}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)
}
}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>Before using the plugin, you must perform platform-specific configurations to enable deep linking and custom URL schemes. The setup requirements vary by platform:
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, ...)
});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.
protocol_activation to list the schemes you want to activate.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.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:
G_APPLICATION_HANDLES_COMMAND_LINE and G_APPLICATION_HANDLES_OPEN.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.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;
}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
}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.
ios/Runner/Info.plist.FlutterDeepLinkingEnabled key set to false inside the <dict> tag.<key>FlutterDeepLinkingEnabled</key>
<false/>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.
scene(_:willConnectTo:options:), call self.scene(scene, openURLContexts: connectionOptions.urlContexts) and iterate through connectionOptions.userActivities using self.scene(scene, continue: userActivity).scene(_:openURLContexts:) to handle Custom URL schemes by calling AppLinks.shared.handleLink(url: context.url).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)
}
}
}