How to Track App Installs in Your Flutter App
Add cross-platform install and revenue tracking to your Flutter app in under 10 minutes. One SDK, one integration, installs tracked on both iOS and Android.
Single codebase, complete install insight
With Flutter, you maintain just one codebase for both iOS and Android, and your tracking should reflect that unity. Integrate the Instally Flutter SDK just once, and you’ll track where installs and revenue come from across both platforms, all from one dashboard.
No separate platform code, no conditional imports. Any iOS-Android mismatch is resolved internally by the SDK, keeping your Dart code simple and consistent. For native-only flows, compare the iOS (Swift) guide and Android (Kotlin) guide. For the full framework behind per-link install tracking, see tracking app installs with links.
This tutorial covers the complete integration, from dependencies through install tracking, plus optional revenue tracking with RevenueCat. See the full RevenueCat integration walkthrough if you need dashboard screenshots.
What you need
| Requirement | Details |
|---|---|
| Instally account | Free tier works. Sign up here |
| Flutter 3.16+ | Dart 3.2 or later |
| iOS deployment target | 15.0+ (set in ios/Podfile) |
| Android min SDK | API 23 (set in android/app/build.gradle) |
| App on App Store and/or Google Play | Bundle ID and package name registered with Instally |
| 10 minutes | One integration, both platforms |
Step 1: Add the dependency
Add the Instally Flutter SDK to your project’s pubspec.yaml file:
Flutter example:
dependencies:
flutter:
sdk: flutter
instally_flutter: ^1.0.0
Then fetch the package:
flutter pub get
The SDK bundles all required native dependencies. iOS relies on CocoaPods, and Android uses Maven Central. Nothing further needs to be configured.
Platform-specific configuration
iOS: Ensure yourios/Podfile specifies a deployment target of at least 15.0:
platform :ios, '15.0'
Next, update your pods:
cd ios && pod install && cd ..
Android: Confirm your android/app/build.gradle sets minSdkVersion to 23:
android {
defaultConfig {
minSdkVersion 23
}
}
No further Android settings are necessary. All required native dependencies are pulled in automatically.
Step 2: Initialize the SDK on launch
Call Instally.configure() before invoking trackInstall. The optimal location is inside your main() function, right after WidgetsFlutterBinding.ensureInitialized().
Flutter example:
import 'package:flutter/material.dart';
import 'package:instally_flutter/instally_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Instally.configure(apiKey: 'your_api_key_here');
runApp(const MyApp());
}
Locate your API key in the Instally dashboard under Settings > API Keys. Use the production key for release builds and the test key while developing.
Step 3: Register the install
After configuring Instally, call trackInstall() at app launch. Because the SDK performs internal deduplication, you can safely call it on every launch.
Flutter example:
import 'package:instally_flutter/instally_flutter.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String _source = 'loading...';
@override
void initState() {
super.initState();
_trackInstall();
}
Future<void> _trackInstall() async {
try {
final installData = await Instally.trackInstall();
setState(() {
_source = installData.linkSlug ?? 'organic';
});
} catch (e) {
setState(() {
_source = 'error';
});
debugPrint('Install tracking failed: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Install source: $_source'),
),
);
}
}
The installData object contains:
| Field | Type | Description |
|---|---|---|
linkSlug | String? | The slug of the Instally link that drove the install. null if organic |
installyUserId | String | Unique Instally user ID for this install. Use this for revenue tracking |
country | String? | Two-letter country code (e.g., US, GB) |
platform | String | ios or android |
isNewInstall | bool | true on first launch, false on subsequent calls |
Platform behavior at a glance
The Instally Flutter SDK automatically handles the platform-specific differences. You call a single Dart method — trackInstall() — and the SDK returns the associated link slug (or null for organic installs). No platform branching in your code.
| iOS | Android | |
|---|---|---|
| Requires user consent | No | No |
| Requires IDFA / Ad ID | No | No |
| ATT prompt required | No | N/A |
| Attribution accuracy | High | Highest |
| Works on sideloaded apps | Yes | Best-effort |
Step 4: Record monetization (optional)
Monetization tracking attributes in-app purchases and recurring subscriptions to the specific Instally link responsible for the original user acquisition. That means you can get answers to things like “Who is the top YouTube creator for bringing in paying users?” or “Did the link in my email send a customer to subscribe, or did the link in my Twitter bio?”
Option A: RevenueCat webhook (recommended)
Simplest integration: just syncing the user ID; no need to modify existing logic.
Set up the webhook in your RevenueCat dashboard under Integrations -> Webhooks:
- URL:
https://api.instally.io/webhooks/revenuecat - Header:
X-Instally-Api-Key: your-api-key - Events:
INITIAL_PURCHASE,RENEWAL,PRODUCT_CHANGE
Flutter:
import 'package:instally_flutter/instally_flutter.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
Future<void> _initTracking() async {
final installData = await Instally.trackInstall();
// Send Instally user ID to RevenueCat
await Purchases.logIn(installData.installyUserId);
}
RevenueCat will send events like INITIAL_PURCHASE to the webhook and Instally will use the user ID from that event to match the purchase against an Instally installation.
Option B: Client-side revenue reporting
Or, if you are using a different payment provider than RevenueCat, or if you are just handling your payments directly (say, for a web app), you can report revenue directly in your app:
Flutter:
await Instally.trackRevenue(
amount: 9.99,
currency: 'USD',
productId: 'pro_monthly',
transactionId: purchaseDetails.purchaseID,
);
It should work with any purchase flow. Call it after any successful transaction.
Option C: Stripe or Adapty webhook
If you are using Stripe or Adapty, you need to also add a webhook to your dashboard:
Instally supports:
| Provider | Webhook URL | Events to enable |
|---|---|---|
| RevenueCat | https://api.instally.io/webhooks/revenuecat | INITIAL_PURCHASE, RENEWAL |
| Stripe | https://api.instally.io/webhooks/stripe | checkout.session.completed, invoice.paid |
| Adapty | https://api.instally.io/webhooks/adapty | Transaction events |
| Superwall | https://api.instally.io/webhooks/superwall | Transaction events |
Step 5: Test integration
After integrating the SDK, build and release your app. Go to the Instally Dashboard and create a new link, then click through your app stores and check your dashboard – both the click and install should show up within a second or two.
Make sure to test it on a real iOS or Android device. You may also want to test on both platforms if you plan to support both. The linking works a bit differently depending on the platform you install from, and it is good to double check everything is working on both.
If it looks like something is not syncing you may want to turn on verbose logging in the SDK to see what’s happening on the device:
Flutter:
Instally.configure(
apiKey: 'ik_live_abc123',
logLevel: InstallyLogLevel.verbose,
);
This will print a log to your development console showing the various signal types we received and whether a match occurred.
Full integration example
Below is a working example of the entire Instally integration in Flutter using RevenueCat:
import 'package:flutter/material.dart';
import 'package:instally_flutter/instally_flutter.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'dart:io' show Platform;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Instally
await Instally.configure(
apiKey: ‘ik_live_abc123’,
logLevel: InstallyLogLevel.error,
);
// Initialize RevenueCat
final rcApiKey = Platform.isIOS ? ‘appl_xyz789’ : ‘goog_xyz789’;
await Purchases.configure(PurchasesConfiguration(rcApiKey));
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String _source = ‘loading...’;
String _platform = ‘’;
@override
void initState() {
super.initState();
_initTracking();
}
Future<void> _initTracking() async {
try {
final install = await Instally.trackInstall();
setState(() {
_source = install.linkSlug ?? ‘organic’;
_platform = install.platform;
});
// Sync user ID with RevenueCat for revenue tracking
await Purchases.logIn(install.installyUserId);
} catch (e) {
setState(() {
_source = ‘error’;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text(‘My App’)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(‘Install source: $_source’),
Text(‘Platform: $_platform’),
],
),
),
);
}
}
FAQ
Does the Flutter SDK work on both iOS and Android?
Yes, it does. The SDK implements the iOS and Android SDKs through platform channels. You write your code in Dart and the SDK automatically handles all platform-specific matching.
Do I need to write any native code?
No, you only need to declare a dependency in pubspec.yaml. The SDK automatically sets up CocoaPods on iOS and Gradle on Android. No extra native code, Swift, Kotlin, or platform-specific configurations (beyond the minimum required SDK versions defined above) are necessary.
Can I use this with other state management solutions?
Yes. These examples use StatefulWidget as a straightforward way to show tracking results, but since trackInstall returns a Future, you can easily integrate it into any state management system: Provider, Riverpod, Bloc, GetX, plain ValueNotifier, etc.
What if my app also supports web?
Instally Flutter SDK currently only supports iOS and Android. Web app tracking can be handled separately using Instally’s JavaScript snippet. See docs for web integration guide.
Is there a performance impact?
The SDK will only make a single HTTP request when it runs for the first time and will then store the result in memory (which will then be cleared when you stop the app). If you call trackInstall a second time (and the result was previously cached), then it’ll just immediately return the result and will not re-request it from Instally’s servers. In normal scenarios, this HTTP request will only take less than 200ms to complete. There will be no background polling or continuous connections that run in the background after Instally’s initialization.
How do I handle errors in production?
The SDK will only throw an exception if there are actual issues with the network or a misconfigured API key. In production, simply wrap your code in a try-catch and fail gracefully. You should never let install tracking slow down your app.
---
When you make the move to Flutter, you’ll be able to ship to all platforms. You should be able to track installs across app stores from a single dependency. One pubspec.yaml change, one configure call, and one trackInstall call, it’s that easy to track installs across both the iOS and Android App Stores. View pricing and start tracking your app installs in minutes!
Stop guessing. Start shipping.
Track clicks, installs, and revenue from every link. Set up in five minutes.
Get started free