visual_reward 1.1.8 copy "visual_reward: ^1.1.8" to clipboard
visual_reward: ^1.1.8 copied to clipboard

Tenqube Visual Reward SDK for Flutter. A reward-ad webview SDK built on flutter_inappwebview for Android and iOS.

Flutter #

Flutter InAppWebView 기반 리워드 광고 SDK입니다.


요구사항 #

  • Flutter 3.38.1+
  • Dart 3.10.0+
  • iOS 12.0+
  • Android minSdk 21

설치 #

pub.dev 에서 설치합니다.

flutter pub add visual_reward

또는 pubspec.yaml에 직접 추가합니다.

dependencies:
  visual_reward: ^<version>

직접 의존성은 WebView 엔진인 flutter_inappwebview 하나뿐입니다. 기기·앱 정보, 광고 식별자(ADID/IDFA), ATT 요청, 외부 URL·설정 열기, 캐시 경로 등은 SDK가 네이티브(Android Kotlin / iOS Swift) 채널로 직접 처리하므로, 별도 플러그인 의존성이 없어 호스트 앱의 다른 플러그인 버전과 충돌하지 않습니다. flutter_inappwebview는 전이 의존성으로 자동 해석되므로 호스트 pubspec.yaml에 별도로 나열할 필요가 없습니다.

로컬 path 설치 (오프라인·사내 배포 등)

전달받은 visual_reward 폴더를 프로젝트 내 원하는 위치에 복사한 뒤 path 의존성으로 추가합니다.

dependencies:
  visual_reward:
    path: ./visual_reward  # 복사한 폴더 경로

R8 / ProGuard #

SDK에 Consumer ProGuard 규칙(consumer-rules.pro)이 포함되어 있어 호스트 앱의 R8 에 자동 적용됩니다. flutter_inappwebview 도 자체 규칙을 포함하므로, 일반적으로 호스트 앱에 별도 규칙을 추가할 필요가 없습니다.

자동 적용되는 규칙은 다음과 같습니다. (직접 문제가 있을 때 android/app/proguard-rules.pro에 수동으로 넣어도 됩니다.)

# Flutter 플러그인
-keep class com.tenqube.visual_reward.** { *; }

# AdMob WebView API (Class.forName 문자열 리플렉션으로 참조 — R8가 못 봄)
# 호스트 앱에 Google Mobile Ads SDK가 없으면 이 룰은 무시됨
-keep class com.google.android.libraries.ads.mobile.sdk.MobileAds { *; }
-keep class com.google.android.gms.ads.MobileAds { *; }

권한 #

Android #

광고 ID 접근 권한(com.google.android.gms.permission.AD_ID)은 SDK가 의존하는 play-services-ads-identifier가 매니페스트 병합으로 자동 추가하므로, 호스트가 별도로 선언할 필요가 없습니다. (광고 ID를 수집하는 앱이 이 권한을 선언하는 것은 Play 정책상 올바른 동작입니다.)

아동 대상 앱 등 광고 ID 권한을 원치 않는 경우에만 호스트 AndroidManifest.xml에서 명시적으로 제거할 수 있습니다.

<uses-permission android:name="com.google.android.gms.permission.AD_ID"
    tools:node="remove" />

iOS #

IDFA 조회(getIDFA)를 사용하려면 Info.plist에 ATT 사용 목적 문자열을 추가해야 합니다.

<key>NSUserTrackingUsageDescription</key>
<string>맞춤형 광고 제공을 위해 기기 식별자를 사용합니다.</string>

ATT 권한 요청은 SDK가 웹뷰 로드 전 네이티브 ATTrackingManager로 자동 처리합니다. 이미 권한이 결정된 경우 팝업 없이 즉시 진행됩니다. NSUserTrackingUsageDescription이 없으면 요청을 스킵하고 바로 로드합니다.


사용법 #

디버그 설정 #

initialize() 전에 호출합니다.

import 'package:visual_reward/visual_reward.dart';

VisualReward.setDebugEnabled(kDebugMode);

초기화 #

main() 또는 앱 시작 시 한 번만 호출합니다.

import 'package:visual_reward/visual_reward.dart';

VisualReward.initialize(
  appKey: 'tq_live_xxx',
  timeout: const Duration(seconds: 5),  // 기본값
  onSuccess: () { /* SDK 준비 완료 */ },
  onFailure: (e) { /* 초기화 실패 */ },
);

initialize 파라미터 #

파라미터 타입 필수 기본값 설명
appKey String ✅ 앱 식별 키
timeout Duration Duration(seconds: 5) 초기화 완료 대기 시간. 초과 시 VisualRewardInitializeTimeoutError 발생
onSuccess VoidCallback? null 초기화 성공 콜백 (선택)
onFailure void Function(VisualRewardError)? null 초기화 실패 콜백 (선택)

사용자 설정 #

로그인 후 또는 사용자가 변경될 때 호출합니다. open() 전에 반드시 호출해야 합니다.

VisualReward.setUserId(user.id);

웹뷰 열기 #

import 'package:visual_reward/visual_reward.dart';

VisualReward.open(
  context: context,
  serviceName: 'reward_home',
  locale: 'ko',
  extra: null,
  onClose: () { /* 웹뷰 닫힘 */ },
  onError: (e) { /* 열기 실패 */ },
);

open 파라미터 #

파라미터 타입 필수 기본값 설명
context BuildContext ✅ 화면을 열 BuildContext
serviceName String ✅ 서비스 이름
locale String? 'ko' 언어 코드
extra String? null 추가 query parameter (선택)
onClose VoidCallback? null 웹뷰가 닫힐 때 호출되는 콜백 (선택)
onError void Function(VisualRewardError)? null 웹뷰를 열지 못했을 때 호출되는 콜백 (선택)

AdPopcorn 브릿지 등록 (선택) #

AdPopcorn SDK(adpopcornssp_flutter: ^1.0.17)가 있는 경우, open 호출 전에 등록합니다.

import 'package:adpopcornssp_flutter/adpopcornssp_flutter.dart';

VisualReward.setAdPopcornBridgeHandler(AdPopcornInAppWebViewBridgeHandler());

VisualReward.open(
  context: context,
  serviceName: 'reward_home',
  locale: 'ko',
  extra: null,
);

AdPopcorn SDK가 없으면 호출하지 않아도 됩니다.


AdMob WebView API (선택, best-effort) #

호스트 앱에 Google Mobile Ads SDK가 링크돼 있으면, SDK가 리워드 웹뷰를 자동으로 registerWebView에 등록해 WebView 내부 광고(AdSense/GPT/IMA HTML5)의 수익화·측정 신호를 개선합니다. GMA SDK가 없으면 조용히 무시되며, 광고 표시·보상 로직에는 영향이 없습니다.

이 SDK가 하는 일은 리플렉션으로 registerWebView를 호출하는 것뿐입니다(Next-Gen 우선, 없으면 레거시 폴백). SDK 의존성·App ID·초기화는 모두 호스트 앱에서 설정해야 하며, 설정은 (1) 사용하는 SDK 세대와 (2) 호스트가 자체 광고도 서빙하는지에 따라 달라집니다.

1. 의존성 #

  • Flutter (권장, 레거시 세대): google_mobile_ads 플러그인을 추가하면 양 플랫폼에 GMA 심볼이 링크됩니다.

    flutter pub add google_mobile_ads
    
  • Next-Gen (직접 링크): 별도 Flutter 플러그인이 없어 네이티브 의존성을 직접 추가해야 합니다(Android: com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk). 대부분 위의 google_mobile_ads(레거시)로 충분합니다.

2. App ID / 초기화 #

호스트 앱 시작 시 MobileAds를 1회 초기화합니다.

import 'package:flutter/widgets.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(const MyApp());
}

App ID는 아래 두 경우에 따라 설정합니다. 레거시는 WebView 전용 App ID 상수가 없어, WebView 전용이어도 실제 App ID가 필수입니다(없으면 시작 크래시). 이 부담을 없앤 것이 Next-Gen의 WebView 전용 상수입니다.

경우 Android (AndroidManifest.xml) iOS (Info.plist)
WebView 신호 전용 (자체 광고 미서빙) 실제 App ID + INTEGRATION_MANAGER=webview 실제 App ID (GADApplicationIdentifier)
자체 광고 병행 실제 App ID만 (INTEGRATION_MANAGER 생략) 실제 App ID (GADApplicationIdentifier)

Android — android/app/src/main/AndroidManifest.xml

<application>
    <!-- App ID 누락 시 시작 크래시 (AdMob 또는 Ad Manager App ID) -->
    <meta-data
        android:name="com.google.android.gms.ads.APPLICATION_ID"
        android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />
    <!-- WebView 전용일 때만 추가. 자체 광고도 서빙하면 이 메타데이터는 삭제 -->
    <meta-data
        android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
        android:value="webview" />
</application>

iOS — ios/Runner/Info.plist

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>

registerWebView용 Consumer ProGuard(R8) 규칙은 SDK에 포함돼 자동 적용됩니다.

3. 연동 검증 #

registerWebView가 호출되면 네이티브 로그(태그/프리픽스 VisualReward)가 남습니다. (Android: Logcat / iOS: Xcode 콘솔)

등록은 리워드 페이지 로드가 끝난 뒤(iOS: onLoadStop) 네이티브 뷰 계층에서 WebView를 탐색해 수행합니다.

  • 성공: AdMob registerWebView registered
    • iOS: ... registered (WKWebView N개 중 1개 등록)
    • Android: ... registered (<사용된 클래스명>)
  • 미링크: Google Mobile Ads SDK not linked — AdMob WebView API not registered
  • WebView 미검출: AdMob registerWebView skipped (...)
    • iOS: WebView가 여럿이면 화면에 보이는 가장 큰 WebView를 리워드 웹뷰로 간주해 등록하며, 0개일 때만 스킵(WKWebView 미검출)
    • Android: WebView가 정확히 1개일 때만 등록하며, 그 외(0개·복수)에는 스킵(WebView N개 발견)
  • 링크됐으나 실패: (Android) ... registerWebView failed / (iOS) GADMobileAds present but registerWebView unavailable

Google 테스트 페이지로 초록 상태 바까지 확인하려면, 해당 도메인(google.github.io)을 원격 설정 allowedDomains에 추가한 상태에서 그 페이지를 SDK 웹뷰로 로드해야 합니다. (설정 캐시 TTL 1시간 주의)

⚠️ Flutter는 flutter_inappwebview가 내부 WebView 인스턴스를 노출하지 않아, 네이티브 뷰 계층에서 WebView를 탐색해 등록하는 best-effort 방식입니다. 플러그인 버전에 따라 탐색이 실패할 수 있으므로 실제 기기에서 위 로그로 검증하세요.


에러 처리 #

에러 타입 #

에러 타입 설명
VisualRewardNetworkError 네트워크 연결 실패
VisualRewardInitializeTimeoutError initialize() 호출 후 완료까지 타임아웃
VisualRewardInvalidAppKeyError 존재하지 않는 앱 키 (404)
VisualRewardServerError(statusCode) 서버 오류 (5xx)
VisualRewardParseError 응답 파싱 실패
VisualRewardNotInitializedError initialize() 호출 없이 open() 호출
VisualRewardUserIdNotSetError setUserId() 호출 없이 open() 호출

initialize 에러 처리 #

onFailure 콜백에서 에러 타입을 분기합니다.

VisualReward.initialize(
  appKey: 'tq_live_xxx',
  timeout: const Duration(seconds: 5),
  onSuccess: () { /* SDK 준비 완료 */ },
  onFailure: (e) {
    if (e is VisualRewardNetworkError) {
      // 네트워크 연결 실패
    } else if (e is VisualRewardInitializeTimeoutError) {
      // 초기화 타임아웃
    } else if (e is VisualRewardInvalidAppKeyError) {
      // 존재하지 않는 앱 키
    } else if (e is VisualRewardServerError) {
      // 서버 오류: e.statusCode
    } else if (e is VisualRewardParseError) {
      // 응답 파싱 실패
    }
  },
);

open 에러 처리 #

open()은 예외를 throw하지 않고 onError 콜백으로 에러를 전달합니다. 콜백에서 에러 타입을 분기합니다.

VisualReward.open(
  context: context,
  serviceName: 'reward_home',
  locale: 'ko',
  extra: null,
  onError: (e) {
    if (e is VisualRewardNotInitializedError) {
      // initialize() 미호출
    } else if (e is VisualRewardUserIdNotSetError) {
      // setUserId() 미호출
    } else if (e is VisualRewardNetworkError) {
      // 네트워크 연결 실패
    } else if (e is VisualRewardServerError) {
      // 서버 오류: e.statusCode
    }
  },
);

SDK 동작 #

아래 동작은 모두 VisualReward.open으로 열린 웹뷰 화면에만 적용됩니다. 호스트 앱의 다른 화면이나 웹뷰에는 영향을 주지 않습니다.

가로 모드 차단 #

웹뷰 화면은 항상 세로 모드로 고정됩니다. 화면을 닫으면 기존 방향 설정이 자동으로 복원됩니다.

영상 광고 자동재생 #

사용자 제스처 없이 미디어(영상 광고)가 자동으로 인라인 재생됩니다.

  • javaScriptEnabled: true — JavaScript 활성화
  • domStorageEnabled: true — DOM Storage 활성화
  • mediaPlaybackRequiresUserGesture: false — 제스처 없이 자동재생 허용
  • allowsInlineMediaPlayback: true (iOS) — 전체화면 강제 전환 없이 인라인 재생
  • allowsPictureInPictureMediaPlayback: false (iOS) — PIP 비활성화

ATT 권한 요청 (iOS) #

웹뷰 로드 전 네이티브 ATTrackingManager로 추적 권한을 자동으로 요청합니다. 이미 권한이 결정된 경우 팝업 없이 즉시 진행됩니다. NSUserTrackingUsageDescription이 없으면 요청을 스킵하고 바로 로드합니다.

스와이프 백 (iOS) #

웹뷰 내 이전 페이지로 돌아가는 스와이프 백 제스처가 기본으로 활성화되어 있습니다. swipeBackEnabled JS 핸들러로 웹에서 활성화 여부를 제어할 수 있습니다.

Mixed Content 허용 (Android) #

HTTPS 페이지 내 HTTP 리소스 로드를 허용합니다. Android 5.0 이상에서 기본 차단되는 mixed content를 광고 크리에이티브가 정상적으로 로드될 수 있도록 허용합니다.

스크롤 바운스 비활성화 (iOS) #

iOS 웹뷰 스크롤이 콘텐츠 끝에 도달해도 바운스 효과가 발생하지 않습니다(disallowOverScroll: true). Android는 해당 없습니다.

백그라운드 미디어 중단 #

앱이 백그라운드로 진입하면 미디어 재생이 중단됩니다.

  • Android: 이 웹뷰만 pause()(per-instance)로 렌더링·JS를 일시정지, 포그라운드 복귀 시 resume()으로 재개. 전역 pauseTimers()는 호출하지 않아 같은 앱의 전면 광고 화면이 덮을 때 광고 웹뷰가 멈추지 않습니다.
  • iOS: setAllMediaPlaybackSuspended(true)로 미디어 재생 명시적 중단

URL 처리 #

케이스 동작
허용 도메인 링크 내부 웹뷰에서 로드
비허용 도메인 링크 외부 브라우저로 오픈
window.open / target="_blank" 도메인 정책에 따라 내부/외부 분기
intent:// 스킴 (Android) 앱 실행, 미설치 시 browser_fallback_url 또는 Play Store로 이동
mailto:, tel:, itms-apps: 등 비 http(s) 스킴 외부로 전달

허용 도메인은 initialize() 시 서버에서 수신한 allowedDomains 설정이 적용됩니다.


JS Interface #

웹에서 네이티브 기능을 호출하는 인터페이스입니다.

호출 형식 #

// 파라미터 없음
window.flutter_inappwebview.callHandler("fncName");

// 파라미터 있음
window.flutter_inappwebview.callHandler("fncName", JSON.stringify({ ... }));

콜백은 window.nativeCallback 객체로 수신합니다.

window.nativeCallback.fncName = (result) => { ... };

getAppVersion() #

앱 버전을 가져옵니다.

window.flutter_inappwebview.callHandler("getAppVersion");

window.nativeCallback.getAppVersion = (version) => {
  console.log(version); // "1.0.0"
};

getADID() — Android Only #

디바이스 광고 식별자(Advertising ID)를 가져옵니다. iOS에서는 동작하지 않습니다(콜백 없음).

window.flutter_inappwebview.callHandler("getADID");

window.nativeCallback.getADID = (adId) => {
  console.log(adId); // "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
};

getIDFA() — iOS Only #

디바이스 광고 식별자(IDFA)를 가져옵니다. ATT 권한 허용 시 IDFA 반환, 미허용 시 빈 문자열을 반환합니다. Android에서는 동작하지 않습니다(콜백 없음).

window.flutter_inappwebview.callHandler("getIDFA");

window.nativeCallback.getIDFA = (idfa) => {
  console.log(idfa); // "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
};

getIDFV() — iOS Only #

디바이스 벤더 식별자(IDFV)를 가져옵니다. Android에서는 동작하지 않습니다(콜백 없음).

window.flutter_inappwebview.callHandler("getIDFV");

window.nativeCallback.getIDFV = (idfv) => {
  console.log(idfv); // "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
};

getDeviceInfo() #

기기 정보를 JSON 문자열로 가져옵니다.

window.flutter_inappwebview.callHandler("getDeviceInfo");

window.nativeCallback.getDeviceInfo = (deviceInfo) => {
  const info = JSON.parse(deviceInfo);
  // Android: { network: "wifi", model: "SM-S911N", manufacturer: "Samsung", osVersion: "14" }
  // iOS:     { network: "wifi", model: "iPhone16,1", manufacturer: "Apple", osVersion: "17.2" }
};
필드 타입 값
network string "wifi" | "mobile". 연결 없으면 키 생략
model string 기기 모델명
manufacturer string 제조사
osVersion string OS 버전

registerBackKeyListener() — Android Only #

디바이스 Back Key 이벤트 권한을 얻습니다. 등록 후 Back Key가 눌리면 onBackKeyPress가 호출됩니다. iOS는 하드웨어 백키가 없어 항상 "0"(실패)을 즉시 반환합니다.

window.flutter_inappwebview.callHandler("registerBackKeyListener");

window.nativeCallback.registerBackKeyListener = (success) => {
  // "1": 권한 획득 성공 (Android)
  // "0": 권한 획득 실패 (iOS)
};

window.nativeCallback.onBackKeyPress = () => {
  // Back Key 눌렸을 때 처리
};

unregisterBackKeyListener() — Android Only #

Back Key 이벤트 권한을 해제합니다. 호출 후 Back Key는 웹뷰 히스토리 뒤로가기 또는 화면 종료로 동작합니다. iOS는 항상 no-op입니다.

window.flutter_inappwebview.callHandler("unregisterBackKeyListener");

swipeBackEnabled() — iOS Only #

iOS 스와이프 백 기능 활성화 여부를 설정합니다.

window.flutter_inappwebview.callHandler(
  "swipeBackEnabled",
  JSON.stringify({ enabled: false }),
);

finish() #

현재 웹뷰를 종료합니다.

window.flutter_inappwebview.callHandler("finish");

openTrackingSettings() #

앱 설정 화면을 엽니다. 광고 추적 권한을 변경할 수 있도록 안내할 때 사용합니다.

window.flutter_inappwebview.callHandler("openTrackingSettings");

openExternalBrowser() #

외부 브라우저를 엽니다.

window.flutter_inappwebview.callHandler(
  "openExternalBrowser",
  JSON.stringify({ url: "https://example.com" }),
);

pushUrl() #

현재 웹뷰에서 URL을 변경합니다.

window.flutter_inappwebview.callHandler(
  "pushUrl",
  JSON.stringify({
    url: "https://example.com",
    replace: false, // true: history 교체, false: history 스택에 추가 (기본값)
  }),
);

0
likes
0
points
460
downloads

Publisher

verified publishertenqube.com

Weekly Downloads

Tenqube Visual Reward SDK for Flutter. A reward-ad webview SDK built on flutter_inappwebview for Android and iOS.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, flutter_inappwebview

More

Packages that depend on visual_reward

Packages that implement visual_reward