Deep links are a powerful tool to enhance your Google Ads App and Web to App campaigns. It helps drive users directly to specific content within your Flutter application. This can help improve user experience, boost engagement, and significantly increase conversion rates for ads. Learn more About integrating deep links with Flutter.
This article guides you on how to implement and validate deep links for your Flutter app, leveraging tools like the Flutter Deeplinking Validator.
On this page
- Prepare for success: Use recommended deep link mechanisms
- How to implement deep links in your Flutter app
- Best practices for Flutter deep linking
Prepare for success: Use recommended deep link mechanisms
To make deep links work, you typically need to configure both your app and an associated website. The 2 primary, secure, and recommended deep link mechanisms are:
- App Links (Android): HTTP/HTTPS URLs that are verified to belong to your website, allowing your Android app to open them directly without user disambiguation.
- Universal Links (iOS): HTTP/HTTPS URLs that securely link to content within your iOS app. Similar to App Links, they require verification with your website to ensure that only your app can handle links from your domain.
It’s highly recommended to use these primary mechanisms instead of older custom schemes such as myapp://. They are more secure and unique, and provide a better fallback experience, like directing to your website if the app isn't installed.
How to implement and test deep links in your Flutter app
Implementing deep links in Flutter involves configuring your Flutter app to handle link-based navigation and setting up platform-specific configurations for Android and iOS, as well as web server configurations for domain association. Follow the steps below to successfully implement deep links in your Flutter app.
Step 1 of 4: App setup (Flutter and native configuration)
This step involves configuring your Flutter app to recognize and handle incoming deep link URLs.
A. Handle incoming URLs and navigation (Flutter-side)
Your Flutter application needs to be able to receive and analyze incoming deep link URIs and then navigate to the appropriate screen.
- Choose a routing solution.
go_router: A popular, declarative routing package for Flutter that simplifies navigation, including deep link handling. It's often recommended for more complex apps. (go_router on pub.dev)- Navigator 2.0 (Declarative Navigation): Flutter's core declarative navigation API. While powerful, it can be more complex to set up directly. Packages like
go_routerare built on top of this. uni_links / app_linkspackages: These packages help you get the initial link that opened the app and listen to subsequent incoming links. They are useful if you prefer to manage routing logic more manually or with a different routing solution.
- Retrieve and analyze the link.
- Initial link: When your app is opened by a deep link, you need to retrieve this initial URI. Packages like
uni_linksprovidegetInitialUri()for this. - Subsequent links: If your app is already running and receives a new deep link, you'll need to listen to a stream of incoming URIs.
uni_linksoffersuriLinkStream. - When you have the URI, analyze its path and any query parameters to determine the destination screen and data to display such as product ID.
- Initial link: When your app is opened by a deep link, you need to retrieve this initial URI. Packages like
- Navigate to the correct screen.
- Based on the analyzed link, use your chosen routing solution like
GoRouter.go(),Navigator.pushNamed()to navigate the user to the relevant screen in your app.
- Based on the analyzed link, use your chosen routing solution like
Example (Conceptual - using a routing package like go_router):
// In your main.dart or routing configuration
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
биологии GoRoute(
path: 'product/:id', // Example: myapp.com/product/123
builder: (BuildContext context, GoRouterState state) {
final String productId = state.pathParameters['id']!;
return ProductScreen(id: productId);
},
),
// Add other deep linkable routes
// Example: myapp.com/category/electronics
GoRoute(
path: 'category/:categoryName',
builder: (BuildContext context, GoRouterState state) {
final String category = state.pathParameters['categoryName']!;
return CategoryScreen(category: category);
},
),
],
),
],
// Error handling for unknown routes
errorBuilder: (context, state) => ErrorScreen(error: state.error),
);
// In your MaterialApp
MaterialApp.router(
routerConfig: _router,
// ... other MaterialApp properties
)
B. Set up native Android configuration (AndroidManifest.xml)
To enable Android App Links, you need to add intent filters to your android/app/src/main/AndroidManifest.xml file within the main <activity> tag that has android.intent.action.MAIN.
- Enable Flutter deep linking. Add this meta-data tag inside your main
<activity>:<meta-data android:name="flutter_deeplinking_enabled" android:value="true" /> - Add intent filters for HTTP/HTTPS links.
<intent-filter android:autoVerify="true"><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><!-- Accepts URIs that begin with https://yourdomain.com --><data android:scheme="https" android:host="yourdomain.com" /><!-- You can add multiple data tags for different hosts or pathPrefixes --><!-- Example for a specific path: --><!-- <data android:scheme="https" android:host="yourdomain.com" android:pathPrefix="/product"/> --></intent-filter><intent-filter> <!-- Also add for http if needed, though https is strongly recommended --><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="http" android:host="yourdomain.com" /></intent-filter>- Replace
yourdomain.comwith your actual domain. - The
android:autoVerify="true"attribute enables your app to be the default handler for links matching thedatatags, provided your website association (see Web Setup below) is correctly configured.
- Replace
C. Set up native iOS configuration
For iOS Universal Links, enable Associated domains in Xcode:
- Open your Flutter project's
iosfolder in Xcode. - Select the
Runnertarget in the Project navigator. - Go to the "Signing & Capabilities" tab.
- Click the
+ Capabilitybutton and add "Associated Domains." - Under the "Associated Domains" section, add an entry in the format:
applinks:yourdomain.com(replaceyourdomain.comwith your domain).
Step 2 of 4: Web setup (Domain association)
This step associates your website domains with your app, verifying that your app is authorized to open links for those domains. These files must be served over HTTPS.
A. For Android (App Links)
- Create a file named
assetlinks.json. - The content should be structured as follows:
[{"relation": ["delegate_permission/common.handle_all_urls"],"target": {"namespace": "android_app","package_name": "com.yourcompany.yourappname", // Replace with your app's package name"sha256_cert_fingerprints": ["YOUR_SHA256_CERT_FINGERPRINT_1", // Replace with your release key fingerprint"YOUR_SHA256_CERT_FINGERPRINT_2" // Add debug key fingerprint for testing if needed]}}]``` * Replace `com.yourcompany.yourappname` with your app's actual package name (applicationId in your `build.gradle`).* Replace `YOUR_SHA256_CERT_FINGERPRINT_...` with the SHA-256 fingerprints of your app's signing certificate(s). You can get this from the Google Play Console (App integrity > App signing) or using the `keytool` command. - Host this file on your website at the following exact path:
https://yourdomain.com/.well-known/assetlinks.json(replaceyourdomain.comwith your domain). - Ensure the file is served with
Content-Type: application/json.
B. For iOS (Universal Links)
- Create a file named
apple-app-site-association(no extension). - The content should be structured as follows:
- Replace
YOUR_TEAM_ID.com.yourcompany.yourappbundleidwith your Apple Team ID and your app's Bundle Identifier. - The
pathsarray specifies which URL paths your app is authorized to open. Use * as a wildcard to match multiple subpaths (e.g., /product/* matches all product detail pages). UseNOT /path/to/exclude/*to exclude specific paths. Using["*"]or["/"]generally allows your app to handle all links from the domain.
- Replace
- Host this file on your website at either
https://yourdomain.com/.well-known/apple-app-site-associationorhttps://yourdomain.com/apple-app-site-association(at the root). The.well-knownpath is generally preferred. - Ensure the file is served with
Content-Type: application/jsonand is not gzipped.
Step 3 of 4: Validate your deep links with the Flutter Deeplinking Validator
After you've started implementing deep links, it's crucial to validate your setup. Google provides the Flutter Deeplinking Validator, a tool integrated directly into Flutter DevTools, to help you with this process.
How to access the Flutter Deep Linking Validator
- Open your Flutter project in your preferred IDE (like VS Code or Android Studio).
- Launch Flutter DevTools.
- Navigate to the "Deeplinking Validator" tab, usually found under the list of available tools.
- Follow the on-screen instructions to select your project and initiate validation.
Best Practice: Use the validator iteratively during your development process to catch and fix deep linking issues early, ensuring a smoother integration with your App Engagement Ads.
Step 4 of 4: Test your deep links
- Use
adbto test intent filters:adb shell am start -a android.intent.action.VIEW \-c android.intent.category.BROWSABLE \-d "https://yourdomain.com/product/123" com.yourcompany.yourappname- Replace
yourdomain.comandcom.yourcompany.yourappnameaccordingly.
- Replace
- Click links from notes, emails, or other apps that match your intent filters.
For iOS
- Click links from Safari, Notes, or other apps that match your Associated Domains configuration.
- You can also test by pasting the Universal Link into Safari. If configured correctly, a banner should appear at the top of the page offering to open the link in your app.
Best practices for Flutter deep linking
- Prioritize security and reliability: Use Android App Links and iOS Universal Links. They offer a secure way to link to your app content and provide a graceful fallback to your website if the app isn't installed.
- Consistent URL structure: Maintain a consistent URL structure between your website and app deep links for easier management and tracking.
- Thorough testing:
- Use the Flutter Deeplinking Validator regularly (especially for Android).
- Test your deep links from various sources, including directly from a browser, from simulated ad clicks, and on different devices and OS versions.
- Website accessibility and configuration:
- Ensure your
assetlinks.json(Android) andapple-app-site-association(iOS) files are correctly hosted, publicly accessible via HTTPS, and have the correctContent-Typeheader (e.g.,application/json). - For iOS, remember the AASA file should not be gzipped by your server and allow for propagation time via Apple's CDN.
- Ensure your
- Prepare for App Not Installed: Always have a relevant fallback experience on your mobile website for users who click a deep link but don't have your app installed.
- Coordinate with marketing: Ensure your development team and marketing team are aligned on the deep link URLs being used in ad campaigns.
