Skip to main content

Flutter SDK

The InHouse Flutter SDK provides comprehensive tracking and analytics capabilities for your cross-platform mobile applications, including user behavior tracking, deep linking support, install attribution, and device fingerprinting.

Installation

Dependencies

Add the SDK to your pubspec.yaml:
dependencies:
  flutter:
    sdk: flutter
  flutter_inhouse: ^1.0.0

Install Dependencies

flutter pub get

Platform Setup

iOS Setup

  1. Minimum iOS Version
Ensure your iOS deployment target is 16.0 or higher in ios/Runner.xcodeproj:
<key>MinimumOSVersion</key>
<string>16.0</string>
  1. Configure URL Schemes in ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>your-app-identifier</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myflutterapp</string>
        </array>
    </dict>
</array>
  1. Add Associated Domains for universal links:
<key>com.apple.developer.associated-domains</key>
<array>
    <string>applinks:your-shortlink-domain.tryinhouse.co</string>
</array>
  1. Add Tracking Usage Description:
<key>NSUserTrackingUsageDescription</key>
<string>This app needs to track users for analytics and attribution purposes.</string>

Android Setup

  1. Minimum SDK Version
Ensure your minimum SDK version is 24 in android/app/build.gradle:
android {
    compileSdkVersion 34

    defaultConfig {
        minSdkVersion 24
        targetSdkVersion 34
    }
}
  1. Add Install Referrer Receiver to android/app/src/main/AndroidManifest.xml:
<application>
    <!-- Your existing application content -->

    <!-- Install Referrer Receiver -->
    <receiver
        android:name="co.tryinhouse.flutter.InstallReferrerReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="com.android.vending.INSTALL_REFERRER" />
        </intent-filter>
    </receiver>
</application>
  1. Configure Deep Link Intent Filters in your main activity:
<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme">

    <!-- Existing intent filters -->

    <!-- Custom Scheme Deep Links -->
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="myflutterapp"/>
    </intent-filter>

    <!-- App Links (Universal Links) -->
    <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" />
        <data android:host="your-shortlink-domain.tryinhouse.co" />
    </intent-filter>
</activity>

Quick Start

Basic Setup

Initialize the SDK in your app:
import 'package:flutter_inhouse/flutter_inhouse.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize the SDK
  await initializeSDK();

  runApp(MyApp());
}

Future<void> initializeSDK() async {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  try {
    final result = await sdk.initialize(
      projectToken: 'your-project-token', // Project token from TryInHouse dashboard
      tokenId: 'your-token-id', // Token ID from TryInHouse dashboard
      shortLinkDomain: 'your-shortlink-domain', // Your custom domain (e.g., 'myapp.tryinhouse.co')
      serverUrl: 'https://api.tryinhouse.co', // Optional: API endpoint
      enableDebugLogging: kDebugMode, // Enable debug logging in development
    );

    print('SDK initialized successfully: $result');
  } catch (error) {
    print('SDK initialization failed: $error');
  }
}

Handle App Lifecycle

import 'package:flutter/widgets.dart';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        sdk.onAppResume();
        break;
      default:
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Flutter App',
      home: HomeScreen(),
    );
  }
}

Event Tracking

Track App Open

Track when users open your app:
import 'package:flutter_inhouse/flutter_inhouse.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  @override
  void initState() {
    super.initState();
    _trackAppOpen();
  }

  // Basic app open tracking
  Future<void> _trackAppOpen() async {
    try {
      final result = await sdk.trackAppOpen();
      print('App open tracked: $result');
    } catch (error) {
      print('Failed to track app open: $error');
    }
  }

  // Track app open with specific shortlink
  Future<void> _trackAppOpenWithLink(String shortLink) async {
    try {
      final result = await sdk.trackAppOpen(shortLink: shortLink);
      print('App open with shortlink tracked: $result');
    } catch (error) {
      print('Failed to track app open: $error');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Welcome to Home Screen'),
            ElevatedButton(
              onPressed: () => _trackAppOpenWithLink('https://myapp.tryinhouse.co/abc123'),
              child: Text('Track App Open with Link'),
            ),
          ],
        ),
      ),
    );
  }
}

Track Session Start

Track user sessions:
// Basic session tracking
Future<void> trackSession() async {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  try {
    final result = await sdk.trackSessionStart();
    print('Session tracked: $result');
  } catch (error) {
    print('Failed to track session: $error');
  }
}

// Track session with shortlink context
Future<void> trackSessionWithLink(String shortLink) async {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  try {
    final result = await sdk.trackSessionStart(shortLink: shortLink);
    print('Session with shortlink tracked: $result');
  } catch (error) {
    print('Failed to track session: $error');
  }
}
Track when users interact with your short links:
// Track when user clicks on a short link
Future<void> trackShortLinkClick() async {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  try {
    final result = await sdk.trackShortLinkClick(
      shortLink: 'https://myapp.tryinhouse.co/campaign1', // Short link URL
      deepLink: 'myflutterapp://product/123', // Optional: Deep link destination
    );
    print('Short link click tracked: $result');
  } catch (error) {
    print('Failed to track short link click: $error');
  }
}

Screen Tracking

Track user navigation through your app:
import 'package:flutter_inhouse/flutter_inhouse.dart';

class ProductScreen extends StatefulWidget {
  final String productId;

  const ProductScreen({Key? key, required this.productId}) : super(key: key);

  @override
  _ProductScreenState createState() => _ProductScreenState();
}

class _ProductScreenState extends State<ProductScreen> {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  @override
  void initState() {
    super.initState();
    _trackScreenView();
  }

  Future<void> _trackScreenView() async {
    // Track screen view (using custom event tracking)
    try {
      await sdk.trackShortLinkClick(
        shortLink: 'internal://screen_view',
        deepLink: 'screen_view?name=ProductScreen&productId=${widget.productId}',
      );
    } catch (error) {
      print('Failed to track screen view: $error');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Product Details')),
      body: Center(
        child: Text('Product ID: ${widget.productId}'),
      ),
    );
  }
}

User Interaction Tracking

Track user actions and interactions:
class InteractiveWidget extends StatelessWidget {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  // Button press tracking
  Future<void> handleButtonPress() async {
    try {
      await sdk.trackShortLinkClick(
        shortLink: 'internal://button_press',
        deepLink: 'button_press?buttonText=Add to Cart&buttonId=add-cart-btn&screen=ProductScreen&productId=prod_123',
      );

      // Your button logic here
      addToCart();
    } catch (error) {
      print('Failed to track button press: $error');
    }
  }

  // Form submission tracking
  Future<void> handleFormSubmit(Map<String, dynamic> formData) async {
    try {
      await sdk.trackShortLinkClick(
        shortLink: 'internal://form_submit',
        deepLink: 'form_submit?formName=contact_form&formType=contact&fields=${formData.keys.join(',')}',
      );

      // Submit form logic
      submitForm(formData);
    } catch (error) {
      print('Failed to track form submit: $error');
    }
  }

  // List item selection tracking
  Future<void> handleItemSelect(Map<String, dynamic> item) async {
    try {
      await sdk.trackShortLinkClick(
        shortLink: 'internal://item_select',
        deepLink: 'item_select?itemId=${item['id']}&itemName=${item['name']}&itemType=product&screen=ProductListScreen&position=${item['position']}',
      );

      // Navigate to item details
      Navigator.pushNamed(context, '/product-detail', arguments: item);
    } catch (error) {
      print('Failed to track item select: $error');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: handleButtonPress,
          child: Text('Add to Cart'),
        ),
        // More widgets...
      ],
    );
  }
}

Install Attribution

Install Referrer Tracking

Track install attribution and referrer data:
import 'package:flutter_inhouse/flutter_inhouse.dart';

class AttributionService {
  static final FlutterInhouse sdk = FlutterInhouse.instance;

  // Get stored install referrer
  static Future<String?> getStoredReferrer() async {
    try {
      final referrer = await sdk.getInstallReferrer();
      print('Stored referrer: $referrer');
      return referrer;
    } catch (error) {
      print('Error getting referrer: $error');
      return null;
    }
  }

  // Fetch fresh install referrer data
  static Future<String?> fetchFreshReferrer() async {
    try {
      final referrer = await sdk.fetchInstallReferrer();
      print('Fresh referrer: $referrer');
      return referrer;
    } catch (error) {
      print('Error fetching referrer: $error');
      return null;
    }
  }

  // Reset first install state (use only for testing)
  static Future<void> resetFirstInstall() async {
    try {
      await sdk.resetFirstInstall();
      print('First install state reset');
    } catch (error) {
      print('Error resetting first install: $error');
    }
  }
}

Real-time Event Callbacks

Listen to SDK events in real-time:
import 'dart:async';
import 'package:flutter_inhouse/flutter_inhouse.dart';

class SDKCallbackHandler {
  static StreamSubscription<TrackingCallback>? _subscription;
  static final FlutterInhouse sdk = FlutterInhouse.instance;

  static void initializeCallbacks() {
    _subscription = sdk.addCallbackListener((TrackingCallback data) {
      print('SDK Event: ${data.callbackType}');
      print('Event Data: ${data.data}');

      switch (data.callbackType) {
        case 'session_start_shortlink':
          _handleSessionWithShortlink(data.data);
          break;
        case 'app_open_shortlink':
          _handleAppOpenFromShortlink(data.data);
          break;
        case 'app_install_from_shortlink':
          _handleInstallFromShortlink(data.data);
          break;
        case 'install_referrer':
          _handleInstallReferrer(data.data);
          break;
        case 'deep_link':
          _handleDeepLink(data.data);
          break;
        default:
          print('Unknown callback type: ${data.callbackType}');
      }
    });
  }

  static void _handleSessionWithShortlink(String data) {
    print('Session started from shortlink: $data');
    // Handle session with shortlink data
  }

  static void _handleAppOpenFromShortlink(String data) {
    print('App opened from shortlink: $data');
    // Handle app open from shortlink
  }

  static void _handleInstallFromShortlink(String data) {
    print('App installed from shortlink: $data');
    // Handle install attribution
  }

  static void _handleInstallReferrer(String data) {
    print('Install referrer received: $data');
    // Handle install referrer data
  }

  static void _handleDeepLink(String data) {
    print('Deep link received: $data');
    // Handle deep link navigation
  }

  static void dispose() {
    _subscription?.cancel();
    _subscription = null;
  }
}

// Usage in your app
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

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

  @override
  void dispose() {
    SDKCallbackHandler.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: HomeScreen(),
    );
  }
}
Handle incoming deep links from TryInHouse short links:
import 'package:flutter/services.dart';
import 'package:uni_links/uni_links.dart';

class DeepLinkHandler {
  static final FlutterInhouse sdk = FlutterInhouse.instance;
  static StreamSubscription? _linkSubscription;

  static Future<void> initializeDeepLinks() async {
    // Handle links that opened the app
    try {
      String? initialLink = await getInitialLink();
      if (initialLink != null) {
        await _handleDeepLink(initialLink);
      }
    } catch (e) {
      print('Error getting initial link: $e');
    }

    // Handle links when app is already running
    _linkSubscription = linkStream.listen((String? link) {
      if (link != null) {
        _handleDeepLink(link);
      }
    }, onError: (err) {
      print('Error handling deep link: $err');
    });
  }

  static Future<void> _handleDeepLink(String link) async {
    print('Deep link received: $link');

    // Notify SDK about the new URL
    try {
      await sdk.onNewURL(link);

      // Track the deep link click
      await sdk.trackShortLinkClick(
        shortLink: link,
        deepLink: link,
      );
    } catch (error) {
      print('Error processing deep link: $error');
    }

    // Parse and handle the deep link
    _handleNavigation(link);
  }

  static void _handleNavigation(String link) {
    try {
      final uri = Uri.parse(link);
      final path = uri.path;
      final query = uri.queryParameters;

      // Navigate based on path
      switch (path) {
        case '/product':
          final productId = query['id'];
          _navigateToProduct(productId);
          break;

        case '/category':
          final categoryId = query['id'];
          _navigateToCategory(categoryId);
          break;

        case '/search':
          final searchQuery = query['q'];
          _navigateToSearch(searchQuery);
          break;

        default:
          // Handle unknown paths
          _navigateToHome();
      }

    } catch (error) {
      print('Error handling navigation: $error');
    }
  }

  static void _navigateToProduct(String? productId) {
    if (productId != null) {
      // Navigate to product page
      print('Navigating to product: $productId');
    }
  }

  static void _navigateToCategory(String? categoryId) {
    if (categoryId != null) {
      // Navigate to category page
      print('Navigating to category: $categoryId');
    }
  }

  static void _navigateToSearch(String? query) {
    if (query != null) {
      // Navigate to search page
      print('Navigating to search: $query');
    }
  }

  static void _navigateToHome() {
    // Navigate to home page
    print('Navigating to home');
  }

  static void dispose() {
    _linkSubscription?.cancel();
  }
}
For iOS universal links, add to pubspec.yaml:
dependencies:
  uni_links: ^0.5.1
And handle in your app:
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    DeepLinkHandler.initializeDeepLinks();
  }

  @override
  void dispose() {
    DeepLinkHandler.dispose();
    super.dispose();
  }
}

Device Fingerprinting

Thumbmark Integration (Android Only)

The SDK includes device fingerprinting capabilities on Android:
import 'package:flutter_inhouse/flutter_inhouse.dart';

class DeviceFingerprintService {
  static final FlutterInhouse sdk = FlutterInhouse.instance;

  // Get device fingerprint
  static Future<String?> getDeviceFingerprint() async {
    try {
      final fingerprint = await sdk.getFingerprint();
      if (fingerprint.isNotEmpty) {
        print('Device fingerprint: $fingerprint');
        return fingerprint;
      }
      return null;
    } catch (error) {
      print('Error getting fingerprint: $error');
      return null;
    }
  }

  // Get fingerprint ID with specific algorithm
  static Future<String?> getFingerprintId([String? algorithm]) async {
    try {
      final fingerprintId = await sdk.getFingerprintId(algorithm: algorithm ?? 'sha256');
      if (fingerprintId.isNotEmpty) {
        print('Fingerprint ID: $fingerprintId');
        return fingerprintId;
      }
      return null;
    } catch (error) {
      print('Error getting fingerprint ID: $error');
      return null;
    }
  }

  // Check if fingerprinting is available (Android only)
  static Future<bool> isFingerprintingAvailable() async {
    try {
      final fingerprint = await getDeviceFingerprint();
      return fingerprint != null && fingerprint.isNotEmpty;
    } catch (error) {
      return false;
    }
  }
}

Configuration Options

Debug Mode

Enable comprehensive logging during development:
await FlutterInhouse.instance.initialize(
  projectToken: 'token',
  tokenId: 'token-id',
  shortLinkDomain: 'domain',
  enableDebugLogging: kDebugMode, // Enable in debug mode only
);

Error Handling

Implement comprehensive error handling:
import 'package:flutter/services.dart';

class SDKManager {
  static final FlutterInhouse sdk = FlutterInhouse.instance;

  static Future<bool> initializeWithErrorHandling() async {
    try {
      await sdk.initialize(
        projectToken: 'your-project-token',
        tokenId: 'your-token-id',
        shortLinkDomain: 'your-domain',
        enableDebugLogging: kDebugMode,
      );
      return true;
    } on PlatformException catch (e) {
      print('Platform error: ${e.code} - ${e.message}');
      return false;
    } catch (e) {
      print('General error: $e');
      return false;
    }
  }

  static Future<void> safeTrackEvent(Future<String> Function() trackingFunction) async {
    try {
      final result = await trackingFunction();
      print('Event tracked successfully: $result');
    } on PlatformException catch (e) {
      print('Platform error tracking event: ${e.code} - ${e.message}');
    } catch (e) {
      print('Error tracking event: $e');
    }
  }
}

TryInHouse Platform Configuration

Configure Well-Known Files

  1. Log in to TryInHouse Dashboard:
  2. Navigate to Project Settings:
    • Select your project from the dashboard
    • Go to SettingsLinkings
  3. Configure iOS Universal Links (apple-app-site-association):
{
  "applinks": {
    "details": [
      {
        "appIDs": ["TEAMID.com.yourcompany.yourflutterapp"],
        "components": [
          {
            "/#": "*",
            "exclude": true,
            "comment": "Matches any URL with a fragment identifier"
          },
          {
            "/?*": "*",
            "comment": "Matches any URL with query parameters"
          },
          {
            "/": "*",
            "comment": "Matches the root URL and any path"
          }
        ]
      }
    ]
  },
  "webcredentials": {
    "apps": ["TEAMID.com.yourcompany.yourflutterapp"]
  }
}
  1. Configure Android App Links (assetlinks.json):
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourflutterapp",
      "sha256_cert_fingerprints": ["YOUR_APP_SHA256_FINGERPRINT"]
    }
  }
]

API Reference

Core Methods

MethodDescriptionParametersReturns
initialize()Initialize the SDKprojectToken, tokenId, shortLinkDomain, serverUrl?, enableDebugLogging?Future<String>
onAppResume()Handle app resumeNoneFuture<void>
onNewURL()Handle new URLurl: StringFuture<void>
trackAppOpen()Track app open eventshortLink?Future<String>
trackSessionStart()Track session startshortLink?Future<String>
trackShortLinkClick()Track link clickshortLink, deepLink?Future<String>
getInstallReferrer()Get stored referrerNoneFuture<String>
fetchInstallReferrer()Fetch fresh referrerNoneFuture<String>
resetFirstInstall()Reset install stateNoneFuture<void>

Device Fingerprinting Methods

MethodDescriptionParametersReturns
getFingerprint()Get device fingerprintNoneFuture<String>
getFingerprintId()Get fingerprint IDalgorithm?Future<String>

Event Listeners

MethodDescriptionParametersReturns
addCallbackListener()Add event listenercallback: (data) => voidStreamSubscription<TrackingCallback>
callbackStreamGet callback streamNoneStream<TrackingCallback>

Dart Types

class TrackingCallback {
  final String callbackType;
  final String data;

  const TrackingCallback({
    required this.callbackType,
    required this.data,
  });
}

class FlutterInhouse {
  Future<String> initialize({
    required String projectToken,
    required String tokenId,
    required String shortLinkDomain,
    String? serverUrl,
    bool enableDebugLogging = false,
  });

  Future<void> onAppResume();
  Future<void> onNewURL(String url);
  Future<String> trackAppOpen({String? shortLink});
  Future<String> trackSessionStart({String? shortLink});
  Future<String> trackShortLinkClick({
    required String shortLink,
    String? deepLink,
  });
  Future<String> getInstallReferrer();
  Future<String> fetchInstallReferrer();
  Future<void> resetFirstInstall();
  Future<String> getFingerprint();
  Future<String> getFingerprintId({String? algorithm});
  StreamSubscription<TrackingCallback> addCallbackListener(
    void Function(TrackingCallback callback) onCallback,
  );
  Stream<TrackingCallback> get callbackStream;
}

Testing and Debugging

# Android testing
adb shell am start -W -a android.intent.action.VIEW -d "https://your-shortlink-domain.tryinhouse.co/test" com.yourcompany.yourflutterapp

# iOS testing (use iOS Simulator)
xcrun simctl openurl booted "https://your-shortlink-domain.tryinhouse.co/test"

Example Test Scenarios

class SDKTester {
  static final FlutterInhouse sdk = FlutterInhouse.instance;

  // Test deep link handling
  static Future<void> testDeepLink() async {
    const shortLink = 'https://myapp.tryinhouse.co/test123';
    const deepLink = 'myflutterapp://home?tab=featured';

    await sdk.trackShortLinkClick(
      shortLink: shortLink,
      deepLink: deepLink,
    );
  }

  // Test install referrer
  static Future<void> testInstallReferrer() async {
    final referrer = await sdk.fetchInstallReferrer();
    print('Test referrer: $referrer');
  }

  // Test device fingerprinting
  static Future<void> testFingerprinting() async {
    final fingerprint = await sdk.getFingerprint();
    final fingerprintId = await sdk.getFingerprintId();
    print('Fingerprint: $fingerprint');
    print('Fingerprint ID: $fingerprintId');
  }

  // Test session tracking
  static Future<void> testSessionTracking() async {
    await sdk.trackSessionStart();
    await Future.delayed(Duration(seconds: 5));
    await sdk.trackAppOpen();
  }
}

Debug Logging

Enable debug logging to see detailed SDK activity:
// Enable debug logging during SDK initialization
await FlutterInhouse.instance.initialize(
  projectToken: 'your-token',
  tokenId: 'your-id',
  shortLinkDomain: 'your-domain',
  enableDebugLogging: true, // This will show detailed logs
);

Troubleshooting

Common Issues

  • Check project token and token ID in TryInHouse dashboard
  • Verify network connectivity
  • Check that initialize() is called before other SDK methods
  • Enable debug logging to see detailed error messages
  • Verify InstallReferrerReceiver is added to AndroidManifest.xml - Test with Google Play Console’s internal testing - Check that app is installed from Play Store, not sideloaded - Use fetchInstallReferrer() to get fresh data - Install referrer only works on Android
  • Device fingerprinting only works on Android
  • Check that minimum SDK version is 24 or higher
  • Verify app permissions if required
  • Test on physical device, not emulator

Platform-Specific Issues

iOS Issues:
  • Universal links require valid SSL certificate
  • Check Associated Domains entitlement
  • Verify Team ID in apple-app-site-association file
  • Test on device, not simulator for production links
Android Issues:
  • App links require verified domain
  • Check SHA256 fingerprint in assetlinks.json
  • Verify intent filters have autoVerify=“true”
  • Test with different Android versions

Best Practices

Event Tracking

  1. Initialize Early: Call initialize() as soon as possible in your app lifecycle
  2. Handle App Resume: Always call onAppResume() when the app becomes active
  3. Deep Link Handling: Use onNewURL() for all incoming deep links on iOS
  4. Error Handling: Wrap SDK calls in try-catch blocks
  5. Callback Management: Properly dispose of callback listeners

Performance

  1. Batch Operations: Avoid calling tracking methods in tight loops
  2. Background Processing: SDK handles background processing automatically
  3. Network Optimization: SDK batches events for optimal network usage
  4. Memory Management: Properly dispose of streams and subscriptions

Development Workflow

  1. Debug Mode: Use debug logging during development
  2. Testing: Test deep links on both platforms
  3. Attribution Testing: Use resetFirstInstall() for testing install attribution
  4. Platform Testing: Test platform-specific features separately

Integration Examples

import 'package:flutter/material.dart';
import 'package:flutter_inhouse/flutter_inhouse.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My Flutter App',
      navigatorObservers: [
        InhouseNavigatorObserver(),
      ],
      home: HomeScreen(),
    );
  }
}

class InhouseNavigatorObserver extends NavigatorObserver {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    _trackNavigation('push', route, previousRoute);
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    _trackNavigation('pop', route, previousRoute);
  }

  void _trackNavigation(String action, Route<dynamic> route, Route<dynamic>? previousRoute) {
    try {
      sdk.trackShortLinkClick(
        shortLink: 'internal://navigation_$action',
        deepLink: 'navigation?action=$action&from=${previousRoute?.settings.name ?? 'unknown'}&to=${route.settings.name ?? 'unknown'}',
      );
    } catch (error) {
      print('Error tracking navigation: $error');
    }
  }
}

State Management Integration

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_inhouse/flutter_inhouse.dart';

class InhouseBlocObserver extends BlocObserver {
  final FlutterInhouse sdk = FlutterInhouse.instance;

  @override
  void onEvent(BlocBase bloc, Object? event) {
    _trackBlocEvent('event', bloc, event.toString());
  }

  @override
  void onTransition(BlocBase bloc, Transition transition) {
    _trackBlocEvent('transition', bloc, '${transition.currentState} -> ${transition.nextState}');
  }

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    _trackBlocEvent('error', bloc, error.toString());
  }

  void _trackBlocEvent(String type, BlocBase bloc, String data) {
    try {
      sdk.trackShortLinkClick(
        shortLink: 'internal://bloc_$type',
        deepLink: 'bloc?type=$type&bloc=${bloc.runtimeType}&data=${Uri.encodeComponent(data)}',
      );
    } catch (error) {
      print('Error tracking bloc event: $error');
    }
  }
}

void main() {
  Bloc.observer = InhouseBlocObserver();
  runApp(MyApp());
}

Next Steps

iOS SDK

Integrate with native iOS apps.

Android SDK

Integrate with native Android apps.

Deep Linking

Set up deep linking for seamless navigation.

Analytics

Understand your analytics data.
Need help with the Flutter SDK? Check our API documentation or contact [email protected].