Android setup
Claim your Lander domain with a verified App Links intent filter, register your signing fingerprints and add an optional custom scheme.
App Links open your app instead of the browser when two things agree: your manifest says the app handles a domain (an intent filter with android:autoVerify="true"), and the domain says it trusts your app's signing key (the assetlinks.json file). Lander hosts the file for you. You add the intent filter and tell Lander your fingerprints.
Know your link domain first: open Build > Domains in the dashboard. Hosted domains are named after your project and organization, for example acme-shop-acme.golander.link. The examples below use acme.golander.link, the domain of Lander's demo project.
Collect your SHA-256 fingerprints
Android only trusts assetlinks.json for the exact keys that signed the installed APK. You almost always have more than one:
| Key | Signs | How to get its SHA-256 |
|---|---|---|
| Debug key | flutter run builds | keytool on ~/.android/debug.keystore (below) |
| Upload key | The bundle you upload to Play | keytool on your upload keystore |
| Play App Signing key | What users install from Google Play | Play Console, on your app's App signing page (copy the app signing key certificate) |
# Debug key (default password is "android")
keytool -list -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass android -keypass android
# Or let Gradle print every variant's signing config
cd android && ./gradlew signingReportCopy the SHA256: lines. They look like AA:BB:CC:... (32 pairs).
The Play App Signing key is the one users get
If you use Play App Signing, Google re-signs your app. An app installed from the Play Store carries Google's key, not your upload key, so App Links only verify for store installs when that fingerprint is registered.
Add your Android app in the dashboard
Open Build > Platforms and fill in the Android app card:
| Field | Value |
|---|---|
| Package name | Your applicationId, for example com.acme.shop. |
| SHA-256 certificate fingerprints | One row per key from step 1. Colon separated or 64 bare hex characters both work. |
| Play Store URL (optional) | Used to send people who do not have the app to the store. Lander builds one from the package name when empty. |
| URI scheme (optional) | Your custom scheme without ://, for example acmeshop. |
Save. Lander now serves this file on your link domain:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.acme.shop",
"sha256_cert_fingerprints": ["AA:BB:..."]
}
}
]Check it is live:
curl -i https://acme.golander.link/.well-known/assetlinks.jsonYou should get 200. A 404 means no Android app with at least one fingerprint is saved for this domain. More detail is in Dashboard: Apps.
Add the intent filters
In android/app/src/main/AndroidManifest.xml, inside your main <activity> (the one running FlutterActivity), add:
<!-- Let Lander deliver links instead of Flutter's built in handler,
so navigation does not happen twice. -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<!-- App Links for your Lander domain. autoVerify makes Android check
https://acme.golander.link/.well-known/assetlinks.json at install time. -->
<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" />
<data android:scheme="https" android:host="acme.golander.link" />
</intent-filter>
<!-- Optional: custom scheme, for example acmeshop://product/123 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="acmeshop" />
</intent-filter>Keep the App Links filter separate from the custom scheme filter, as above. Android verifies App Links per intent filter, and mixing a custom scheme into the verified filter can stop it from verifying.
If you also use a custom domain, add a <data android:scheme="https" android:host="..." /> line (or its own filter) for each domain. Lander serves assetlinks.json on every verified domain of the project.
Keep the activity singleTop
Your activity should look like the one Flutter generates:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
...>singleTop (the Flutter default) makes links that arrive while the app is running go to onNewIntent on the existing activity, where the plugin picks them up. With the standard launch mode Android would start a second copy of your app instead.
You do not need to change MainActivity.kt. The plugin reads the launch intent on cold start and listens to onNewIntent on warm start. When someone reopens the app from Recents, Android replays the original launch intent; the plugin ignores it, so an old link does not navigate again.
Check verification
Install the app (a build signed by one of the registered keys), then ask Android what it decided:
adb shell pm get-app-links com.acme.shopLook for acme.golander.link: verified. If it says none or legacy_failure, re-run verification after fixing the cause:
adb shell pm verify-app-links --re-verify com.acme.shopOn a test device you can also mark the domain as approved without verification, which is handy while you sort out fingerprints:
adb shell pm set-app-links --package com.acme.shop 2 allDeferred deep links: nothing to do
On first launch the SDK reads the Play Install Referrer once and sends it to Lander. When the install came from a Lander link, the link comes back with confidence: deterministic and method: install_referrer. The plugin adds the installreferrer dependency for you. See Deferred links.
Local development over HTTP
Only needed if you point the SDK at a Lander API running on your machine. The Android emulator reaches your computer at 10.0.2.2, and plain HTTP needs cleartext traffic allowed. Put it in the debug manifest so it never ships:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application android:usesCleartextTraffic="true" />
</manifest>Try it
adb shell am start -a android.intent.action.VIEW \
-d "https://acme.golander.link/p-123" com.acme.shopIf the app is verified it opens on the link's screen. An unverified App Link opens a chooser or the browser instead; see Troubleshooting.