/// Real Adsgram SDK interface using JavaScript interop /// /// This provides a Dart interface to the Adsgram JavaScript SDK. /// The SDK is loaded via script tag in web/index.html: /// /// /// The JavaScript bridge functions are defined in web/foos.js library; import 'dart:developer' as developer; import 'dart:js_util' as js_util; /// Configuration for Adsgram ad display class AdsgramAd { /// Adsgram block ID (required) final String blockId; /// Reward amount (for tracking purposes) final int rewardAmount; /// Callback invoked when ad completes successfully final void Function()? onReward; /// Callback invoked when ad fails or is closed early final void Function(String error)? onError; const AdsgramAd({ required this.blockId, required this.rewardAmount, this.onReward, this.onError, }); } /// Adsgram SDK interface for showing rewarded ads class Adsgram { static final Adsgram _instance = Adsgram._internal(); static Adsgram get instance => _instance; Adsgram._internal(); /// Check if Adsgram SDK is available in the JavaScript environment bool get isAvailable { try { final adsgram = js_util.getProperty(js_util.globalThis, 'Adsgram'); return adsgram != null; } catch (e) { return false; } } /// Show a rewarded ad using the Adsgram JavaScript SDK /// /// This method: /// 1. Validates the ad configuration /// 2. Checks SDK availability /// 3. Sets up JavaScript callbacks for reward/error events /// 4. Calls the JavaScript showAdWithBlockId() function /// 5. Handles ad lifecycle events via callbacks /// /// Throws [Exception] if SDK is not available or ad fails to show /// Throws [ArgumentError] if ad configuration is invalid Future showRewardedAd(AdsgramAd adConfig) async { // Validate block ID if (adConfig.blockId.trim().isEmpty) { final error = 'Block ID is required and cannot be empty'; developer.log(error, name: 'Adsgram'); adConfig.onError?.call(error); throw ArgumentError.value( adConfig.blockId, 'blockId', error, ); } // Check SDK availability if (!isAvailable) { final error = 'Adsgram SDK not available. Make sure sad.min.js is loaded.'; developer.log(error, name: 'Adsgram'); adConfig.onError?.call(error); throw Exception(error); } // Validate that JavaScript bridge functions exist try { final showAdFunction = js_util.getProperty( js_util.globalThis, 'showAdWithBlockId', ); if (showAdFunction == null) { final error = 'JavaScript bridge function showAdWithBlockId not found. Make sure foos.js is loaded.'; developer.log(error, name: 'Adsgram'); adConfig.onError?.call(error); throw Exception(error); } } catch (e) { final error = 'Failed to access JavaScript bridge: $e'; developer.log(error, name: 'Adsgram'); adConfig.onError?.call(error); throw Exception(error); } try { developer.log( 'Showing rewarded ad with block ID: ${adConfig.blockId}', name: 'Adsgram', ); // Set up callbacks in JavaScript before showing ad await _setupCallbacks(adConfig); // Call the JavaScript showAdWithBlockId function // The function is defined in web/foos.js and handles the Adsgram SDK await js_util.promiseToFuture( js_util.callMethod( js_util.globalThis, 'showAdWithBlockId', [adConfig.blockId], ), ); developer.log('Ad show request completed', name: 'Adsgram'); // Note: The actual reward/error callbacks are triggered from JavaScript // when ad completes or fails } catch (e, s) { developer.log( 'Failed to show ad', error: e, stackTrace: s, name: 'Adsgram', ); // If JavaScript call fails, call error callback final errorMessage = 'Failed to show ad: $e'; adConfig.onError?.call(errorMessage); rethrow; } } /// Set up JavaScript callbacks for ad completion /// /// Creates JavaScript wrapper functions that call the Dart callbacks /// These are registered with the JavaScript bridge in foos.js Future _setupCallbacks(AdsgramAd adConfig) async { try { // Create JavaScript function for reward callback final rewardJsFunction = js_util.allowInterop(() { developer.log('Ad reward callback triggered', name: 'Adsgram'); adConfig.onReward?.call(); }); // Create JavaScript function for error callback final errorJsFunction = js_util.allowInterop((dynamic error) { final errorMessage = error?.toString() ?? 'Unknown error'; developer.log( 'Ad error callback triggered: $errorMessage', name: 'Adsgram', ); adConfig.onError?.call(errorMessage); }); // Register callbacks with JavaScript bridge // Check if callback functions exist before calling final setRewardCallback = js_util.getProperty( js_util.globalThis, 'setRewardCallback', ); final setErrorCallback = js_util.getProperty( js_util.globalThis, 'setErrorCallback', ); if (setRewardCallback == null || setErrorCallback == null) { throw Exception( 'JavaScript bridge callback functions not found. Make sure foos.js is loaded.', ); } js_util.callMethod( js_util.globalThis, 'setRewardCallback', [rewardJsFunction], ); js_util.callMethod( js_util.globalThis, 'setErrorCallback', [errorJsFunction], ); developer.log('Callbacks registered with JavaScript bridge', name: 'Adsgram'); } catch (e, s) { developer.log( 'Failed to set up callbacks', error: e, stackTrace: s, name: 'Adsgram', ); rethrow; } } /// Alternative method to show ad with specific block ID /// /// This allows dynamic block ID configuration without creating /// an AdsgramAd object. Prefer using [showRewardedAd] for better /// error handling and callback management. /// /// Throws [Exception] if ad fails to show @Deprecated('Use showRewardedAd() instead for better error handling') Future showAdWithBlockId(String blockId) async { if (!isAvailable) { throw Exception('Adsgram SDK not available'); } try { await js_util.promiseToFuture( js_util.callMethod( js_util.globalThis, 'showAdWithBlockId', [blockId], ), ); } catch (e) { throw Exception('Failed to show ad with block ID $blockId: $e'); } } }