uni_links

by avioli

avioli / uni_links

Flutter plugin for accepting incoming links.

274 Stars 84 Forks Last release: Not found Other 71 Commits 4 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Uni Links

Travis' Continuous Integration build status

A Flutter plugin project to help with App/Deep Links (Android) and Universal Links and Custom URL schemes (iOS).

These links are simply web-browser-like-links that activate your app and may contain information that you can use to load specific section of the app or continue certain user activity from a website (or another app).

App Links and Universal Links are regular https links, thus if the app is not installed (or setup correctly) they'll load in the browser, allowing you to present a web-page for further action, eg. install the app.

Make sure you read both the Installation and the Usage guides, thoroughly, especiallly for App/Universal Links (the https scheme).

Installation

To use the plugin, add

uni_links
as a dependency in your pubspec.yaml file.

Permission

Android and iOS require to declare links' permission in a configuration file.

Feel free to examine tha example app in the example directory for Deep Links (Android) and Custom URL schemes (iOS).

The following steps are not Flutter specific, but platform specific. You might be able to find more in-depth guides elsewhere online, by searching about App Links or Deep Links on Android; Universal Links or Custom URL schemas on iOS.

For Android

Uni Links supports two types of Android links: "App Links" and "Deep Links".

  • App Links only work with
    https
    scheme and require a specified host, plus a hosted file -
    assetlinks.json
    . Check the Guide links below.
  • Deep Links can have any custom scheme and do not require a host, nor a hosted file. The downside is that any app can claim a scheme + host combo, so make sure yours are as unique as possible, eg.
    HST0000001://host.com
    .

You need to declare at least one of the two intent filters in

android/app/src/main/AndroidManifest.xml
:
  
  
    
      

  <!-- Deep Links -->
  <intent-filter>
    <action android:name="android.intent.action.VIEW"></action>
    <category android:name="android.intent.category.DEFAULT"></category>
    <category android:name="android.intent.category.BROWSABLE"></category>
    <!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
    <data android:scheme="[YOUR_SCHEME]" android:host="[YOUR_HOST]"></data>
  </intent-filter>

  <!-- App Links -->
  <intent-filter android:autoverify="true">
    <action android:name="android.intent.action.VIEW"></action>
    <category android:name="android.intent.category.DEFAULT"></category>
    <category android:name="android.intent.category.BROWSABLE"></category>
    <!-- Accepts URIs that begin with https://YOUR_HOST -->
    <data android:scheme="https" android:host="[YOUR_HOST]"></data>
  </intent-filter>
</activity>

The

android:host
attribute is optional for Deep Links.

To further the specificity you can add an

android:pathPrefix
attribute:

For more info read The Ultimate Guide. Pay close attention to the App Links section in the Guide regarding the required

/.well-known/assetlinks.json
file.

The Android developer docs are also a great source of information for both Deep Links and App Links.

For iOS

There are two kinds of links in iOS: "Universal Links" and "Custom URL schemes".

  • Universal Links only work with
    https
    scheme and require a specified host, entitlements and a hosted file -
    apple-app-site-association
    . Check the Guide links below.
  • Custom URL schemes can have... any custom scheme and there is no host specificity, nor entitlements or a hosted file. The downside is that any app can claim any scheme, so make sure yours is as unique as possible, eg.
    hst0000001
    or
    myIncrediblyAwesomeScheme
    .

You need to declare at least one of the two.

--

For Universal Links you need to add or create a

com.apple.developer.associated-domains
entitlement - either through Xcode or by editing (or creating and adding to Xcode)
ios/Runner/Runner.entitlements
file.

  
  com.apple.developer.associated-domains
  
    applinks:[YOUR_HOST]
  
  


This allows for your app to be started from

https://YOUR_HOST
links.

For more information, read Apple's guide for Universal Links.

--

For Custom URL schemes you need to declare the scheme in

ios/Runner/Info.plist
(or through Xcode's Target Info editor, under URL Types):

  
  CFBundleURLTypes
  
    
      CFBundleTypeRole
      Editor
      CFBundleURLName
      [ANY_URL_NAME]
      CFBundleURLSchemes
      
        [YOUR_SCHEME]
      
    
  
  


This allows for your app to be started from

YOUR_SCHEME://ANYTHING
links.

For a little more information, read Apple's guide for Inter-App Communication.

I strongly recommend watching the Apple WWDC 2015, session 509 - Seamless Linking to Your App to understand how the Universal Links work (and are setup).

Usage

There are two ways your app will recieve a link - from cold start and brought from the background. More on these after the example usage in More about app start from a link.

Initial Link (String)

Returns the link that the app was started with, if any.

import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart'; import 'package:flutter/services.dart' show PlatformException;

// ...

Future initUniLinks() async { // Platform messages may fail, so we use a try/catch PlatformException. try { String initialLink = await getInitialLink(); // Parse the link and warn the user, if it is not correct, // but keep in mind it could be null. } on PlatformException { // Handle exception by warning the user their action did not succeed // return? } }

// ...

Initial Link (Uri)

Same as the

getInitialLink
, but converted to a
Uri
.
    // Uri parsing may fail, so we use a try/catch FormatException.
    try {
      Uri initialUri = await getInitialUri();
      // Use the uri and warn the user, if it is not correct,
      // but keep in mind it could be `null`.
    } on FormatException {
      // Handle exception by warning the user their action did not succeed
      // return?
    }
    // ... other exception handling like PlatformException

One can achieve the same by using

Uri.parse(initialLink)
, which is what this convenience method does.

On change event (String)

Usually you would check the

getInitialLink
and also listen for changes.
import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

// ...

StreamSubscription _sub;

Future initUniLinks() async { // ... check initialLink

// Attach a listener to the stream
_sub = getLinksStream().listen((String link) {
  // Parse the link and warn the user, if it is not correct
}, onError: (err) {
  // Handle exception by warning the user their action did not succeed
});

// NOTE: Don't forget to call _sub.cancel() in dispose()

}

// ...

On change event (Uri)

Same as the

stream
, but transformed to emit
Uri
objects.

Usually you would check the

getInitialUri
and also listen for changes.
import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

// ...

StreamSubscription _sub;

Future initUniLinks() async { // ... check initialUri

// Attach a listener to the stream
_sub = getUriLinksStream().listen((Uri uri) {
  // Use the uri and warn the user, if it is not correct
}, onError: (err) {
  // Handle exception by warning the user their action did not succeed
});

// NOTE: Don't forget to call _sub.cancel() in dispose()

}

// ...

More about app start from a link

If the app was terminated (or rather not running in the background) and the OS must start it anew - that's a cold start. In that case,

getInitialLink
will have the link that started your app and the Stream won't produce a link (at that point in time).

Alternatively - if the app was running in the background and the OS must bring it to the foreground the Stream will be the one to produce the link, while

getInitialLink
will be either
null
, or the initial link, with which the app was started.

Because of these two situations - you should always add a check for the initial link (or URI) and also subscribe for a Stream of links (or URIs).

Tools for invoking links

If you register a schema, say

unilink
, you could use these cli tools:

Android

You could do below tasks within Android Studio.

Assuming you've installed Android Studio (with the SDK platform tools):

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://host/path/subpath"'
adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://example.com/path/portion/?uid=123&token=abc"'
adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://example.com/?arr%5b%5d=123&arr%5b%5d=abc&addr=1%20Nowhere%20Rd&addr=Rand%20City%F0%9F%98%82"'

If you don't have

adb
in your path, but have

$ANDROID_HOME
env variable then use
"$ANDROID_HOME"/platform-tools/adb ...
.

Note: Alternatively you could simply enter an

adb shell
and run the
am
commands in it.

Note: I use single quotes, because what follows the

shell
command is what will run in the emulator (or device) and shell metacharacters, such as question marks (
?
) and ampersands (
&
), usually mean something different to your own shell.

adb shell
communicates with the only available device (or emulator), so if you've got multiple devices you have to specify which one you want to run the shell in via:
  • The only USB connected device -
    adb -d shell '...'
  • The only emulated device -
    adb -e shell '...'

You could use

adb devices
to list currently available devices (similarly
flutter devices
does the same job).

iOS

Assuming you've got Xcode already installed:

/usr/bin/xcrun simctl openurl booted "unilinks://host/path/subpath"
/usr/bin/xcrun simctl openurl booted "unilinks://example.com/path/portion/?uid=123&token=abc"
/usr/bin/xcrun simctl openurl booted "unilinks://example.com/?arr%5b%5d=123&arr%5b%5d=abc&addr=1%20Nowhere%20Rd&addr=Rand%20City%F0%9F%98%82"

If you've got

xcrun
(or
simctl
) in your path, you could invoke it directly.

The flag

booted
assumes an open simulator (you can start it via
open -a Simulator
) with a booted device. You could target specific device by specifying its UUID (found via
xcrun simctl list
or
flutter devices
), replacing the
booted
flag.

App Links or Universal Links

These types of links use

https
for schema, thus you can use above examples by replacing
unilinks
with
https
.

Contributing

For help on editing plugin code, view the documentation.

License

BSD 2-clause

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.