Skip to main content

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 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

  1. Open your pubspec.yaml file in the root of your Flutter project
  2. Add flutter_ulink_sdk to your dependencies:
pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_ulink_sdk: ^0.1.0
  1. Run the following command in your terminal:
flutter pub get
Code Generation

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.

  1. Open android/app/src/main/AndroidManifest.xml in your Flutter project
  2. 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 ...>
  1. Find your main activity (usually MainActivity or io.flutter.embedding.android.FlutterActivity)
  2. 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>
Android Configuration Notes
  • Replace links.shared.ly with the domain you configured in the ULink dashboard
  • Replace myapp with 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
Finding Your Main Activity

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

  1. Open your iOS project in Xcode:

    open ios/Runner.xcworkspace

    (Use .xcworkspace, not .xcodeproj if you're using CocoaPods)

  2. Select the Runner target in the project navigator

  3. Go to the Info tab

  4. Scroll down to URL Types section

  5. Click the + button to add a new URL Type

  6. Fill in the following:

    • Identifier: Your bundle identifier (e.g., com.yourapp)
    • URL Schemes: Enter your scheme (e.g., myapp - this will create URLs like myapp://)
    • Role: Editor
URL Scheme Best Practices
  • Use lowercase letters and numbers only
  • Keep it short and memorable (e.g., myapp, shop, news)
  • Avoid special characters or spaces
  1. In Xcode, with the Runner target selected, go to the Signing & Capabilities tab
  2. Click + Capability and select Associated Domains
  3. Click + under Domains to add a new domain
  4. Enter your domain in the format: applinks:yourdomain.com or applinks:links.shared.ly
    • Replace yourdomain.com with 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)
Domain Configuration

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.

Alternative: Edit Info.plist Directly

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:

  1. Open your lib/main.dart file
  2. Import the SDK:
import 'package:flutter_ulink_sdk/flutter_ulink_sdk.dart';
  1. Initialize the SDK in your main function:
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());
}
Initialization Timing
  • 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 await to ensure it completes
Complete Example

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.

  1. Open your ULink project in the dashboard
  2. Navigate to Configuration → General
  3. 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 ://)
  4. Configure Android settings:
    • Enter your Package Name (found in android/app/build.gradle.kts as namespace, or in AndroidManifest.xml as the package attribute)
    • If you configured a custom URL scheme, enter your URL Scheme (e.g., myapp - without the ://)
  5. Navigate to Domains section
  6. 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
  7. Ensure the domain matches exactly what you used in:
    • Android: The android:host value in your intent filter (step 3.1)
    • iOS: The domain in your Associated Domains (step 3.2, without the applinks: prefix)
Matching Configuration
  • 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:host value in your Android intent filter
    • The domain in your iOS Associated Domains (without the applinks: prefix)
Finding Your Bundle ID and Package Name
  • iOS Bundle ID: Found in Xcode under your target's General tab, or in ios/Runner.xcodeproj/project.pbxproj as PRODUCT_BUNDLE_IDENTIFIER
  • Android Package Name: Found in android/app/build.gradle.kts as namespace = "com.yourapp", or in AndroidManifest.xml as package="com.yourapp"

Follow the same dashboard process as native apps:

  1. Links → Create Link
  2. Choose Unified (store) or Dynamic (deep link parameters)
  3. Save and copy the short URL

7. Test on devices

  1. Run flutter run on iOS and Android.
  2. Tap the short URL (use adb or share sheets).
  3. Ensure:
    • Flutter log shows DEBUG: Link listeners setup complete.
    • Your onDynamicLink or onUnifiedLink callback receives data.

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.