개요
리워드 광고는 사용자가 광고를 끝까지 시청하면 보상을 받는 형태의 풀스크린 광고입니다. 게임 내 아이템, 추가 콘텐츠 등의 보상을 제공할 때 사용합니다.주요 특징
- 화면 전체를 덮는 풀스크린 광고
- 동영상 광고 지원
- 보상 이벤트 콜백 제공
- 보상 타입과 수량 정보 전달
- 로드와 표시를 분리하여 유연한 타이밍 제어
개발 환경에서는 테스트 유닛 ID를 사용하세요:
PUBLIC_TEST_UNIT_ID_REWARDEDAdropRewardedAd
생성자
복사
AdropRewardedAd({
required String unitId,
AdropRewardedListener? listener,
})
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
unitId | String | Y | 애드컨트롤 콘솔에서 생성한 유닛 ID |
listener | AdropRewardedListener | N | 광고 이벤트 리스너 |
속성
| 속성 | 타입 | 설명 |
|---|---|---|
isLoaded | bool | 광고 로드 완료 여부 |
unitId | String | 광고 유닛 ID |
creativeId | String | 크리에이티브 ID |
txId | String | 트랜잭션 ID |
campaignId | String | 캠페인 ID |
destinationURL | String | 목적지 URL |
메서드
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
load() | Future<void> | 광고를 로드합니다 |
show() | Future<void> | 광고를 화면에 표시합니다 |
dispose() | Future<void> | 리소스를 해제합니다 |
기본 사용법
복사
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('리워드 광고 로드 성공: ${ad.creativeId}');
setState(() {
isLoaded = true;
});
},
onAdFailedToReceive: (ad, errorCode) {
debugPrint('리워드 광고 로드 실패: $errorCode');
},
onAdClicked: (ad) {
debugPrint('리워드 광고 클릭');
},
onAdDidPresentFullScreen: (ad) {
debugPrint('리워드 광고 표시됨');
},
onAdDidDismissFullScreen: (ad) {
debugPrint('리워드 광고 닫힘');
},
onAdFailedToShowFullScreen: (ad, errorCode) {
debugPrint('리워드 광고 표시 실패: $errorCode');
},
onAdEarnRewardHandler: (ad, type, amount) {
debugPrint('보상 획득: 타입=$type, 수량=$amount');
_giveReward(type, amount);
},
),
);
}
void _giveReward(int type, int amount) {
// 보상 지급 로직 구현
// 예: 코인, 아이템, 추가 생명 등
}
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('리워드 광고 예제')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: loadAd,
child: const Text('광고 로드'),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: isLoaded ? showAd : null,
child: const Text('광고 보고 보상 받기'),
),
],
),
),
);
}
}
AdropRewardedListener
리워드 광고 이벤트를 처리하는 리스너입니다.콜백 함수
복사
AdropRewardedListener(
onAdReceived: (AdropAd ad) {
// 광고 로드 성공
},
onAdClicked: (AdropAd ad) {
// 광고 클릭
},
onAdImpression: (AdropAd ad) {
// 광고 노출
},
onAdWillPresentFullScreen: (AdropAd ad) {
// 광고가 표시되기 직전 (iOS 전용)
},
onAdDidPresentFullScreen: (AdropAd ad) {
// 광고가 표시됨
},
onAdWillDismissFullScreen: (AdropAd ad) {
// 광고가 닫히기 직전 (iOS 전용)
},
onAdDidDismissFullScreen: (AdropAd ad) {
// 광고가 닫힘
},
onAdFailedToReceive: (AdropAd ad, AdropErrorCode errorCode) {
// 광고 로드 실패
},
onAdFailedToShowFullScreen: (AdropAd ad, AdropErrorCode errorCode) {
// 광고 표시 실패
},
onAdEarnRewardHandler: (AdropAd ad, int type, int amount) {
// 보상 획득
},
)
콜백 설명
| 콜백 | 설명 |
|---|---|
onAdReceived | 광고 로드 성공 시 호출 |
onAdClicked | 광고 클릭 시 호출 |
onAdImpression | 광고 노출 시 호출 |
onAdWillPresentFullScreen | 광고가 표시되기 직전 호출 (iOS 전용) |
onAdDidPresentFullScreen | 광고가 표시된 후 호출 |
onAdWillDismissFullScreen | 광고가 닫히기 직전 호출 (iOS 전용) |
onAdDidDismissFullScreen | 광고가 닫힌 후 호출 |
onAdFailedToReceive | 광고 로드 실패 시 호출 |
onAdFailedToShowFullScreen | 광고 표시 실패 시 호출 |
onAdEarnRewardHandler | 보상 획득 시 호출 |
보상 처리
보상 콜백
onAdEarnRewardHandler 콜백에서 보상 정보를 받아 처리합니다.
복사
onAdEarnRewardHandler: (ad, type, amount) {
debugPrint('보상 획득: 타입=$type, 수량=$amount');
// 보상 타입에 따른 처리
switch (type) {
case 1:
// 코인 보상
_addCoins(amount);
break;
case 2:
// 아이템 보상
_addItem(amount);
break;
default:
// 기본 보상
_giveDefaultReward(amount);
}
}
보상 지급 시점
보상은 반드시
onAdEarnRewardHandler 콜백에서 지급해야 합니다. 광고가 완전히 시청되기 전에 닫히면 이 콜백이 호출되지 않습니다.복사
class RewardManager {
bool _rewardEarned = false;
void setupRewardedAd() {
rewardedAd = AdropRewardedAd(
unitId: 'YOUR_UNIT_ID',
listener: AdropRewardedListener(
onAdEarnRewardHandler: (ad, type, amount) {
// 보상 플래그 설정
_rewardEarned = true;
},
onAdDidDismissFullScreen: (ad) {
// 광고가 닫힌 후 보상 지급 확인
if (_rewardEarned) {
_giveReward();
_rewardEarned = false;
}
},
),
);
}
}
광고 재생성
리워드 광고는 일회성입니다. 한 번 표시된 광고는 다시 표시할 수 없으므로, 새로운 광고를 로드하려면 인스턴스를 재생성해야 합니다.복사
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) {
// 광고가 닫힌 후 새 광고 준비
_createRewardedAd();
rewardedAd?.load();
},
),
);
setState(() {
isLoaded = false;
isShown = false;
});
}
@override
void dispose() {
rewardedAd?.dispose();
super.dispose();
}
}
에러 처리
복사
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('네트워크 오류가 발생했습니다.');
break;
case AdropErrorCode.adNoFill:
debugPrint('노출할 광고가 없습니다.');
break;
case AdropErrorCode.adShown:
debugPrint('이미 표시된 광고입니다.');
break;
default:
debugPrint('오류 발생: ${error.code}');
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: rewardedAd?.load,
child: const Text('광고 로드'),
),
ElevatedButton(
onPressed: isLoaded ? rewardedAd?.show : null,
child: const Text('광고 보고 보상 받기'),
),
if (errorCode != null)
Text(
'에러: ${errorCode?.code}',
style: const TextStyle(color: Colors.red),
),
],
);
}
}
베스트 프랙티스
1. 미리 로드하기
사용자가 보상을 요청할 때 즉시 광고를 보여줄 수 있도록 미리 로드합니다.복사
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) {
// 다음 광고 미리 로드
_preloadAd();
},
),
);
rewardedAd?.load();
}
void showRewardedAd() {
if (isAdReady) {
rewardedAd?.show();
setState(() {
isAdReady = false;
});
}
}
@override
void dispose() {
rewardedAd?.dispose();
super.dispose();
}
}
2. 사용자에게 보상 안내
광고 시청 전 받을 수 있는 보상을 명확히 안내합니다.복사
void showRewardDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('보상 받기'),
content: const Text('광고를 시청하면 100 코인을 받을 수 있습니다!'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('취소'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
showRewardedAd();
},
child: const Text('광고 보기'),
),
],
),
);
}
3. 광고 가용성 표시
광고가 준비되었을 때만 보상 버튼을 활성화합니다.복사
Widget buildRewardButton() {
return ElevatedButton.icon(
onPressed: isAdReady ? showRewardedAd : null,
icon: const Icon(Icons.play_circle_outline),
label: Text(isAdReady ? '광고 보고 보상 받기' : '광고 로딩 중...'),
style: ElevatedButton.styleFrom(
backgroundColor: isAdReady ? Colors.green : Colors.grey,
),
);
}
4. 보상 지급 신뢰성
보상 지급은 서버에서 검증하는 것이 좋습니다.복사
onAdEarnRewardHandler: (ad, type, amount) async {
try {
// 서버에 보상 지급 요청
await _api.grantReward(
userId: currentUserId,
transactionId: ad.txId,
rewardType: type,
amount: amount,
);
// 로컬 상태 업데이트
_updateLocalBalance(amount);
} catch (e) {
debugPrint('보상 지급 실패: $e');
// 재시도 또는 오류 처리
}
}
5. 리소스 관리
사용하지 않는 광고 인스턴스는 반드시 해제하세요.복사
@override
void dispose() {
rewardedAd?.dispose();
super.dispose();
}
전체 예제
복사
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', // 테스트 유닛 ID
listener: AdropRewardedListener(
onAdReceived: (ad) {
debugPrint('광고 로드 성공');
debugPrint('크리에이티브 ID: ${ad.creativeId}');
debugPrint('캠페인 ID: ${ad.campaignId}');
setState(() {
isLoaded = true;
isLoading = false;
errorCode = null;
});
},
onAdClicked: (ad) {
debugPrint('광고 클릭: ${ad.destinationURL}');
},
onAdImpression: (ad) {
debugPrint('광고 노출: ${ad.creativeId}');
},
onAdWillPresentFullScreen: (ad) {
debugPrint('광고 표시 예정');
},
onAdDidPresentFullScreen: (ad) {
debugPrint('광고 표시됨');
setState(() {
isShown = true;
});
},
onAdWillDismissFullScreen: (ad) {
debugPrint('광고 닫힘 예정');
},
onAdDidDismissFullScreen: (ad) {
debugPrint('광고 닫힘');
// 새 광고 준비
_createRewardedAd();
},
onAdFailedToReceive: (ad, error) {
debugPrint('광고 로드 실패: $error');
setState(() {
isLoaded = false;
isLoading = false;
errorCode = error;
});
},
onAdFailedToShowFullScreen: (ad, error) {
debugPrint('광고 표시 실패: $error');
setState(() {
errorCode = error;
});
},
onAdEarnRewardHandler: (ad, type, amount) {
debugPrint('보상 획득: 타입=$type, 수량=$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('$amount 코인을 획득했습니다!'),
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('리워드 광고 예제'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 코인 표시
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 코인',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
),
const SizedBox(height: 32),
// 로드 버튼
ElevatedButton(
onPressed: isLoading ? null : loadAd,
child: Text(isLoading ? '로딩 중...' : '광고 로드'),
),
const SizedBox(height: 16),
// 표시 버튼
ElevatedButton.icon(
onPressed: isLoaded ? showAd : null,
icon: const Icon(Icons.play_circle_outline),
label: const Text('광고 보고 보상 받기'),
style: ElevatedButton.styleFrom(
backgroundColor: isLoaded ? Colors.green : Colors.grey,
),
),
const SizedBox(height: 16),
// 재설정 버튼
TextButton(
onPressed: (isShown || errorCode != null)
? () => _createRewardedAd()
: null,
child: const Text('재설정'),
),
const SizedBox(height: 24),
// 상태 표시
Text('로드됨: ${isLoaded ? "예" : "아니오"}'),
Text('표시됨: ${isShown ? "예" : "아니오"}'),
// 에러 표시
if (errorCode != null) ...[
const SizedBox(height: 16),
Text(
'에러: ${errorCode?.code}',
style: const TextStyle(color: Colors.red),
),
],
],
),
),
),
);
}
}