Implementing Deep Linking in Flutter with AppsFlyer: A Complete Guide

In this blog we will be focusing on deeplinking integration right from creating. onelink in appsflyer dashboard and configuring flutter app to fetch the deeplink and utilise it to open the app.

Deeplink plays a crucial role in app user management it might be redirecting user’s to your offers, new feature alerts driving new app installs which all together boosts your app reach.

Let’s start configuring your app first then i will also provide you the deep drive on creating a OneLink, and implementing the same.

Android :

As we deal with flutter app there needs to be some customisations done on android level to handle these deeplinks which we are going to see in below steps

AndroidManifest.xml :

<meta-data
    android:name="com.appsflyer.sdk.AF_DEV_KEY"
    android:value="your_dev_key"/>
<meta-data
    android:name="com.appsflyer.sdk.ONE_LINK_ID"
    android:value="your_one_link_id"/>

<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="your_host"/>
</intent-filter>

iOS :

Now we need to have iOS app to be configured to be able to handle deeplink. Here we are making server side changes even to make app get auto configured once it’s installed by default.

In android we did the same in manual way you can find it explained in detail android tutorial.

apple-app-site-association

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "P4K5RYADAL.com.abhi.appsflyer.flutterAppsflyer",
        "paths": [ "/product/*" ]
      }
    ]
  }
}

Android :

iOS :

home.dart

import 'package:flutter/material.dart';
import 'main.dart';
import 'util.dart';

class Home extends StatelessWidget {
  const Home();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Home Screen"),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            Util().logEvents(appsflyerSdk);
          },
          child: const Text("Home"),
        ),
      ),
    );
  }
}

profile.dart

import 'package:flutter/material.dart';

class Profile extends StatelessWidget {
  const Profile({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Profile"),
      ),
      body: const Center(
        child:  Text("Profile Screen"),
      ),
    );
  }
}

util.dart

import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:device_info_plus/device_info_plus.dart';

class Util{

  void getIDFV() async {
    final deviceInfo = DeviceInfoPlugin();
    final iosInfo = await deviceInfo.iosInfo;
    final idfv = iosInfo.identifierForVendor;
    final androidInfo = await deviceInfo.androidInfo;
    print("IDFV : $idfv");
    print("androidId : $androidInfo");
  }

  void requestAtt() async {
    final status = await
    AppTrackingTransparency.trackingAuthorizationStatus;

    if (status == TrackingStatus.notDetermined) {
      await AppTrackingTransparency.requestTrackingAuthorization();
    }
    getIDFA();
  }

  void getIDFA() async {
    final status = await
    AppTrackingTransparency.trackingAuthorizationStatus;
    if (status == TrackingStatus.authorized) {
      final idfa = await
      AppTrackingTransparency.getAdvertisingIdentifier();
      print("IDFA : $idfa");
    } else {
      print("Tracking not authorized : $status");
    }
  }

  void logEvents(appsflyerSdk) {
    appsflyerSdk.logEvent("Home_Screen", {
      "btn_click": "add_to_cart",
      "favourites": "pen_drives",
      "platform": "android"
    }).then((result) {
      print("Events Sent : $result");
    });
  }
}

main.dart

import 'dart:async';

import 'package:appsflyer_sdk/appsflyer_sdk.dart';
import 'package:flutter/material.dart';
import 'package:flutter_appsflyer/profile.dart';

import 'home.dart';
import 'util.dart';

void main() {
  runApp(MyApp());
}

late AppsflyerSdk appsflyerSdk;
late final StreamSubscription _sub;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    init();
  }

  @override
  void dispose() {
    _sub.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      initialRoute: "/",
      routes: {
        '/': (context) => const Home(),
        '/profile': (context) => const Profile()
      },
        debugShowCheckedModeBanner: false,
    );
  }

  void init() {
    appsflyerSdk = AppsflyerSdk(AppsFlyerOptions(
      afDevKey: "dTUckosyNHAywNJJXAgNvH",
      appId: "111106547",
      showDebug: true,
      timeToWaitForATTUserAuthorization: 0,
    ));

    appsflyerSdk.initSdk(
      registerOnDeepLinkingCallback: true
    );

    appsflyerSdk.onDeepLinking((DeepLinkResult result){
      final status = result.status;
      final deepLinkData = result.deepLink;

      if((status == Status.FOUND) && deepLinkData != null){
        final data = deepLinkData.clickEvent;
        final deepLinkValue = data['deep_link_sub1'] as String?;
        _handleIncomingLink(deepLinkValue);
      }else {
        print(" Deeplink status is ${status.toString()} or data is null");
      }
    });

    Util().getIDFV();
    // requestAtt();
  }
}

void _handleIncomingLink(String? path){
  print("DeepLinkValue $path");

  switch(path) {
    case 'home_screen':
      navigatorKey.currentState?.pushNamed('/');
      break;
    case 'profile_screen':
      navigatorKey.currentState?.pushNamed('/profile');
      break;
    default :
      navigatorKey.currentState?.pushNamed('/');
      break;
  }
}

Leave a Comment