vela_links 0.2.5
vela_links: ^0.2.5 copied to clipboard
Deep links, deferred deep links, short links, QR codes, link tracking and in-app event analytics (sessions, screens, sign-ups, purchases) for Flutter.
vela_links #
Deep links, deferred deep links, short share links, QR codes, link tracking and in-app analytics for Flutter apps, powered by the Vela Links service. A link opens the right screen in your app, even when the app is not installed yet, and you see which links and campaigns bring people who sign up and pay.
Platforms: Android and iOS. Needs: Flutter 3.22 or newer; Android minSdk 24 and iOS 13 with the plugin versions pub.dev picks today (set your app's minimum to at least that).
What you get #
- Deep links and deferred deep links: a web link, a QR code or a custom scheme opens a screen in your app. A person who installs from a link lands on that screen at first launch.
- Short share links and QR codes: create links from inside the app. Every app gets a trackable QR code.
- Link analytics: taps, QR scans, installs and opens per link, in the dashboard.
- In-app analytics: active users, sessions, retention, screens, sign-ups, purchases and revenue, credited to the link or campaign that brought each person.
Quick start #
1. Add your app in the dashboard. Sign up at deeplinks.velaapps.in (there is a free plan), add your app (package name, signing fingerprint, Apple Team ID, store links) and copy the SDK key. Its SDK setup tab shows the exact Android and iOS settings for your app, and Verify setup checks them for you.
2. Install.
flutter pub add vela_links
3. Start it and listen for links.
import 'package:vela_links/vela_links.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await VelaLinks.init(
apiKey: 'vl_...',
baseUrl: 'https://api.links.velaapps.in',
slug: 'your-app-slug', // optional: ignore links that belong to another app
);
// A link opened your app (or matched your first launch after install)
VelaLinks.instance.onLink.listen((link) {
// link.route, link.params, link.deferred
if (const {'/invite', '/product'}.contains(link.route)) {
// open that screen; ignore anything you do not expect
}
});
runApp(const MyApp());
}
Create a short link from inside the app, to share anywhere:
final url = await VelaLinks.instance.createLink(route: '/invite', params: {'id': '42'});
If a link arrives before you start listening, read it once with VelaLinks.instance.consumePendingLink().
4. See how people use your app. Add the screen observer once:
MaterialApp(
navigatorObservers: [VelaLinksObserver()], // reports each named screen
routes: {/* your named routes */},
)
Then add one line wherever something important happens:
VelaLinks.instance.track('signup');
VelaLinks.instance.track('share', properties: {'channel': 'whatsapp'});
VelaLinks.instance.trackPurchase(499, currency: 'INR', orderId: 'ORD-1001');
VelaLinks.instance.track('tutorial_done', once: true); // once per person
// after sign-in, with your own user id (not an email or phone number):
await VelaLinks.instance.setUserId('user_123');
The dashboard's Behaviour tab shows active users, new installs, sessions, how many people come back after 1, 7 and 30 days, your most
used screens and events, funnels (for example install, sign-up, purchase), revenue, and which link or campaign brought the people who pay.
With another router, call VelaLinks.instance.trackScreen('Name') yourself.
Platform setup #
The dashboard fills these in for your app. In short:
<!-- AndroidManifest.xml, inside <manifest>: release builds of a Flutter app do not have it by default -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- AndroidManifest.xml, inside your main <activity> -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<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-LINK-DOMAIN" android:pathPrefix="/l/"/>
</intent-filter>
<!-- plus a second intent-filter like it with <data android:scheme="YOUR-URI-SCHEME"/> for your custom scheme -->
- iOS: add
applinks:YOUR-LINK-DOMAINunder Associated Domains, add your URI scheme as a URL type, and setFlutterDeepLinkingEnabledtoNOinInfo.plist. - Save these in the dashboard, because your link domain builds its
apple-app-site-associationandassetlinks.jsonfiles from them: the Apple Team ID and bundle id (iOS), and the package name and SHA-256 signing fingerprint (Android; use the Play App Signing fingerprint for builds from the Play Store). Without them the phone does not verify the domain and web links open the browser instead. - Turning off Flutter's own deep-link handling applies to every link and route of your app (go_router and Navigator deep links from
other domains too), not only Vela links; the SDK receives links through its own listener either way. If your app relies on Flutter's
deep-link routing, leave both settings out and make your router ignore paths that start with
/l/. - Android reads the Play Install Referrer by itself for deferred links.
- Debug and flavour builds: the SDK sends your package name or bundle id with each call, and the service refuses (401) any id that is
not saved for the app in the dashboard. A build with another id (for example
com.example.app.dev) gets no links, matches or events until you add that id in the dashboard or passappId: 'com.example.app'toinit. - Other
initoptions:listenForLinks: falseif you receive links yourself and callhandleUri(uri), andhttpClient:to supply your ownhttp.Client(you then close it yourself).
Good to know #
- The SDK key identifies your app and can create links, report opens, send in-app events and look up a first-launch match for it, nothing else. It lives in your app like an analytics key, so it is rate-limited, and you can rotate it in the dashboard at any time.
- Revenue is client-reported. Anyone who copies the key out of your app could send fake events, so verify real purchases on your own server. The package name or bundle id check only guards against mistakes, because the app sends the id itself.
- Treat
link.routeandlink.paramsas untrusted input, like any deep link: check the route against the screens you allow, and never put secrets in link params. - Event rules: names use letters, digits and
_. Up to 10 properties with short text, number or true/false values. A purchase needs an amount and a three-lettercurrency(INRif you give none). AnorderIdwith unsupported characters is left off and the purchase is still recorded. Every event has its own id, so a resend after a bad network is stored once, and the same event repeated within a second counts once. - One person, many phones: call
setUserIdafter sign-in andresetUser()on sign-out. Both first send what is waiting under the previous identity. User ids are 1 to 100 visible ASCII characters with no spaces. Without one, each install is its own anonymous person. - Consent first: by default
first_openandsession_startare queued as soon asinitruns. If you must ask first, callinit(trackEvents: false), thensetTrackingEnabled(true)once the person agrees. PasstrackEventsfrom your stored consent on every launch (trackEvents: consentGiven): a saved opt-in does not overridetrackEvents: false, while a savedsetTrackingEnabled(false)does overridetrackEvents: true. While tracking is off the link that brought the install is still remembered on the phone (never sent), so a person who agrees later is credited to it.setTrackingEnabled(false)stops in-app events only: link opens still resolve, and are counted unless you passreportOpens: false.autoTrack: falseturns off only the automatic events. - Updating the package needs no code change; the CHANGELOG lists what changed. If you ask for consent, remember the line above applies to every launch, not only the first.
- Adding the SDK to an app that is already installed:
first_opencannot tell an existing install from a new one. Everyone who updates to the first release that contains the SDK, or from an older version of it (including 0.1.x), sendsfirst_openandsession_startonce, so the dashboard's new-installs figure rises for that release. Read the figures around that date, or accept it. The first-launch link check also runs once for those people: on iOS it can show the paste notice once, and on Android an old install referrer could open an old link. PassmatchDeferred: falsein that release to skip this check. - Errors: every network step has a time limit (
requestTimeout, 10 seconds). A failure raisesVelaLinksException(background work never raises) and never stops your app starting.VelaLinks.instancethrows aStateErrorbeforeinithas finished, so useVelaLinks.maybeInstance?.track(...)where code can run earlier.baseUrlmust behttps://(plainhttp://only forlocalhostor the Android emulator10.0.2.2), and redirects are never followed.
Privacy: what the SDK sends and stores #
Sent to the Vela Links service, over https
- Always: your SDK key, your app's package name or bundle id, the link code that opened the app, and whether it came from a link or a QR code. When you create a link: the route, params and title you give it.
- With in-app events: the event name, the time, your properties, screen names, purchase amounts, currency and order ids, the app version, the
platform, a random install id made on the phone, the user id if you call
setUserId(the service stores only a one-way hash), and the links that brought the install. The service adds a country from the request and keeps no IP address. It replaces values that look like an email address or a long phone number, but it cannot recognise names or addresses in free text, so do not send personal data. - First launch after install (it stops after one success, or after 3 launches that could not reach the service): on Android only the Vela Links token from the Play install referrer is read. On iOS the clipboard is read once per try, only to look for a Vela Links token (iOS may show its paste notice); after the token is used the clipboard is emptied (even if something else was copied in the meantime), and any other clipboard text is ignored and never sent. If there is no token, the service can still match an iOS install to a recent tap using a one-way hash of the network address (it never stores the address itself).
Stored on the phone (shared preferences, keys starting vela_links.): the random install id; the events not yet sent (names and
properties, in plain text, with the install id and user id they belong to, for up to 7 days); the links that brought the install and last opened the app; your user id if you set one; the
names of once-only events already sent; whether first-open was reported and when the app was last active; whether tracking is switched off;
and whether the first-launch check finished (the link codes are kept even while tracking is off, and are not sent then). Android Auto Backup and
iOS device backups save the whole preferences file, not single keys, so they can restore these onto a new phone and give two phones one install
id. To prevent that on Android, exclude the file (this also drops your own saved preferences from backup):
<!-- AndroidManifest.xml: <application android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" ...> -->
<!-- res/xml/data_extraction_rules.xml (Android 12 and newer) -->
<data-extraction-rules>
<cloud-backup><exclude domain="sharedpref" path="FlutterSharedPreferences.xml"/></cloud-backup>
<device-transfer><exclude domain="sharedpref" path="FlutterSharedPreferences.xml"/></device-transfer>
</data-extraction-rules>
<!-- res/xml/backup_rules.xml (Android 11 and older) -->
<full-backup-content><exclude domain="sharedpref" path="FlutterSharedPreferences.xml"/></full-backup-content>
On iOS this cannot be done per key; the effect is only the repeated install id.
Not used: no advertising id (IDFA/GAID), no device id, no location, and no native code of its own.
Please describe this in your app's privacy policy, ask for consent where your law requires it, and use it to fill in the App Store and Play Console data forms.
Support and security #
Questions: info@velaapps.in. To report a security problem privately, see SECURITY.md.
License #
MIT. See LICENSE.