Flutter Integration Guide

Integrate AppSpike into your Flutter app and serve your first ad in under 10 minutes.

AppSpike sits on top of your existing ad networks and uses real-time bidding to route every impression to the highest-paying demand source. You keep your current ad network accounts — AppSpike optimizes across all of them.

Prerequisites

Step 1: Add the SDK

Add the appspike_flutter package to your pubspec.yaml:

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  appspike_flutter: ^1.0.0

Then run:

flutter pub get

Android Setup

Add the AppSpike Maven repository to your android/build.gradle:

android/build.gradle
allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://sdk.appspike.dev/android' }
    }
}

Set your minimum SDK version in android/app/build.gradle:

android/app/build.gradle
android {
    defaultConfig {
        minSdkVersion 21
    }
}

iOS Setup

AppSpike's iOS dependencies are managed via CocoaPods. After adding the package, run:

cd ios
pod install

For App Tracking Transparency (ATT) compliance, add the NSUserTrackingUsageDescription key to your ios/Runner/Info.plist:

ios/Runner/Info.plist
<key>NSUserTrackingUsageDescription</key>
<string>This allows us to show you more relevant ads.</string>

Step 2: Initialize the SDK

Initialize AppSpike as early as possible, ideally in your main() function before running the app:

main.dart
import 'package:appspike_flutter/appspike_flutter.dart';

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

  final result = await AppSpike.initialize(appId: "YOUR_APP_ID");
  if (result.isSuccess) {
    debugPrint("AppSpike initialized — ${result.adapterCount} adapters loaded");
  } else {
    debugPrint("AppSpike init failed: ${result.error}");
  }

  runApp(const MyApp());
}

Use the AppSpikeBannerAd widget to embed a banner directly in your widget tree:

banner_example.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class BannerExample extends StatefulWidget {
  const BannerExample({super.key});

  @override
  State<BannerExample> createState() => _BannerExampleState();
}

class _BannerExampleState extends State<BannerExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Banner Ad")),
      body: Column(
        children: [
          // Your content
          const Expanded(
            child: Center(child: Text("App Content")),
          ),

          // Banner pinned to bottom
          AppSpikeBannerAd(
            placementId: "YOUR_APPSPIKE_PLACEMENT",
            size: BannerAdSize.banner,
            onAdLoaded: () {
              debugPrint("Banner loaded");
            },
            onAdFailedToLoad: (error) {
              debugPrint("Banner failed: $error");
            },
          ),
        ],
      ),
    );
  }
}

Available banner sizes: BannerAdSize.banner, BannerAdSize.largeBanner, BannerAdSize.mediumRectangle, BannerAdSize.fullBanner, BannerAdSize.leaderboard.

Step 4: Interstitial Ads

interstitial_example.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class InterstitialExample extends StatefulWidget {
  const InterstitialExample({super.key});

  @override
  State<InterstitialExample> createState() => _InterstitialExampleState();
}

class _InterstitialExampleState extends State<InterstitialExample> {
  InterstitialAd? _interstitialAd;

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

  Future<void> _loadInterstitial() async {
    final result = await AppSpike.loadInterstitialAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      setState(() {
        _interstitialAd = result.ad;
      });
    } else {
      debugPrint("Interstitial failed to load: ${result.error}");
    }
  }

  // Call this at a natural break point (e.g., between levels)
  Future<void> _showInterstitial() async {
    if (_interstitialAd == null) {
      debugPrint("Interstitial not ready yet");
      return;
    }

    await _interstitialAd!.show(
      onAdDismissed: () {
        _interstitialAd = null;
        _loadInterstitial(); // Pre-load the next one
      },
      onAdFailedToShow: (error) {
        debugPrint("Interstitial failed to show: $error");
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: _showInterstitial,
          child: const Text("Next Level"),
        ),
      ),
    );
  }
}

Step 5: Rewarded Ads

rewarded_example.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class RewardedExample extends StatefulWidget {
  const RewardedExample({super.key});

  @override
  State<RewardedExample> createState() => _RewardedExampleState();
}

class _RewardedExampleState extends State<RewardedExample> {
  RewardedAd? _rewardedAd;

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

  Future<void> _loadRewarded() async {
    final result = await AppSpike.loadRewardedVideoAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      setState(() {
        _rewardedAd = result.ad;
      });
    } else {
      debugPrint("Rewarded ad failed to load: ${result.error}");
    }
  }

  // Call this when the user opts in (e.g., "Watch ad for 50 coins")
  Future<void> _showRewarded() async {
    if (_rewardedAd == null) {
      debugPrint("Rewarded ad not ready yet");
      return;
    }

    await _rewardedAd!.show(
      onUserEarnedReward: (reward) {
        debugPrint("User earned: ${reward.amount} ${reward.type}");
        _grantReward(reward.amount);
      },
      onAdDismissed: () {
        _rewardedAd = null;
        _loadRewarded();
      },
      onAdFailedToShow: (error) {
        debugPrint("Rewarded ad failed to show: $error");
      },
    );
  }

  void _grantReward(int amount) {
    // Your reward logic here
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: _rewardedAd != null ? _showRewarded : null,
          child: const Text("Watch Ad for 50 Coins"),
        ),
      ),
    );
  }
}

Step 6: Native Ads

native_ad_example.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class NativeAdExample extends StatefulWidget {
  const NativeAdExample({super.key});

  @override
  State<NativeAdExample> createState() => _NativeAdExampleState();
}

class _NativeAdExampleState extends State<NativeAdExample> {
  NativeAd? _nativeAd;

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

  Future<void> _loadNativeAd() async {
    final result = await NativeAd.load(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      setState(() {
        _nativeAd = result.ad;
      });
    } else {
      debugPrint("Native ad failed: ${result.error}");
    }
  }

  @override
  void dispose() {
    _nativeAd?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_nativeAd == null) return const SizedBox.shrink();

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                _nativeAd!.iconWidget(width: 40, height: 40),
                const SizedBox(width: 8),
                Expanded(
                  child: Text(
                    _nativeAd!.headline,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 8),
            Text(_nativeAd!.body),
            const SizedBox(height: 8),
            ElevatedButton(
              onPressed: () => _nativeAd!.performClick(),
              child: Text(_nativeAd!.callToAction),
            ),
          ],
        ),
      ),
    );
  }
}

Step 7: App Open Ads

App open ads display when users bring your app to the foreground. Use WidgetsBindingObserver to trigger them automatically:

app_open_ad_manager.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class AppOpenAdManager with WidgetsBindingObserver {
  AppOpenAd? _appOpenAd;

  void init() {
    WidgetsBinding.instance.addObserver(this);
    _loadAppOpenAd();
  }

  Future<void> _loadAppOpenAd() async {
    final result = await AppSpike.loadAppOpenAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      _appOpenAd = result.ad;
    } else {
      debugPrint("App open ad failed to load: ${result.error}");
    }
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed && _appOpenAd != null) {
      _appOpenAd!.show(
        onAdDismissed: () {
          _appOpenAd = null;
          _loadAppOpenAd();
        },
        onAdFailedToShow: (error) {
          debugPrint("App open ad failed to show: $error");
        },
      );
    }
  }

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

Initialize it in your root widget:

my_app.dart
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _appOpenAdManager = AppOpenAdManager();

  @override
  void initState() {
    super.initState();
    _appOpenAdManager.init();
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomeScreen(),
    );
  }
}

Step 8: Rewarded Interstitial Ads

Rewarded interstitials combine the full-screen experience of an interstitial with an opt-in reward. Unlike standard rewarded ads, they can be shown without the user explicitly choosing to watch:

rewarded_interstitial_example.dart
import 'package:flutter/material.dart';
import 'package:appspike_flutter/appspike_flutter.dart';

class RewardedInterstitialExample extends StatefulWidget {
  const RewardedInterstitialExample({super.key});

  @override
  State<RewardedInterstitialExample> createState() =>
      _RewardedInterstitialExampleState();
}

class _RewardedInterstitialExampleState
    extends State<RewardedInterstitialExample> {
  RewardedInterstitialAd? _rewardedInterstitialAd;

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

  Future<void> _loadRewardedInterstitial() async {
    final result = await AppSpike.loadRewardedInterstitialAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      setState(() {
        _rewardedInterstitialAd = result.ad;
      });
    } else {
      debugPrint("Rewarded interstitial failed to load: ${result.error}");
    }
  }

  Future<void> _showRewardedInterstitial() async {
    if (_rewardedInterstitialAd == null) {
      debugPrint("Rewarded interstitial not ready yet");
      return;
    }

    await _rewardedInterstitialAd!.show(
      onUserEarnedReward: (reward) {
        debugPrint("User earned: ${reward.amount} ${reward.type}");
        _grantReward(reward.amount);
      },
      onAdDismissed: () {
        _rewardedInterstitialAd = null;
        _loadRewardedInterstitial();
      },
      onAdFailedToShow: (error) {
        debugPrint("Rewarded interstitial failed to show: $error");
      },
    );
  }

  void _grantReward(int amount) {
    // Your reward logic here
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: _showRewardedInterstitial,
          child: const Text("Continue"),
        ),
      ),
    );
  }
}

Centralized Ad Service

For production apps, manage all ads from a centralized service. This example uses a singleton pattern:

ad_service.dart
import 'package:appspike_flutter/appspike_flutter.dart';
import 'package:flutter/foundation.dart';

class AdService {
  AdService._();
  static final AdService instance = AdService._();

  InterstitialAd? _interstitialAd;
  RewardedAd? _rewardedAd;
  bool _isInitialized = false;

  Future<void> initialize() async {
    final result = await AppSpike.initialize(appId: "YOUR_APP_ID");
    if (result.isSuccess) {
      _isInitialized = true;
      await Future.wait([
        _loadInterstitial(),
        _loadRewarded(),
      ]);
    } else {
      debugPrint("AppSpike init failed: ${result.error}");
    }
  }

  // ── Interstitial ──

  Future<void> _loadInterstitial() async {
    final result = await AppSpike.loadInterstitialAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      _interstitialAd = result.ad;
    }
  }

  Future<void> showInterstitial({VoidCallback? onComplete}) async {
    if (_interstitialAd == null) {
      onComplete?.call();
      return;
    }

    await _interstitialAd!.show(
      onAdDismissed: () {
        _interstitialAd = null;
        _loadInterstitial();
        onComplete?.call();
      },
      onAdFailedToShow: (_) {
        onComplete?.call();
      },
    );
  }

  // ── Rewarded ──

  Future<void> _loadRewarded() async {
    final result = await AppSpike.loadRewardedVideoAd(
      placementId: "YOUR_APPSPIKE_PLACEMENT",
    );
    if (result.isSuccess) {
      _rewardedAd = result.ad;
    }
  }

  bool get isRewardedReady => _rewardedAd != null;

  Future<void> showRewarded({
    required void Function(int amount) onReward,
    VoidCallback? onFail,
  }) async {
    if (_rewardedAd == null) {
      onFail?.call();
      return;
    }

    await _rewardedAd!.show(
      onUserEarnedReward: (reward) => onReward(reward.amount),
      onAdDismissed: () {
        _rewardedAd = null;
        _loadRewarded();
      },
      onAdFailedToShow: (_) => onFail?.call(),
    );
  }
}

Usage from any widget:

// Show interstitial between levels
await AdService.instance.showInterstitial(
  onComplete: () {
    Navigator.pushNamed(context, '/level2');
  },
);

// Show rewarded ad for coins
if (AdService.instance.isRewardedReady) {
  await AdService.instance.showRewarded(
    onReward: (amount) => PlayerData.addCoins(amount),
    onFail: () => debugPrint("Rewarded ad not available"),
  );
}

Testing

Enable test mode during development to avoid serving live ads:

final result = await AppSpike.initialize(
  appId: "YOUR_APP_ID",
  testMode: true,
);
// Test ads will always fill and never charge

Use the built-in ad inspector to debug mediation and demand source selection:

AppSpike.openAdInspector();

The ad inspector shows:

Best Practices

Next Steps