> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adrop.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Rewarded Ads

> Learn how to implement rewarded ads in your Flutter app.

## Overview

Rewarded ads are full-screen ads where users receive rewards after watching the entire ad. Use them to provide rewards such as in-game items, additional content, etc.

### Key Features

* Full-screen ads that cover the entire screen
* Video ad support
* Reward event callbacks
* Reward type and amount information
* Separate load and show for flexible timing control

<Note>
  Use the test unit ID in development: `PUBLIC_TEST_UNIT_ID_REWARDED`
</Note>

***

## AdropRewardedAd

### Constructor

```dart theme={null}
AdropRewardedAd({
  required String unitId,
  AdropRewardedListener? listener,
})
```

**Parameters**

| Parameter  | Type                    | Required | Description                               |
| ---------- | ----------------------- | -------- | ----------------------------------------- |
| `unitId`   | `String`                | Y        | Unit ID created in the Ad Control Console |
| `listener` | `AdropRewardedListener` | N        | Ad event listener                         |

### Properties

| Property                        | Type                             | Description                                           |
| ------------------------------- | -------------------------------- | ----------------------------------------------------- |
| `isLoaded`                      | `bool`                           | Whether the ad is loaded                              |
| `unitId`                        | `String`                         | Ad unit ID                                            |
| `creativeId`                    | `String`                         | Creative ID                                           |
| `txId`                          | `String`                         | Transaction ID                                        |
| `campaignId`                    | `String`                         | Campaign ID                                           |
| `destinationURL`                | `String`                         | Destination URL                                       |
| `browserTarget`                 | `BrowserTarget?`                 | Browser target (external or internal)                 |
| `serverSideVerificationOptions` | `ServerSideVerificationOptions?` | Server-side verification options (userId, customData) |

### Methods

| Method      | Return Type    | Description            |
| ----------- | -------------- | ---------------------- |
| `load()`    | `Future<void>` | Loads the ad           |
| `show()`    | `Future<void>` | Shows the ad on screen |
| `dispose()` | `Future<void>` | Releases resources     |

***

## Basic Usage

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

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

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

class _RewardedExampleState extends State<RewardedExample> {
  bool isLoaded = false;
  AdropRewardedAd? rewardedAd;

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

  void _createRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          debugPrint('Rewarded ad loaded: ${ad.creativeId}');
          setState(() {
            isLoaded = true;
          });
        },
        onAdFailedToReceive: (ad, errorCode) {
          debugPrint('Rewarded ad failed to load: $errorCode');
        },
        onAdClicked: (ad) {
          debugPrint('Rewarded ad clicked');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('Rewarded ad presented');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('Rewarded ad dismissed');
        },
        onAdFailedToShowFullScreen: (ad, errorCode) {
          debugPrint('Rewarded ad failed to show: $errorCode');
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          debugPrint('Reward earned: type=$type, amount=$amount');
          _giveReward(type, amount);
        },
      ),
    );
  }

  void _giveReward(int type, int amount) {
    // Implement reward logic
    // e.g., coins, items, extra lives, etc.
  }

  void loadAd() {
    rewardedAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      rewardedAd?.show();
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Rewarded Ad Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: loadAd,
              child: const Text('Load Ad'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: isLoaded ? showAd : null,
              child: const Text('Watch Ad for Reward'),
            ),
          ],
        ),
      ),
    );
  }
}
```

***

## AdropRewardedListener

A listener for handling rewarded ad events.

### Callbacks

```dart theme={null}
AdropRewardedListener(
  onAdReceived: (AdropAd ad) {
    // Ad loaded successfully
  },
  onAdClicked: (AdropAd ad) {
    // Ad clicked
  },
  onAdImpression: (AdropAd ad) {
    // Ad impression
  },
  onAdWillPresentFullScreen: (AdropAd ad) {
    // Ad about to be presented (iOS only)
  },
  onAdDidPresentFullScreen: (AdropAd ad) {
    // Ad presented
  },
  onAdWillDismissFullScreen: (AdropAd ad) {
    // Ad about to be dismissed (iOS only)
  },
  onAdDidDismissFullScreen: (AdropAd ad) {
    // Ad dismissed
  },
  onAdFailedToReceive: (AdropAd ad, AdropErrorCode errorCode) {
    // Ad failed to load
  },
  onAdFailedToShowFullScreen: (AdropAd ad, AdropErrorCode errorCode) {
    // Ad failed to show
  },
  onAdEarnRewardHandler: (AdropAd ad, int type, int amount) {
    // Reward earned
  },
)
```

### Callback Descriptions

| Callback                     | Description                              |
| ---------------------------- | ---------------------------------------- |
| `onAdReceived`               | Called when ad loads successfully        |
| `onAdClicked`                | Called when ad is clicked                |
| `onAdImpression`             | Called when ad is displayed              |
| `onAdWillPresentFullScreen`  | Called before ad is presented (iOS only) |
| `onAdDidPresentFullScreen`   | Called after ad is presented             |
| `onAdWillDismissFullScreen`  | Called before ad is dismissed (iOS only) |
| `onAdDidDismissFullScreen`   | Called after ad is dismissed             |
| `onAdFailedToReceive`        | Called when ad fails to load             |
| `onAdFailedToShowFullScreen` | Called when ad fails to show             |
| `onAdEarnRewardHandler`      | Called when reward is earned             |

***

## Reward Handling

### Reward Callback

Process reward information in the `onAdEarnRewardHandler` callback.

```dart theme={null}
onAdEarnRewardHandler: (ad, type, amount) {
  debugPrint('Reward earned: type=$type, amount=$amount');

  // Handle based on reward type
  switch (type) {
    case 1:
      // Coin reward
      _addCoins(amount);
      break;
    case 2:
      // Item reward
      _addItem(amount);
      break;
    default:
      // Default reward
      _giveDefaultReward(amount);
  }
}
```

### Reward Timing

<Warning>
  Rewards must be granted in the `onAdEarnRewardHandler` callback. This callback is not called if the ad is dismissed before being fully watched.
</Warning>

```dart theme={null}
class RewardManager {
  bool _rewardEarned = false;

  void setupRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdEarnRewardHandler: (ad, type, amount) {
          // Set reward flag
          _rewardEarned = true;
        },
        onAdDidDismissFullScreen: (ad) {
          // Check reward after ad is dismissed
          if (_rewardEarned) {
            _giveReward();
            _rewardEarned = false;
          }
        },
      ),
    );
  }
}
```

***

## Ad Recreation

Rewarded ads are one-time use. Once shown, an ad cannot be displayed again, so you must recreate the instance to load a new ad.

```dart theme={null}
class _RewardedState extends State<RewardedWidget> {
  bool isLoaded = false;
  bool isShown = false;
  AdropRewardedAd? rewardedAd;

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

  void _createRewardedAd() {
    rewardedAd?.dispose();
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdDidPresentFullScreen: (ad) {
          setState(() {
            isShown = true;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          _giveReward(type, amount);
        },
        onAdDidDismissFullScreen: (ad) {
          // Prepare new ad after dismissal
          _createRewardedAd();
          rewardedAd?.load();
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
    });
  }

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

***

## Error Handling

```dart theme={null}
class _RewardedState extends State<RewardedWidget> {
  bool isLoaded = false;
  AdropErrorCode? errorCode;
  AdropRewardedAd? rewardedAd;

  void _createRewardedAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
            errorCode = null;
          });
        },
        onAdFailedToReceive: (ad, error) {
          setState(() {
            isLoaded = false;
            errorCode = error;
          });
          _handleError(error);
        },
        onAdFailedToShowFullScreen: (ad, error) {
          setState(() {
            errorCode = error;
          });
          _handleError(error);
        },
      ),
    );
  }

  void _handleError(AdropErrorCode error) {
    switch (error) {
      case AdropErrorCode.network:
        debugPrint('Network error occurred.');
        break;
      case AdropErrorCode.adNoFill:
        debugPrint('No ads available.');
        break;
      case AdropErrorCode.adShown:
        debugPrint('Ad already shown.');
        break;
      default:
        debugPrint('Error: ${error.code}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: rewardedAd?.load,
          child: const Text('Load Ad'),
        ),
        ElevatedButton(
          onPressed: isLoaded ? rewardedAd?.show : null,
          child: const Text('Watch Ad for Reward'),
        ),
        if (errorCode != null)
          Text(
            'Error: ${errorCode?.code}',
            style: const TextStyle(color: Colors.red),
          ),
      ],
    );
  }
}
```

***

## Best Practices

### 1. Preload Ads

Preload ads so they can be shown immediately when users request rewards.

```dart theme={null}
class GameScreen extends StatefulWidget {
  @override
  State<GameScreen> createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {
  AdropRewardedAd? rewardedAd;
  bool isAdReady = false;

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

  void _preloadAd() {
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isAdReady = true;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          _giveReward(type, amount);
        },
        onAdDidDismissFullScreen: (ad) {
          // Preload next ad
          _preloadAd();
        },
      ),
    );
    rewardedAd?.load();
  }

  void showRewardedAd() {
    if (isAdReady) {
      rewardedAd?.show();
      setState(() {
        isAdReady = false;
      });
    }
  }

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

### 2. Inform Users About Rewards

Clearly communicate the rewards users can receive before watching the ad.

```dart theme={null}
void showRewardDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('Get Reward'),
      content: const Text('Watch an ad to receive 100 coins!'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
            showRewardedAd();
          },
          child: const Text('Watch Ad'),
        ),
      ],
    ),
  );
}
```

### 3. Show Ad Availability

Only enable the reward button when an ad is ready.

```dart theme={null}
Widget buildRewardButton() {
  return ElevatedButton.icon(
    onPressed: isAdReady ? showRewardedAd : null,
    icon: const Icon(Icons.play_circle_outline),
    label: Text(isAdReady ? 'Watch Ad for Reward' : 'Loading Ad...'),
    style: ElevatedButton.styleFrom(
      backgroundColor: isAdReady ? Colors.green : Colors.grey,
    ),
  );
}
```

### 4. Reward Reliability

It's recommended to verify rewards on the server.

```dart theme={null}
onAdEarnRewardHandler: (ad, type, amount) async {
  try {
    // Request reward from server
    await _api.grantReward(
      userId: currentUserId,
      transactionId: ad.txId,
      rewardType: type,
      amount: amount,
    );

    // Update local state
    _updateLocalBalance(amount);
  } catch (e) {
    debugPrint('Failed to grant reward: $e');
    // Retry or handle error
  }
}
```

### 5. Resource Management

Always dispose unused ad instances.

```dart theme={null}
@override
void dispose() {
  rewardedAd?.dispose();
  super.dispose();
}
```

***

## Complete Example

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

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

  @override
  State<RewardedAdPage> createState() => _RewardedAdPageState();
}

class _RewardedAdPageState extends State<RewardedAdPage> {
  bool isLoaded = false;
  bool isLoading = false;
  bool isShown = false;
  int coins = 0;
  AdropErrorCode? errorCode;
  AdropRewardedAd? rewardedAd;

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

  void _createRewardedAd() {
    rewardedAd?.dispose();
    rewardedAd = AdropRewardedAd(
      unitId: 'PUBLIC_TEST_UNIT_ID_REWARDED', // Test unit ID
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          debugPrint('Ad loaded');
          debugPrint('Creative ID: ${ad.creativeId}');
          debugPrint('Campaign ID: ${ad.campaignId}');
          setState(() {
            isLoaded = true;
            isLoading = false;
            errorCode = null;
          });
        },
        onAdClicked: (ad) {
          debugPrint('Ad clicked: ${ad.destinationURL}');
        },
        onAdImpression: (ad) {
          debugPrint('Ad impression: ${ad.creativeId}');
        },
        onAdWillPresentFullScreen: (ad) {
          debugPrint('Ad will present');
        },
        onAdDidPresentFullScreen: (ad) {
          debugPrint('Ad presented');
          setState(() {
            isShown = true;
          });
        },
        onAdWillDismissFullScreen: (ad) {
          debugPrint('Ad will dismiss');
        },
        onAdDidDismissFullScreen: (ad) {
          debugPrint('Ad dismissed');
          // Prepare new ad
          _createRewardedAd();
        },
        onAdFailedToReceive: (ad, error) {
          debugPrint('Ad failed to load: $error');
          setState(() {
            isLoaded = false;
            isLoading = false;
            errorCode = error;
          });
        },
        onAdFailedToShowFullScreen: (ad, error) {
          debugPrint('Ad failed to show: $error');
          setState(() {
            errorCode = error;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          debugPrint('Reward earned: type=$type, amount=$amount');
          setState(() {
            coins += amount;
          });
          _showRewardNotification(amount);
        },
      ),
    );

    setState(() {
      isLoaded = false;
      isShown = false;
      errorCode = null;
    });
  }

  void loadAd() {
    setState(() {
      isLoading = true;
      errorCode = null;
    });
    rewardedAd?.load();
  }

  void showAd() {
    if (isLoaded) {
      rewardedAd?.show();
    }
  }

  void _showRewardNotification(int amount) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('You earned $amount coins!'),
        backgroundColor: Colors.green,
        duration: const Duration(seconds: 2),
      ),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Rewarded Ad Example'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // Coin display
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.amber.shade100,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Icon(Icons.monetization_on, color: Colors.amber),
                    const SizedBox(width: 8),
                    Text(
                      '$coins Coins',
                      style: const TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 32),

              // Load button
              ElevatedButton(
                onPressed: isLoading ? null : loadAd,
                child: Text(isLoading ? 'Loading...' : 'Load Ad'),
              ),
              const SizedBox(height: 16),

              // Show button
              ElevatedButton.icon(
                onPressed: isLoaded ? showAd : null,
                icon: const Icon(Icons.play_circle_outline),
                label: const Text('Watch Ad for Reward'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: isLoaded ? Colors.green : Colors.grey,
                ),
              ),
              const SizedBox(height: 16),

              // Reset button
              TextButton(
                onPressed: (isShown || errorCode != null)
                    ? () => _createRewardedAd()
                    : null,
                child: const Text('Reset'),
              ),
              const SizedBox(height: 24),

              // Status display
              Text('Loaded: ${isLoaded ? "Yes" : "No"}'),
              Text('Shown: ${isShown ? "Yes" : "No"}'),

              // Error display
              if (errorCode != null) ...[
                const SizedBox(height: 16),
                Text(
                  'Error: ${errorCode?.code}',
                  style: const TextStyle(color: Colors.red),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}
```

***

## Server-Side Verification (SSV)

Server-side verification allows you to securely verify reward grants on your server. Set `ServerSideVerificationOptions` to include `userId` and `customData` in the server-to-server callback when the ad is loaded.

<Note>
  To receive SSV callbacks on your server, register a callback URL in the console: **\[Management] > \[Integrations] > Reward Ad SSV**. The `userId` and `customData` set here are included in the encrypted payload sent to that URL. See the [Reward Ad SSV guide](/integrations/ssv) for payload decryption and setup.
</Note>

### Setup

Set `serverSideVerificationOptions` before calling `load()`.

```dart theme={null}
final rewardedAd = AdropRewardedAd(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropRewardedListener(
    onAdReceived: (ad) {
      setState(() {
        isLoaded = true;
      });
    },
    onAdEarnRewardHandler: (ad, type, amount) {
      debugPrint('Reward earned: type=$type, amount=$amount');
      // Verify reward on server
    },
  ),
);

// Set SSV options before calling load()
rewardedAd.serverSideVerificationOptions = ServerSideVerificationOptions(
  userId: 'user_123',
  customData: 'level_5_reward',
);

await rewardedAd.load();
```

### ServerSideVerificationOptions

| Property     | Type      | Description                                          |
| ------------ | --------- | ---------------------------------------------------- |
| `userId`     | `String?` | User identifier for server-side verification         |
| `customData` | `String?` | Custom data string to include in the server callback |

### Full Example

```dart theme={null}
class _RewardedSSVState extends State<RewardedSSVWidget> {
  bool isLoaded = false;
  AdropRewardedAd? rewardedAd;

  void _createRewardedAd() {
    rewardedAd?.dispose();
    rewardedAd = AdropRewardedAd(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropRewardedListener(
        onAdReceived: (ad) {
          setState(() {
            isLoaded = true;
          });
        },
        onAdEarnRewardHandler: (ad, type, amount) {
          // Verify reward on server
          _verifyRewardOnServer(
            transactionId: ad.txId,
            rewardType: type,
            amount: amount,
          );
        },
        onAdDidDismissFullScreen: (ad) {
          // Prepare next ad
          _createRewardedAd();
          rewardedAd?.load();
        },
      ),
    );

    // Set SSV options
    rewardedAd!.serverSideVerificationOptions = ServerSideVerificationOptions(
      userId: currentUserId,
      customData: 'reward_context_data',
    );
  }

  Future<void> _verifyRewardOnServer({
    required String transactionId,
    required int rewardType,
    required int amount,
  }) async {
    // Send reward verification request to server
    try {
      await api.verifyReward(
        transactionId: transactionId,
        rewardType: rewardType,
        amount: amount,
      );
      _updateLocalBalance(amount);
    } catch (e) {
      debugPrint('Reward verification failed: $e');
    }
  }

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

<Warning>
  `serverSideVerificationOptions` must be set before calling `load()`. Options set after `load()` will not be applied to that ad request.
</Warning>

***

## Backfill Ads

When backfill ads are enabled, backfill ads are automatically loaded when direct ads are unavailable. You can handle backfill-related errors using the error code.

```dart theme={null}
rewardedAd = AdropRewardedAd(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropRewardedListener(
    onAdReceived: (ad) {
      debugPrint('Ad loaded: ${ad.creativeId}');
    },
    onAdFailedToReceive: (ad, errorCode) {
      switch (errorCode) {
        case AdropErrorCode.adNoFill:
          debugPrint('No direct ad available');
          break;
        case AdropErrorCode.backfillNoFill:
          debugPrint('No backfill ad available either');
          break;
        default:
          debugPrint('Ad load failed: ${errorCode.code}');
      }
    },
  ),
);
```

<Note>
  To use backfill ads, add the backfill dependency to native platforms. See [Getting Started](/sdk/flutter/overview).
</Note>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Popup Ads" href="/sdk/flutter/popup">
    Implement popup-style ads
  </Card>

  <Card title="Targeting" href="/sdk/flutter/targeting">
    Set up targeting for personalized ads
  </Card>

  <Card title="Reference" href="/sdk/flutter/reference">
    Check types, methods, and error codes
  </Card>
</CardGroup>
