sniffurl_flutter
Flutter SDK for SniffURL deep linking & attribution tracking.
Track installs, signups, purchases, and custom in-app events — all with one line.
✨ Features
- 🔗 Attribution tracking – installs, clicks, signups, purchases
- 📱 Deep link support – auto-extracts
sn_vidfrom app links - 📊 Custom events – send rich metadata with any event
- 🛠 Lightweight SDK – minimal dependencies
- 🔒 Secure – uses project ID + API key authentication
- 🍎 Cross-platform – works on iOS and Android
📦 Installation
Add this to your package's pubspec.yaml file:
dependencies:
sniffurl_flutter: ^1.0.0
Then run:
flutter pub get
⚙️ Setup
1. Import the SDK
import 'package:sniffurl_flutter/sniffurl_flutter.dart';
2. Initialize with your keys
Get your Project Key and API Key from the SniffURL dashboard.
await SniffURL.init(
projectKey: 'YOUR_PROJECT_ID',
apiKey: 'YOUR_API_KEY',
appName: 'EXAMPLE_APP_NAME',
appVersion: '1.1.0',
bundleId: 'com.example.app',
);
3. Configure Deep Links (iOS)
Add the following to your ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.yourcompany.yourapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>yourapp</string>
</array>
</dict>
</array>
4. Configure Deep Links (Android)
Add the following to your android/app/src/main/AndroidManifest.xml inside the <activity> tag:
<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="yourapp" android:host="open" />
</intent-filter>
🚀 Usage
Initialize in main.dart
import 'package:flutter/material.dart';
import 'package:sniffurl_flutter/sniffurl_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SniffURL
await SniffURL.init(
projectKey: 'YOUR_PROJECT_ID',
apiKey: 'YOUR_API_KEY',
appName: 'EXAMPLE_APP_NAME',
appVersion: '1.1.0',
bundleId: 'com.example.app',
);
runApp(MyApp());
}
Track installs & app opens
await SniffURL.track('install');
await SniffURL.track('app_open');
Track signups
await SniffURL.track('signup', {
'userId': '12345',
'plan': 'pro',
});
Track purchases
await SniffURL.track('purchase', {
'orderId': 'ORD-998877',
'amount': 1499,
'currency': 'INR',
});
Track custom events
await SniffURL.track('level_up', {
'level': 7,
'character': 'wizard',
});
Get visitor ID
String? visitorId = SniffURL.getVisitorId();
print('Visitor ID: $visitorId');
Manually handle deep links
await SniffURL.handleUrl('yourapp://open?sn_vid=123456');
🧑💻 Complete Example
import 'package:flutter/material.dart';
import 'package:sniffurl_flutter/sniffurl_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize SniffURL
await SniffURL.init(
projectKey: 'kalp_12345',
apiKey: 'sk_live_abcde',
appName: 'MyApp',
appVersion: '1.0.0',
bundleId: 'com.example.myapp',
);
// Track app install
await SniffURL.track('install');
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SniffURL Demo',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
_trackAppOpen();
}
Future<void> _trackAppOpen() async {
await SniffURL.track('app_open');
}
Future<void> _handleSignup() async {
// Your signup logic here
// Track signup event
await SniffURL.track('signup', {
'userId': 'U123',
'plan': 'basic',
'timestamp': DateTime.now().toIso8601String(),
});
}
Future<void> _handlePurchase() async {
// Your purchase logic here
// Track purchase event
await SniffURL.track('purchase', {
'orderId': 'ORD-998877',
'amount': 1499,
'currency': 'INR',
'items': ['Product 1', 'Product 2'],
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SniffURL Flutter Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _handleSignup,
child: Text('Track Signup'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _handlePurchase,
child: Text('Track Purchase'),
),
SizedBox(height: 20),
Text('Visitor ID: ${SniffURL.getVisitorId() ?? "Not set"}'),
],
),
),
);
}
@override
void dispose() {
SniffURL.dispose();
super.dispose();
}
}
📱 Deep Link Testing
iOS Simulator
xcrun simctl openurl booted "yourapp://open?sn_vid=test123"
Android Emulator
adb shell am start -W -a android.intent.action.VIEW -d "yourapp://open?sn_vid=test123"
Real Device (iOS)
Use Safari to open: yourapp://open?sn_vid=test123
Real Device (Android)
Use Chrome to open: yourapp://open?sn_vid=test123
🔐 Keys & Security
- Your Project Key and API Key are available in the SniffURL Dashboard.
- Keep keys secure — don't hardcode them in public repos.
- Use environment variables or encrypted configs for production:
// Use flutter_dotenv or similar
import 'package:flutter_dotenv/flutter_dotenv.dart';
await dotenv.load();
await SniffURL.init(
projectKey: dotenv.env['SNIFFURL_PROJECT_KEY']!,
apiKey: dotenv.env['SNIFFURL_API_KEY']!,
appName: dotenv.env['APP_NAME']!,
appVersion: dotenv.env['APP_VERSION']!,
bundleId: dotenv.env['BUNDLE_ID']!,
);
🎯 API Reference
SniffURL.init()
Initialize the SDK with your credentials.
Parameters:
projectKey(String, required): Your SniffURL project keyapiKey(String, required): Your SniffURL API keyappName(String, required): Your application nameappVersion(String, required): Your application versionbundleId(String, required): Your application bundle ID
Returns: Future<void>
SniffURL.track()
Track an event with optional data.
Parameters:
event(String, required): Event namedata(Map<String, dynamic>, optional): Event metadata
Returns: Future<void>
SniffURL.trackEvent()
Alias for track(). Same parameters and return type.
SniffURL.handleUrl()
Manually handle a deep link URL.
Parameters:
url(String, required): The URL string to parse
Returns: Future<void>
SniffURL.getVisitorId()
Get the current visitor ID.
Returns: String? - The visitor ID or null if not set
SniffURL.dispose()
Clean up resources (call in widget's dispose method).
Returns: void
🐛 Troubleshooting
Deep links not working?
- iOS: Check that
CFBundleURLSchemesis correctly configured inInfo.plist - Android: Ensure the intent filter is in the right
<activity>tag inAndroidManifest.xml - Both: Make sure
app_linkspackage is properly installed
Events not being tracked?
- Check that SDK is initialized before calling
track() - Verify your Project Key and API Key are correct
- Check debug console for error messages
- Ensure you have internet connectivity
Visitor ID is null?
- The visitor ID comes from deep link parameters (
sn_vid) - If no deep link has been opened, it will be null
- You can test by manually calling
handleUrl()with a test URL
📞 Contact & Support
- 🌐 Website: https://kalp.ltd
- 🌐 SniffURL Dashboard: https://sniffurl.com
- 📧 Email: support@kalp.ltd
- 💼 LinkedIn: Aman Sharma
📄 License
MIT © Kalp Intelligence