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

Tenqube Visual Reward SDK for Flutter. A content webview SDK built on flutter_inappwebview for Android and iOS.

Flutter SDK #

Flutter InAppWebView 기반 콘텐츠 SDK를 앱에 연동합니다.

준비 #

요구사항 #

  • Flutter 3.24+ / Dart 3.5+
  • iOS 14.0+
  • Android minSdk 24
  • Android Gradle Plugin 8.x / Kotlin Gradle Plugin 2.2 이상

pub.dev #

pubspec.yaml에 아래 의존성을 추가합니다. 최신 버전은 pub.dev에서 확인할 수 있습니다.

dependencies:
  # visual_reward SDK
  visual_reward: <version>

  # GMA SDK: 호스트가 지정한 버전을 그대로 사용합니다. SDK 는 버전을 강제하지 않습니다.
  # google_mobile_ads 가 양 플랫폼에 GMA 를 링크합니다. visual_reward 는 GMA 를 링크하지 않고
  # 런타임에 발견해 사용하므로, 미링크 시 광고만 비활성됩니다.
  google_mobile_ads: ^9.0.0

GMA 버전별 동작 #

google_mobile_ads 광고 기능
5.2.0 이상 정상 동작
5.2.0 미만 Android 광고 비활성. iOS 는 정상
미선언 광고 비활성 (크래시 없음)

AndroidManifest 설정 #

<application> 안에 선언할 meta-data 가 네이티브 광고 사용 여부에 따라 달라집니다. 두 키를 동시에 넣지 마세요.

네이티브 광고를 사용하는 경우

App ID 만 선언하면 됩니다. 등록 방법은 App ID를 참고하세요.

네이티브 광고를 사용하지 않는 경우

App ID 검사를 건너뛰는 키를 넣습니다.

<!-- android/app/src/main/AndroidManifest.xml -->
<!-- 이 키가 없으면 앱 시작 즉시 IllegalStateException 으로 크래시합니다 -->
<meta-data
    android:name="com.google.android.gms.ads.INTEGRATION_MANAGER"
    android:value="webview" />

Info.plist 설정 #

IDFA 수익화에 필요한 키는 광고 사용 여부와 무관하게 넣습니다.

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

네이티브 광고를 사용하는 경우

App ID 만 선언하면 됩니다. 등록 방법은 App ID를 참고하세요.

네이티브 광고를 사용하지 않는 경우

App ID 검사를 건너뛰는 키를 넣습니다. 없으면 앱 시작 시 GADInvalidInitializationException 으로 크래시합니다.

<!-- ios/Runner/Info.plist -->
<key>GADIntegrationManager</key>
<string>webview</string>

사용법 #

동작 흐름 #

initialize()가 서버에서 설정을 로드하고, 이후 open()이 리워드 웹뷰를 엽니다.

sequenceDiagram
    participant A as Host App
    participant B as VisualReward SDK
    participant C as VisualReward 서버
    participant D as WebView 화면

    A->>B: initialize(appKey: ...)
    B->>C: config 요청
    C-->>B: config 응답
    B-->>A: onSuccess()

    A->>B: open(context: ..., serviceName: ..., userId: ...)
    B->>D: 웹뷰 열기

    D->>B: 웹뷰 닫힘
    B-->>A: onClose

디버그 설정 #

initialize() 전에 호출하세요.

import 'package:flutter/foundation.dart';
import 'package:visual_reward/visual_reward.dart';

VisualReward.setDebugEnabled(kDebugMode);

초기화 #

main()에서 한 번만 호출합니다.

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

var rewardReady = false;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  VisualReward.initialize(
    appKey: 'app_key',                    // 발급된 app_key
    adTestMode: false,                    // 테스트 광고 활성화 여부
    timeout: const Duration(seconds: 5),  // 기본값
    onSuccess: () => rewardReady = true,
    onFailure: (e) { /* 초기화 실패 */ },
  );

  runApp(const MyApp());
}

::: info onSuccess/onFailure는 리워드 설정 로드 결과에 대한 콜백입니다. 광고 엔진 초기화는 백그라운드에서 진행되며 실패해도 리워드 기능은 정상 동작합니다(광고만 비활성). :::

VisualReward.initialize 파라미터

파라미터 타입 필수 설명 기본값
appKey String ✔ 발급받은 app_key. stage별로 발급 요청이 필요합니다.
adTestMode bool 테스트 광고 활성화 여부. 광고 엔진 첫 초기화 시점의 값만 적용됩니다. false
timeout Duration open() 이 초기화 완료를 기다리는 상한. 초과 시 open() 이 VisualRewardInitializeTimeoutError 로 실패합니다. 5초
showDefaultErrorUi bool open() 이 실패했을 때 SDK 가 디폴트 에러 페이지를 띄운 뒤 onError 를 호출합니다. 원격 안내 페이지를 먼저 띄우고, 실패하면 앱에 동봉된 HTML 로 떨어집니다. 화면에 표시되는 진단 코드는 디폴트 에러 페이지 code 를 참고하세요. true
onSuccess VoidCallback? 초기화 성공 콜백
onFailure void Function(VisualRewardError)? 초기화 실패 콜백

::: warning 이미 onError 로 안내하고 있다면 showDefaultErrorUi: false 로 끄세요 기본값이 켜져 있어 그대로 두면 안내가 두 번 뜹니다. :::

웹뷰 열기 #

SDK 초기화 성공 이후에 호출합니다.

import 'package:visual_reward/visual_reward.dart';

if (rewardReady) {
  VisualReward.open(
    context: context,
    serviceName: 'anyService', // 서비스 식별자
    userId: user.id,           // 사용자 식별자
    locale: 'ko',
    extra: null,
    onClose: () { /* 웹뷰 닫힘 */ },
    onError: (e) { /* 에러 처리 */ },
  );
}

VisualReward.open 파라미터

파라미터 타입 필수 설명 기본값
context BuildContext ✔ 리워드 웹뷰를 열 BuildContext
serviceName String ✔ 서비스 식별자. 사용할 값은 별도로 전달드립니다. 빈 문자열이면 서비스 구획 없이 사이트 루트로 진입합니다.
userId String ✔ 사용자 식별자. 비어 있으면 VisualRewardUserIdNotSetError 가 발생합니다.
locale String? BCP 47 언어 서브태그(ISO 639-1, 2자리 소문자) "ko"
extra String? 포스트백 추가 데이터(JSON serialize된 문자열)
onClose VoidCallback? 웹뷰가 닫힐 때 호출되는 콜백
onError void Function(VisualRewardError)? 에러 발생 시 호출되는 콜백

::: info 한 번의 open()은 onClose 또는 onError 중 정확히 하나를 받습니다. 화면 트리에서 사라진 BuildContext 로 호출하면 VisualRewardOpenFailedError 로 알립니다. 닫힘 트리거가 여러 번 발생해도 콜백은 한 번만 통보됩니다. :::

호스트 WebView 브릿지 #

호스트 앱이 직접 소유한 WebView의 웹 페이지에서 리워드 웹뷰를 열 수 있습니다. 네이티브에서 브릿지를 붙인 뒤, 웹 페이지에서 호출합니다.

네이티브: 브릿지 부착

import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:visual_reward/visual_reward.dart';

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

  @override
  State<HostWebViewPage> createState() => _HostWebViewPageState();
}

class _HostWebViewPageState extends State<HostWebViewPage> {
  InAppWebViewController? _controller;

  @override
  void dispose() {
    final controller = _controller;
    if (controller != null) VisualReward.detachWebBridge(controller);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InAppWebView(
      initialUrlRequest: URLRequest(url: WebUri('https://your-page.example.com')),
      onWebViewCreated: (controller) {
        _controller = controller;
        // 웹이 브릿지를 호출하기 전에 부착합니다.
        VisualReward.attachWebBridge(controller: controller, context: context);
      },
    );
  }
}
파라미터 타입 필수 설명 기본값
controller InAppWebViewController ✔ 브릿지를 붙일 호스트 WebView 컨트롤러
context BuildContext ✔ 리워드 웹뷰를 열 BuildContext

::: warning attachWebBridge 는 웹이 브릿지를 호출하기 전에 붙어 있어야 합니다. onWebViewCreated 에서 붙이면 항상 안전합니다.

화면을 떠날 때 detachWebBridge 를 잊지 마세요. 핸들러가 컨트롤러에 남아 있으면 사라진 화면의 BuildContext 로 리워드 웹뷰를 열려고 시도합니다. :::

웹: 핸들러 호출

window.flutter_inappwebview.callHandler('visualRewardBridge', JSON.stringify({
  userId: 'u-123',            // 필수: 사용자 식별자
  serviceName: 'anyService',  // 필수: 서비스 식별자
  locale: 'ko',               // 선택: 기본 "ko"
  extra: '',                  // 선택: 포스트백 추가 데이터(JSON serialize된 문자열)
}));
파라미터 타입 필수 설명 기본값
userId string ✔ 사용자 식별자
serviceName string ✔ 서비스 식별자. 사용할 값은 별도로 전달드립니다. 값이 빈 문자열이면 사이트 루트로 진입합니다(키를 생략하거나 문자열이 아니면 INVALID_PAYLOAD).
locale string BCP 47 언어 서브태그 "ko"
extra string 포스트백 추가 데이터(JSON serialize된 문자열)

응답 콜백은 페이지에서 전역 객체로 등록합니다.

window.visualRewardCallback = {
  onClose: function () {},                // 리워드 웹뷰 닫힘
  onError: function (code, message) {},
};

onError의 code는 네이티브 에러 타입에 대응합니다.

code 의미
NETWORK_ERROR 설정 조회 네트워크 오류
INITIALIZE_TIMEOUT open() 이 설정 완료를 기다렸으나 시간 초과
INVALID_APP_KEY 존재하지 않는 app_key
SERVER_ERROR 설정 서버 오류
PARSE_ERROR 설정 응답 파싱 실패
NOT_INITIALIZED 앱이 initialize()를 호출하기 전에 열기 시도
USER_ID_NOT_SET userId 누락 또는 빈 문자열
OPEN_FAILED 리워드 화면 진입 실패
INVALID_PAYLOAD serviceName 누락 또는 JSON 파싱 실패 (브릿지 자체 검증). userId 누락은 USER_ID_NOT_SET 으로 옵니다

브릿지 주입 여부를 확인하고 호출하세요.

if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
  window.flutter_inappwebview.callHandler('visualRewardBridge', JSON.stringify({ /* ... */ }));
}

네이티브 광고 설정 #

네이티브 광고를 서빙하는 데 필요한 설정입니다. App ID는 광고 요청에, app-ads.txt는 이 앱의 광고 인벤토리를 누가 판매할 수 있는지 선언하는 데 쓰입니다. 둘 다 없으면 광고가 채워지지 않습니다.

App ID #

AdMob/Ad Manager App ID가 필요합니다. 없다면 요청해주세요. 양 플랫폼 모두에 선언합니다.

<!-- android/app/src/main/AndroidManifest.xml -->
<application>
    <meta-data
        android:name="com.google.android.gms.ads.APPLICATION_ID"
        android:value="ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy" />
</application>
<!-- ios/Runner/Info.plist -->
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy</string>

<!-- SKAdNetwork (Google 제공 식별자) -->
<key>SKAdNetworkItems</key>
<array><!-- ... --></array>

app-ads.txt #

앱 스토어 등록정보에 있는 개발자 웹사이트의 도메인 루트에 app-ads.txt 를 아래 내용 그대로 게시해주세요(https://<개발자-웹사이트>/app-ads.txt).

::: warning 이미 app-ads.txt 를 운영 중이라면 기존 줄을 지우지 말고 아래 목록을 덧붙이세요. 중복 줄은 제거해도 됩니다. 목록이 갱신되면 다시 전달드립니다. :::

::: details app-ads.txt 전체 내용 (127줄) <<< @/public/app-ads.txt :::

AdPopcorn SSP 브릿지 #

AdPopcorn SDK를 쓰지 않는다면 이 절을 건너뛰세요.

flutter_inappwebview 컨트롤러에 핸들러를 얹어야 하므로 호스트가 인스턴스를 넘겨줘야 합니다. 넘기지 않으면 리워드 웹뷰 안의 AdPopcorn 배너가 서빙되지 않습니다.

import 'package:visual_reward/visual_reward.dart';

// initialize() 전후 어디서든, open() 보다 먼저 한 번만 호출합니다.
VisualReward.setAdPopcornBridgeHandler(AdPopcornInAppWebViewBridgeHandler());
파라미터 타입 필수 설명 기본값
handler dynamic ✔ AdPopcorn SDK의 AdPopcornInAppWebViewBridgeHandler 인스턴스

openWindow 로 띄운 새 화면에서도 사용하려면 (eventSink)

핸들러에 eventSink 를 달아 배달을 SDK에 맡기면, SDK가 살아 있는 화면 전부에 이벤트를 뿌리고 각 화면은 페이로드의 placementId 로 자기 것만 처리합니다.

AdPopcorn 문서에서 복사해 쓰는 핸들러 필드와 _handleNativeEvent 를 이렇게 고칩니다.

class AdPopcornInAppWebViewBridgeHandler {
  InAppWebViewController? _webViewController;

  /// VisualReward 가 주입하는 배달 싱크. null 이면 종전처럼 단일 컨트롤러로 배달합니다.
  void Function(String js)? eventSink;

  void _handleNativeEvent(String data) {
    final js = publishEvent("NativeEvent", data);
    final sink = eventSink;
    if (sink != null) {
      sink(js);
    } else {
      _webViewController?.evaluateJavascript(source: js);
    }
  }
  // … 나머지는 변경 없습니다.
}

문제 해결 #

에러 타입 #

onFailure/onError 콜백에서 is 로 분기합니다.

타입 설명
VisualRewardNetworkError 네트워크 연결 실패
VisualRewardInitializeTimeoutError open() 이 설정 조회를 timeout 까지 기다렸으나 오지 않음. initialize() 의 실패 콜백으로는 오지 않습니다
VisualRewardInvalidAppKeyError 존재하지 않는 app_key
VisualRewardServerError(statusCode) 설정 서버 오류
VisualRewardParseError 설정 응답 파싱 실패
VisualRewardNotInitializedError initialize() 호출 없이 open() 호출
VisualRewardUserIdNotSetError open()의 userId가 빈 문자열
VisualRewardOpenFailedError 리워드 화면 진입 실패 (예: 화면 트리에서 사라진 BuildContext)

디폴트 에러 페이지 code #

showDefaultErrorUi 가 켜져 있을 때 뜨는 화면의 코드입니다. 문구는 사용자가 취할 수 있는 행동 기준으로 3종이고, 화면 하단의 code 는 원인별로 다릅니다. 사용자가 알려준 코드로 원인을 좁힐 수 있습니다.

code 원인 안내 문구
N01 open() 의 userId 가 비어 있음 필수 정보가 존재하지 않습니다.
N02 open JS 인터페이스 요청 페이로드 오류 〃
N03 네트워크 오류(DNS·연결 실패), 잘못된 serviceName 혹은 누락, 페이지 로드 실패 네트워크에 연결할 수 없습니다.
N04 open() 이 설정 조회를 timeout 안에 받지 못함 〃
N05 initialize() 전 open() 호출 일시적인 오류가 발생했습니다. (앱 재시작 안내)
N06 설정 서버 오류 일시적인 오류가 발생했습니다.
N07 설정 응답 파싱 실패 〃
N08 존재하지 않는 앱 키 〃
N09 open() 에 넘긴 BuildContext 가 화면 트리에서 사라짐 〃

증상별 점검 #

::: details iOS 빌드가 deployment target 오류로 실패해요 Podfile 의 platform :ios, '14.0' 과 Xcode 에서 Runner 타겟의 Minimum Deployments 를 양쪽 다 14.0 으로 올려야 합니다. Flutter 템플릿 기본값이 13.0 이라 Podfile 만 고치면 실패합니다. :::

::: details AGP 9 · Kotlin 2.1.x 에서 Android 빌드가 실패해요 AGP 9 는 필수 의존성 flutter_inappwebview 가 지원하지 않고, Kotlin Gradle Plugin 2.1.x 로는 플러그인 의존성 컴파일이 실패합니다. android/settings.gradle.kts 에서 AGP 8.x / KGP 2.2 이상을 확인하세요. :::

::: details 광고가 나오지 않아요 google_mobile_ads 선언 여부와 링크되는 GMA 아티팩트 버전(GMA 버전별 동작), 양 플랫폼의 App ID를 확인하세요. 비활성 사유는 VisualReward 로그 태그에 남습니다. :::

::: details 네이티브 광고만 서빙되지 않아요 호스트가 MobileAds 를 직접 초기화하고 있는지 확인하세요. 광고 엔진 초기화는 SDK 가 담당합니다. 호스트가 먼저 초기화하면 그때의 App ID 가 유지되어 SDK 의 설정이 무시될 수 있습니다. :::

::: details VisualRewardNotInitializedError 가 발생해요 initialize() 성공 이후에 open()·브릿지를 호출하세요. :::

::: details VisualRewardInitializeTimeoutError 가 발생해요 네트워크 상태를 확인하고, 필요하면 timeout 을 늘리세요. 타임아웃은 대기를 끊은 것일 뿐 조회 자체는 계속됩니다. 조회가 늦게라도 성공하면 그 설정이 채택되므로 다음 open() 은 정상적으로 열립니다. :::

::: details VisualRewardOpenFailedError 가 발생해요 화면 트리에 살아 있는 BuildContext 로 호출했는지 확인하세요. :::

::: details 페이지에 브릿지가 없어요 페이지가 브릿지를 호출하는 시점보다 attachWebBridge 가 먼저인지 확인하세요. onWebViewCreated 에서 붙이면 항상 먼저입니다. :::

::: details 브릿지를 호출해도 아무 반응이 없어요 호출한 페이지의 오리진이 allowedDomains에 포함돼 있는지 확인하세요. 이 값은 app_key 발급 시 지정되므로 발급 담당자에게 확인하세요. 검증에 실패하면 콜백 없이 로그만 남습니다. :::

::: details VisualRewardNetworkError 가 발생해요 설정 조회가 네트워크 단계에서 실패한 경우입니다. VisualReward 로그의 원인을 먼저 확인하세요.

호스트를 찾지 못했다는 오류(Unable to resolve host / cannot find host)면 DNS 조회 실패입니다. 서버 장애가 아니라 기기 쪽 문제이므로 다음을 확인하세요.

  • 기기의 비공개 DNS(Private DNS) 설정
  • 광고 차단 앱 · VPN 사용 여부 (도메인에 reward 가 포함돼 차단 목록에 걸리는 사례가 있습니다)
  • 사내망 · 학교망 프록시 정책
  • 브라우저에서 https://visual.reward.tenqube.com 접속 가능 여부

일시적인 실패라면 다음 open() 이 설정을 자동으로 다시 조회하므로 앱 재시작 없이 회복됩니다. :::

해결되지 않으면 setDebugEnabled(true) 로 로그를 켠 뒤 VisualReward 태그 로그와 함께 문의해주세요.

0
likes
0
points
460
downloads

Publisher

verified publishertenqube.com

Weekly Downloads

Tenqube Visual Reward SDK for Flutter. A content 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