Flutter Getting Started
Install the Flutter plugin, connect it to your ULink project, and validate on both platforms.
1. Prerequisites
Development Environment
- Flutter SDK installed
- iOS: Xcode 15+ and iOS 13+ deployment target (for iOS builds)
- Android: Android Studio Giraffe or newer, Gradle 8+, Min SDK 21+ (for Android builds)
ULink Account Setup
- ULink account and project (create one here)
- API key generated from Dashboard → Settings → API Keys → Generate API Key
- Domain configured in your project (Domains → Add Domain)
Platform Configuration
- iOS: URL scheme and associated domain configured (see step 3.2)
- Android: Intent filters and permissions configured (see step 3.1)
2. Add the dependency
- Open your
pubspec.yamlfile in the root of your Flutter project - Add
flutter_ulink_sdkto your dependencies:
dependencies:
flutter:
sdk: flutter
flutter_ulink_sdk: ^0.1.0
- Run the following command in your terminal:
flutter pub get
If you use code generation elsewhere in your project, you may also need to run flutter pub run build_runner build. This is typically only needed if you're using packages like json_serializable or freezed.
3. Configure Native Platforms
Flutter apps need platform-specific configuration for both iOS and Android. You'll need to configure each platform separately.
Step 3.1: Configure Android Platform
Android requires intent filters in the manifest to handle deep links.
- Open
android/app/src/main/AndroidManifest.xmlin your Flutter project - Add required permissions at the top level (inside
<manifest>tag, before<application>):
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourapp">
<!-- Add these permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...>
- Find your main activity (usually
MainActivityorio.flutter.embedding.android.FlutterActivity) - Add intent filters inside the activity tag:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Existing launcher intent filter (keep this) -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Android App Link intent filter (for 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" />
<data
android:scheme="https"
android:host="links.shared.ly" />
</intent-filter>
<!-- Optional: Custom URL scheme intent filter -->
<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="myapp" />
</intent-filter>
</activity>
- Replace
links.shared.lywith the domain you configured in the ULink dashboard - Replace
myappwith your custom URL scheme (if using one) - The
android:autoVerify="true"attribute enables Android App Links verification - The
android:launchMode="singleTop"ensures proper deep link handling - Keep your existing launcher intent filter - don't remove it
In Flutter projects, the main activity is typically:
MainActivity(if you have a custom one)io.flutter.embedding.android.FlutterActivity(default Flutter activity)
Check your AndroidManifest.xml to see which one you're using.
Step 3.2: Configure iOS Platform
iOS requires URL schemes and Associated Domains to handle deep links.
Configure URL Scheme
-
Open your iOS project in Xcode:
open ios/Runner.xcworkspace(Use
.xcworkspace, not.xcodeprojif you're using CocoaPods) -
Select the Runner target in the project navigator
-
Go to the Info tab
-
Scroll down to URL Types section
-
Click the + button to add a new URL Type
-
Fill in the following:
- Identifier: Your bundle identifier (e.g.,
com.yourapp) - URL Schemes: Enter your scheme (e.g.,
myapp- this will create URLs likemyapp://) - Role:
Editor
- Identifier: Your bundle identifier (e.g.,
- Use lowercase letters and numbers only
- Keep it short and memorable (e.g.,
myapp,shop,news) - Avoid special characters or spaces
Configure Associated Domains (Universal Links)
- In Xcode, with the Runner target selected, go to the Signing & Capabilities tab
- Click + Capability and select Associated Domains
- Click + under Domains to add a new domain
- Enter your domain in the format:
applinks:yourdomain.comorapplinks:links.shared.ly- Replace
yourdomain.comwith the domain you configured in the ULink dashboard - The
applinks:prefix is required by iOS - For shared domains, use:
applinks:links.shared.ly(or your custom subdomain)
- Replace
The domain you add here must match exactly with the domain configured in your ULink dashboard under Domains. If you're using a shared domain like links.shared.ly, make sure it's added in both places.
If you prefer editing files directly, you can also configure URL schemes in ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.yourapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
And Associated Domains in ios/Runner/Runner.entitlements:
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:links.shared.ly</string>
</array>
4. Initialize the SDK in Dart
Initialize the ULink SDK in your Flutter app before rendering the widget tree:
- Open your
lib/main.dartfile - Import the SDK:
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
- Initialize the SDK in your
mainfunction:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize ULink SDK
await ULink.instance.initialize(
ULinkConfig(
apiKey: 'ULINK_API_KEY', // Replace with your API key
baseUrl: 'https://api.ulink.ly',
debug: true, // Set to false in production
),
);
runApp(MyApp());
}
- Call
WidgetsFlutterBinding.ensureInitialized()first - this is required for async initialization - Initialize the SDK before
runApp()to ensure it's ready when your app starts - The SDK initialization is asynchronous, so use
awaitto ensure it completes
Here's a complete example of a main.dart file with SDK initialization:
import 'package:flutter/material.dart';
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize ULink SDK
await ULink.instance.initialize(
ULinkConfig(
apiKey: 'your-api-key-here',
baseUrl: 'https://api.ulink.ly',
debug: true,
),
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: HomeScreen(),
);
}
}
5. Configure Dashboard Settings
Now that your app is configured, you need to match those settings in the ULink dashboard.
- Open your ULink project in the dashboard
- Navigate to Configuration → General
- Configure iOS settings:
- Enter your Bundle ID (found in Xcode under your target's General tab, or in
ios/Runner.xcodeproj/project.pbxproj) - Enter your URL Scheme (the scheme you configured in step 3.2, e.g.,
myapp- without the://)
- Enter your Bundle ID (found in Xcode under your target's General tab, or in
- Configure Android settings:
- Enter your Package Name (found in
android/app/build.gradle.ktsasnamespace, or inAndroidManifest.xmlas thepackageattribute) - If you configured a custom URL scheme, enter your URL Scheme (e.g.,
myapp- without the://)
- Enter your Package Name (found in
- Navigate to Domains section
- If you haven't already, add your domain:
- For shared domains: Click Add Domain → Select Shared Domain (.shared.ly) → Enter your subdomain
- For custom domains: Click Add Domain → Enter your custom domain → Complete DNS verification
- Ensure the domain matches exactly what you used in:
- Android: The
android:hostvalue in your intent filter (step 3.1) - iOS: The domain in your Associated Domains (step 3.2, without the
applinks:prefix)
- Android: The
- iOS Bundle ID in the dashboard must match your Xcode project's bundle identifier
- Android Package Name in the dashboard must match your app's package name exactly
- URL Scheme in the dashboard must match the scheme you added in URL Types (iOS) and intent filter (Android)
- Domain in the dashboard must match:
- The
android:hostvalue in your Android intent filter - The domain in your iOS Associated Domains (without the
applinks:prefix)
- The
- iOS Bundle ID: Found in Xcode under your target's General tab, or in
ios/Runner.xcodeproj/project.pbxprojasPRODUCT_BUNDLE_IDENTIFIER - Android Package Name: Found in
android/app/build.gradle.ktsasnamespace = "com.yourapp", or inAndroidManifest.xmlaspackage="com.yourapp"
6. Create a test link
Follow the same dashboard process as native apps:
- Links → Create Link
- Choose Unified (store) or Dynamic (deep link parameters)
- Save and copy the short URL
7. Test on devices
- Run
flutter runon iOS and Android. - Tap the short URL (use
adbor share sheets). - Ensure:
- Flutter log shows
DEBUG: Link listeners setup complete. - Your
onDynamicLinkoronUnifiedLinkcallback receives data.
- Flutter log shows
See Receive Links → Flutter for wiring the streams to navigation.
8. Confirm analytics
- In Links, check that click counts increment.
- Click the Analytics button for that specific link to open its analytics page and ensure the platform shows the correct value (iOS or Android) and geo data populates.
If not, revisit the platform-specific getting-started pages or Troubleshoot & Test Deep Links.