패키지, 저장소 및 앱 카테고리 검색

패키지 상세v3.0.2

pangle_flutter

안드로이드 및 iOS에서 ByteDance Pangle SDK를 지원하는 플러터 플러그인입니다. 예: 스플래시 광고, 보상형 비디오 광고 등.

AndroidiOS
30일 다운로드
159
좋아요
33
Pub 점수
160

패키지 설치

flutter pub add pangle_flutter

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

pangle_flutter는 Android 및 iOS용 바이트댄스 팬글 SDK를 통합하는 플러터 플러그인으로, 스플래시 광고 및 보상 영상 광고와 같은 다양한 광고 형식을 쉽게 구현할 수 있도록 도와줍니다. 이 플러그인은 플러터 애플리케이션에서 광고 요청, 렌더링 및 추적을 관리하기 위한 통합 인터페이스를 제공합니다. 주요 모바일 플랫폼을 모두 지원하며 팬글의 수익화 생태계와의 통합을 간소화합니다. 명확한 문서와 적극적인 유지보수를 통해 고성능 네이티브 광고를 통해 앱이 수익을 창출하도록 돕습니다.

사용 사례

  • 비디오 광고를 통한 모바일 앱 수익화
  • 앱 시작 시 스플래시 광고 표시
  • 보상형 비디오 콘텐츠 통합
  • 인센티브를 통한 사용자 참여도 향상
  • 크로스 플랫폼 광고 캠페인 실행 중

주요 기능

  • 스플래시 및 보상형 비디오 광고 지원
  • 크로스 플랫폼 Android 및 iOS 호환성
  • 광고 통합을 위한 간단한 API
  • 실시간 광고 성과 추적
  • pub.dev를 통한 간편 설정

적합한 대상

  • 광고 수익화를 찾는 플러터 개발자
  • 중국 또는 글로벌 시장을 겨냥한 앱
  • 바이트댄스 팬글 네트워크를 사용하는 출판사
  • 네이티브 광고 형식을 요구하는 프로젝트
  • 고광고 유지율을 목표로 하는 모바일 앱

고려 사항

  • Pangle 계정 및 광고 단위 설정이 필요합니다
  • 안드로이드에서 추가 권한이 필요할 수 있습니다
  • 광고 이용 가능 여부는 지역 및 채우기 비율에 따라 달라집니다
  • 팬글 정책 준수 필요
  • 생산 릴리스 전 테스트 광고로 테스트하기

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

Thanks for non-commercial open source development authorization by JetBrains.

English | 中文

Pangle Flutter SDK

A Flutter plugin integrating the ByteDance Pangle Android and iOS ad SDKs.


Table of Contents

Native platform demos:


Migration Guide

2.x → 3.0
  • SplashView now has two distinct error callbacks instead of one:
    • onError — fires when the ad fails to load (network error, no fill, etc.)
    • onRenderFail — fires when the ad fails to render after loading (template rendering error)
    • If you were using onError to catch all splash failures, add an onRenderFail handler to cover render errors.
  • iOS touchable bounds semantics changed: addTouchableBounds / clearTouchableBounds now directly restrict which areas of the native ad view receive touch events. If the list is empty (default) all touches pass through normally. Previously this was a pass-through whitelist that only activated when FlutterOverlayView was detected — a mechanism that was already a no-op in Flutter 3+.
  • BannerView and FeedView now size themselves automatically from expressSize — no external AspectRatio or fixed height required. SplashView still needs a size-constraining wrapper (Container, SizedBox, Expanded, etc.).
  • Closing the ad (tapping the ✕ button) no longer auto-removes the view — handle removal in onClose.

SDK Versions

The SDK is bundled as a dependency — no manual import needed. To use a different SDK version, fork this project and update the dependency.


Official Documentation


Parameter Reference

See PROPERTY.md for a full description of every configuration parameter.


Screenshots


Integration

1. Add the dependency
dependencies:
  pangle_flutter: latest
2. Platform setup

See SETUP.md for Android manifest changes and iOS Info.plist / CocoaPods configuration.

iOS note: This plugin depends on Ads-CN-Beta/BUAdSDK and Ads-CN-Beta/CSJMediation. These are the beta/mediation variants of the Pangle iOS SDK.

Pure Objective-C projects (iOS): Create an empty Swift file in your project and select Create Bridging Header when prompted. This is required for Swift-based plugins to work.

OC to Swift bridging

Usage

1. Initialization
import 'package:pangle_flutter/pangle_flutter.dart';

// Call before runApp if initializing at startup
WidgetsFlutterBinding.ensureInitialized();

await pangle.init(
  iOS: IOSConfig(appId: kAppId),
  android: AndroidConfig(appId: kAppId),
);

// To enable GroMore mediation:
await pangle.init(
  iOS: IOSConfig(appId: kAppId, useMediation: true),
  android: AndroidConfig(appId: kAppId, useMediation: true),
);

2. Splash Ad

Full-screen (non-PlatformView)

await pangle.loadSplashAd(
  iOS: IOSSplashConfig(slotId: kSplashId, isExpress: false),
  android: AndroidSplashConfig(slotId: kSplashId, isExpress: false),
);

Custom / PlatformView

SplashView(
  iOS: IOSSplashConfig(slotId: kSplashId, isExpress: false),
  android: AndroidSplashConfig(slotId: kSplashId, isExpress: false),
  onLoad: () {},          // Ad loaded successfully
  onShow: () {},          // Ad appeared on screen
  onClick: () {},         // User tapped the ad
  onClose: (type) {},     // Ad dismissed
  onError: (code, msg) {},       // Load failed
  onRenderFail: (code, msg) {},  // Render failed (after load)
);

3. Rewarded Video Ad

Use RewardedAd.load() + ad.show() for one-off load/show, or RewardedAdPool when you want ads preloaded and ready before the user triggers them.

One-off (load then show)

try {
  final ad = await RewardedAd.load(
    slotId: kRewardedVideoId,
    iOS: const IOSRewardedVideoConfig(slotId: kRewardedVideoId),
    android: AndroidRewardedVideoConfig(slotId: kRewardedVideoId),
  );
  // Reaching here means load succeeded
  final result = await ad.show(
    onEvent: (PangleAdEvent event) {
      switch (event) {
        case AdRewardEvent(:final verified):
          if (verified) grantReward();
        case AdClosedEvent():
          // ad dismissed
        default:
          break;
      }
    },
  );
} on AdLoadException catch (e) {
  debugPrint('load failed: $e');
}

Preload pool (recommended for better UX)

// Configure once at startup (e.g. in initState or main())
await RewardedAdPool.instance.configure(
  slotId: kRewardedVideoId,
  poolSize: 2,        // keep 2 ads cached
  autoRefill: true,   // reload automatically after each show
  iOS: const IOSRewardedVideoConfig(slotId: kRewardedVideoId),
  android: AndroidRewardedVideoConfig(slotId: kRewardedVideoId),
);

// Later, when the user earns an ad trigger:
if (await RewardedAdPool.instance.isReady(kRewardedVideoId)) {
  await RewardedAdPool.instance.show(
    slotId: kRewardedVideoId,
    onEvent: (event) {
      if (event case AdRewardEvent(:final verified) when verified) {
        grantReward();
      }
    },
  );
} else {
  // Ad not ready yet — show a "try again later" message
}

4. Fullscreen Video Ad

Follows the same pattern as rewarded video. Use FullscreenAd for one-off or FullscreenAdPool for preloading.

One-off

try {
  final ad = await FullscreenAd.load(
    slotId: kFullscreenVideoId,
    iOS: const IOSFullscreenVideoConfig(slotId: kFullscreenVideoId),
    android: AndroidFullscreenVideoConfig(slotId: kFullscreenVideoId),
  );
  await ad.show(
    onEvent: (event) {
      if (event is AdClosedEvent) Navigator.pop(context);
    },
  );
} on AdLoadException catch (e) {
  debugPrint('load failed: $e');
}

Preload pool

await FullscreenAdPool.instance.configure(
  slotId: kFullscreenVideoId,
  iOS: const IOSFullscreenVideoConfig(slotId: kFullscreenVideoId),
  android: AndroidFullscreenVideoConfig(slotId: kFullscreenVideoId),
);

if (await FullscreenAdPool.instance.isReady(kFullscreenVideoId)) {
  await FullscreenAdPool.instance.show(slotId: kFullscreenVideoId);
}

5. Banner Ad

The close button (✕) no longer auto-removes the view. Handle removal yourself in the appropriate callback.

BannerView applies AspectRatio internally based on expressSize — no wrapper needed.

BannerView(
  iOS: IOSBannerConfig(
    slotId: kBannerExpressId,
    expressSize: PangleExpressSize(width: 600, height: 260),
  ),
  android: AndroidBannerConfig(
    slotId: kBannerExpressId,
    expressSize: PangleExpressSize(width: 600, height: 260),
  ),
  onClick: () {},
  onError: (code, msg) {},
  onRenderFail: (code, msg) {},
)

6. Feed Ad

The close button (✕) no longer auto-removes the item. Handle removal yourself in onDislike.

Request feed ads

// Returns a list of ad keys used to render FeedView widgets
PangleFeedAd feedAd = await pangle.loadFeedAd(
  iOS: IOSFeedConfig(slotId: kFeedId, count: 2),
  android: AndroidFeedConfig(slotId: kFeedId, count: 2),
);
// feedAd.data — list of ad identifiers

Display

Pass the same expressSize used in loadFeedAd — FeedView sizes itself automatically.

final expressSize = PangleExpressSize(width: 375, height: 120);

// Load
PangleAd feedAd = await pangle.loadFeedAd(
  iOS: IOSFeedConfig(slotId: kFeedId, expressSize: expressSize),
  android: AndroidFeedConfig(slotId: kFeedId, expressSize: expressSize),
);

// Render
FeedView(
  id: item.feedId,
  expressSize: expressSize,
  onDislike: (option, enforce) {
    pangle.removeFeedAd([item.feedId]);
    setState(() => items.removeAt(index));
  },
)

Release cached ads

@override
void dispose() {
  pangle.removeFeedAd(feedIds);
  super.dispose();
}

7. Interstitial Ad
final result = await pangle.loadInterstitialAd(
  iOS: IOSInterstitialConfig(
    slotId: kInterstitialId,
    expressSize: PangleExpressSize(width: width, height: height),
  ),
  android: AndroidInterstitialConfig(slotId: kInterstitialId),
);

8. Touchable Bounds (iOS)

addTouchableBounds restricts which areas of a native ad view can receive touch events. When the list is non-empty, only touches within the declared rectangles are forwarded to the native view; all other touches pass through to Flutter widgets underneath.

Note: This API is iOS-only. Android platform views handle touch routing natively.

Container(
  height: 260,
  child: BannerView(
    iOS: IOSBannerConfig(
      slotId: kBannerId,
      expressSize: PangleExpressSize(width: 600, height: 260),
    ),
    android: AndroidBannerConfig(slotId: kBannerId),
    onBannerViewCreated: (BannerViewController controller) {
      // Allow touches only within these screen-coordinate rects
      controller.addTouchableBound(Rect.fromLTWH(0, 0, 300, 260));

      // Or clear all restrictions (all touches pass through to native view)
      controller.clearTouchableBounds();
    },
  ),
),

Use case: You have a floating button that overlaps the ad. Declare only the non-button area as touchable so the button remains tappable while the rest of the ad still receives clicks.

_initTouchableBounds(BannerViewController controller) {
  if (!Platform.isIOS) return;

  final RenderBox buttonBox =
      _floatingButtonKey.currentContext!.findRenderObject() as RenderBox;
  final buttonBound = PangleHelper.fromRenderBox(buttonBox);

  // Allow the ad area excluding the button region
  controller.addTouchableBound(Rect.fromLTWH(
    0,
    buttonBound.top,
    kPangleScreenWidth - buttonBound.width,
    buttonBound.height,
  ));
}

9. Draw Ad

TikTok-style vertical full-screen video ads. Load a batch of IDs, then embed DrawView inside a full-screen PageView for swipeable playback.

// Load
final PangleDrawAd drawAd = await pangle.loadDrawAd(
  iOS: IOSDrawConfig(slotId: kDrawId, adCount: 3),
  android: AndroidDrawConfig(slotId: kDrawId, adCount: 2),
);

// Display
PageView.builder(
  scrollDirection: Axis.vertical,
  itemCount: drawAd.data.length,
  itemBuilder: (context, i) => DrawView(
    id: drawAd.data[i],
    onClick: () {},
    onRenderFail: (code, msg) {},
  ),
);

// Clean up
await pangle.removeDrawAd(drawAd.data);

10. Stream Ad

Returns video metadata for your own custom player — no SDK-rendered view required.

final PangleStreamAd streamAd = await pangle.loadStreamAd(
  iOS: IOSStreamConfig(slotId: kStreamId),
  android: AndroidStreamConfig(slotId: kStreamId, imgSize: PangleSize(width: 640, height: 320)),
);
for (final StreamAdItem item in streamAd.data) {
  // Use item.videoUrl with your video player
  // item.title, item.imageUrl, item.videoDuration, item.description
}

11. EcMall Ad

Commerce-integrated native ad rendered as a platform view. Must be wrapped in a size-constraining widget.

SizedBox(
  width: 600,
  height: 257,
  child: EcMallView(
    slotId: kEcMallId,
    width: 600,
    height: 257,
    onClick: () {},
    onShow: () {},
    onError: (code, msg) {},
  ),
)

12. Feed Icon Ad

Icon-sized feed ad for compact grid or list layouts. Use the standard FeedView widget to render.

final PangleAd iconAd = await pangle.loadFeedIconAd(
  android: AndroidFeedIconConfig(slotId: kFeedIconId, expressViewWidth: 160),
);
FeedView(id: iconAd.data.first)

13. Half-Screen Splash (Android)

Show a splash ad occupying ~4/5 of the screen height instead of full-screen. Android only.

await pangle.loadSplashAd(
  android: AndroidSplashConfig(slotId: kSplashId, isHalfSize: true),
  iOS: IOSSplashConfig(slotId: kSplashId),
);

Contributing

  • For feature requests, open a PR.
  • For bugs or usage questions, open an issue.

Sponsors

BokAugust

카테고리

의존성

의존성 목록 1 개

  • plugin_platform_interface^2.0.2

소스 저장소

이 패키지를 사용하는 프로젝트

아직 데이터가 없습니다.