BiddingStack Flutter SDK Integration Guide
BiddingStack Flutter SDK provides cross-platform header bidding for iOS and Android with Google Ad Manager (GAM) integration. It wraps the native iOS and Android SDKs via platform channels. View Changelog.
| Component | Minimum Version |
|---|---|
| Flutter | 3.3+ |
| Dart | 3.9+ |
| iOS | 15.6+ |
| Android Min SDK | 23 |
Add the BiddingStack Flutter packages to your pubspec.yaml:
dependencies:
biddingstack_core:
path: ../biddingstack-flutter-core
biddingstack_gam_adapter:
path: ../biddingstack-flutter-gam-adapter
Add to your Info.plist:
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX</string>
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>22mmun2rn5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2fnua5tdw4.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>2u9pt9hc89.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qcr597p9d.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3qy4746246.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3rd42ekr43.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>3sh42y64q3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4468km3ulz.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>44jx6755aq.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>47vhws6wlr.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4dzt52r2t5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>4fzdc2evr5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>578prtvx9j.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>7ug5zh24hu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8c4e2ghe7u.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>8s468mfl3y.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>97r2b46745.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>9t245vhmpl.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>a2p9lx4jpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>c3frkrj4fj.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>c6k4g5qg8m.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cp8zw746q7.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>e5fvkxwrpn.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>f38h382jlk.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>gta9lk7p23.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>hs6bdukanm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>k674qkevps.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>kbd757ywx3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>kbmxgpxpgc.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>klf5c3l5u5.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ludvb6z3bs.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>mlmmfzh3r3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>n38lu8286q.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>p78axxw29g.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ppxm28t8ap.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>s39g8k73mm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>su67r6k2v3.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>t38b2kh725.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>tl55sbb4fm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>uw77j35x4d.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v4nxqhlyqp.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v72qych5uu.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>v9wttpbfk9.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>vutu7akeur.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>wg4vff78zm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>wzmmz9fp6w.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>y5ghdn5j9k.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>yclnxrl5pm.skadnetwork</string>
</dict>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>ydx93a7ass.skadnetwork</string>
</dict>
</array>
| SKAdNetwork ID | Company |
|---|---|
cstr6suwn9 | |
4fzdc2evr5 | Aarki |
2fnua5tdw4 | AppLovin |
ydx93a7ass | Adikteev |
p78axxw29g | ByteDance (Pangle) |
v72qych5uu | Appier |
c6k4g5qg8m | Beeswax (Freewheel) |
hs6bdukanm | Criteo |
mlmmfzh3r3 | Appreciate |
wzmmz9fp6w | Snap Inc. |
yclnxrl5pm | Jampp |
t38b2kh725 | Lifestreet |
7ug5zh24hu | Liftoff (Vungle) |
9t245vhmpl | Moloco |
44jx6755aq | Persona.ly |
4468km3ulz | Kayzen |
tl55sbb4fm | PubNative (Verve Group) |
2u9pt9hc89 | Remerge |
8s468mfl3y | RTB House |
klf5c3l5u5 | Sift |
ppxm28t8ap | Smadex |
3rd42ekr43 | YouAppi |
| Others | Google (Demand Partners) |
Update AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX"/>
</application>
Initialize BiddingStack from Dart before rendering any ads. Use await to ensure the remote config is loaded before building the widget tree:
import 'package:biddingstack_core/services.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await BiddingStack.initialize('YOUR_BS_ACCOUNT_ID', enableTestMode: true);
runApp(const MyApp());
}
Important: iOS and Android use separate BiddingStack account IDs and config IDs. See Platform-Specific Configuration for how to pass the correct values per platform.
| Parameter | Type | Default | Description |
|---|---|---|---|
accountId | String | required | Your BiddingStack Account ID |
appBundle | String | '' | App bundle ID (auto-detected if empty) |
appName | String | '' | App name (resolved from config if empty) |
appStoreId | String | '' | App Store / Play Store ID (resolved from config if empty) |
timeout | int | 2000 | Bid request timeout in ms |
enableTestMode | bool | false | Enable debug logging and test mode |
currency | String | 'USD' | Currency code for bid requests |
enableCMP | bool | true | Auto-read IAB CMP strings from UserDefaults/SharedPreferences |
requestATTPermission | bool | true | iOS only: prompt for App Tracking Transparency permission during initialization |
Note: App bundle, name, store ID, and GAM ad unit paths are resolved automatically from the remote config. You only need to pass the account ID.
final bannerAdUnit = BiddingStackBannerAdUnit(
configId: 'YOUR_CONFIG_ID',
gamAdUnitId: 'YOUR_GAM_AD_UNIT_ID', // optional fallback if config API is unavailable
adSizes: const [BiddingStackAdSize(300, 250)],
autoRefreshEnabled: true,
);
BiddingStackGAMBannerView(
adUnit: bannerAdUnit,
onAdLoaded: () => debugPrint('Banner loaded'),
onAdFailedToLoad: (error) => debugPrint('Banner failed: \$error'),
)
| Parameter | Type | Default | Description |
|---|---|---|---|
configId | String | required | Placement config ID |
gamAdUnitId | String | '' | GAM ad unit path (resolved from config if empty) |
adSizes | List<BiddingStackAdSize> | required | Ad sizes |
autoRefreshEnabled | bool | false | Auto-refresh ads |
lazyLoadEnabled | bool | false | Lazy load when visible |
isAutoLoadAd | bool | true | Auto-load on creation |
formats | List<BiddingStackFormat> | [banner] | banner, video |
final videoAdUnit = BiddingStackBannerAdUnit(
configId: 'YOUR_VIDEO_CONFIG_ID',
adSizes: [BiddingStackAdSize(300, 250)],
formats: [BiddingStackFormat.video],
);
BiddingStackIMAVideoRendererBannerView(
adUnit: videoAdUnit,
onAdLoaded: () => debugPrint('Video loaded'),
onAdPlaybackCompleted: () => debugPrint('Video complete'),
)
// Google interstitial size: 320x480 phone, 768x1024 tablet
final interstitialAdUnit = BiddingStackInterstitialAdUnit(
configId: 'YOUR_CONFIG_ID',
size: BiddingStackAdSize(320, 480),
formats: [BiddingStackFormat.banner, BiddingStackFormat.video],
);
// Load and show via GAM
BiddingStackGAMAdapter.loadAndShowInterstitialAdByGAM(interstitialAdUnit);
// Load
BiddingStackGAMAdapter.loadInterstitialAdWithGAMBannerProvider(
interstitialAdUnit,
GAMBannerProvider(adUnitId: 'AD_UNIT_ID', sizes: [BiddingStackAdSize(320, 480)]),
);
// Show when ready
BiddingStackGAMAdapter.showInterstitialAdWithGAMBannerProvider(interstitialAdUnit);
App open ads show at natural app-entry points, such as launch or returning from the background. The SDK preloads the ad, sizes it for the device (320x480 phone, 768x1024 tablet), and preloads the next one after each show.
// Preload early, e.g. at app start
BiddingStackGAMAdapter.loadAppOpenAd('YOUR_CONFIG_ID', 'AD_UNIT_ID');
// Show when the app returns to the foreground
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
BiddingStackGAMAdapter.showAppOpenAdIfAvailable();
}
}
}
showAppOpenAdIfAvailable() returns true when an ad was shown. If no ad is ready, it starts loading one for the next opportunity and returns false.
final rewardedAdUnit = BiddingStackRewardedAdUnit(
configId: 'YOUR_CONFIG_ID',
size: BiddingStackAdSize(300, 250),
);
BiddingStackGAMAdapter.loadAndShowRewardedAdByGAM(rewardedAdUnit);
final nativeAdUnit = BiddingStackNativeAdUnit(
configId: 'YOUR_CONFIG_ID',
);
final ad = await BiddingStack.getNativeAd(nativeAdUnit);
if (ad != null) {
print(ad.title);
print(ad.body);
print(ad.callToAction);
}
| Property | Type | Description |
|---|---|---|
title | String? | Ad headline |
body | String? | Ad body text |
callToAction | String? | CTA button text |
sponsoredBy | String? | Sponsor/advertiser name |
iconURL | String? | Icon image URL |
mainImageURL | String? | Main image URL |
clickURL | String | Click-through URL |
await BiddingStack.preloadBid(bannerAdUnit);
// Later, the ad view uses the cached bid automatically
BiddingStack.setUserId("1234");
BiddingStack.setUserGender("M");
BiddingStack.setUserYob(1980);
BiddingStack.setUserLatLng(34.0522, -118.2437);
BiddingStack.setAppStoreUrl("https://apps.apple.com/app/id123456");
BiddingStack.setAppCategories(["IAB1", "IAB2"]);
BiddingStack.setExternalUserIds({"partner": "uid_abc"});
BiddingStack.setSchainObject('{"ver":"1.0","complete":1,"nodes":[...]}');
BiddingStack.setBuyerUid("buyer_123");
// Custom targeting
BiddingStack.addTargetingKV("interest", "tech");
BiddingStack.addTargetingKV("interest", ["tech", "gaming"]);
BiddingStack.removeTargetingKey("interest");
BiddingStack.clearTargetingKV();
Pass the web URL of the content the user is currently viewing so it is included in every bid request as app.content.url. This is important for brand safety and yield: it gives bidders page-level context to verify where their ads run, which increases bid rates and CPMs. Call it before the screen's ad widgets build — e.g. in initState — and update it on each screen change; pass null to clear.
@override
void initState() {
super.initState();
BiddingStack.setContentUrl("https://www.example.com/article/12345");
}
Available since SDK 26.7.1.
BiddingStack.setCoppa(true);
BiddingStack.setUSPrivacy("1YNN");
BiddingStack.setGppConsent("gpp_string", [1, 2]);
BiddingStack.setGDPR("USER_CONSENT_STRING", true);
BiddingStack.setIdfa("IDFA_STRING"); // iOS only
final bannerAdUnit = BiddingStackBannerAdUnit(
configId: 'YOUR_CONFIG_ID',
adSizes: const [BiddingStackAdSize(300, 250)],
deals: [BiddingStackDeal(id: 'deal_123', bidfloor: 5.50, bidfloorcur: 'USD')],
);
If iOS and Android use different account IDs or config IDs:
import 'dart:io' show Platform;
await BiddingStack.initialize(
Platform.isIOS ? 'IOS_ACCOUNT_ID' : 'ANDROID_ACCOUNT_ID',
enableTestMode: true,
);
final bannerAdUnit = BiddingStackBannerAdUnit(
configId: Platform.isIOS ? 'ios_config_id' : 'android_config_id',
adSizes: [BiddingStackAdSize(300, 250)],
);
BiddingStack.enableLogging = true;