메인 콘텐츠로 건너뛰기

개요

배너 광고는 화면의 일부 영역에 표시되는 직사각형 광고입니다. AdropBannerView 위젯을 사용하여 손쉽게 구현할 수 있습니다.

주요 특징

  • 화면 상단, 하단 또는 중간에 고정 배치 가능
  • 이미지 및 동영상 광고 지원
  • 콜백을 통한 광고 이벤트 처리
  • 광고 메타데이터 제공 (크리에이티브 ID, 캠페인 ID 등)
개발 환경에서는 테스트 유닛 ID를 사용하세요: PUBLIC_TEST_UNIT_ID_320_100

AdropBannerView

생성자

AdropBannerView({
  required String unitId,
  AdropBannerListener? listener,
})
파라미터
파라미터타입필수설명
unitIdStringY애드컨트롤 콘솔에서 생성한 유닛 ID
listenerAdropBannerListenerN광고 이벤트 리스너

속성

속성타입설명
creativeSizeCreativeSize?광고 크리에이티브 크기
adSizeSize?배너 뷰의 크기 설정

메서드

메서드반환 타입설명
load()Future<void>광고를 로드합니다
dispose()Future<void>리소스를 해제합니다

기본 사용법

import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

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

  @override
  State<BannerExample> createState() => _BannerExampleState();
}

class _BannerExampleState extends State<BannerExample> {
  bool isLoaded = false;
  late AdropBannerView bannerView;

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

    bannerView = AdropBannerView(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropBannerListener(
        onAdReceived: (unitId, metadata) {
          debugPrint('배너 광고 수신 성공: $unitId');
          setState(() {
            isLoaded = true;
          });
        },
        onAdClicked: (unitId, metadata) {
          debugPrint('배너 광고 클릭: $unitId');
        },
        onAdImpression: (unitId, metadata) {
          debugPrint('배너 광고 노출: $unitId');
        },
        onAdFailedToReceive: (unitId, errorCode) {
          debugPrint('배너 광고 수신 실패: $unitId, $errorCode');
          setState(() {
            isLoaded = false;
          });
        },
      ),
    );

    // 광고 로드
    bannerView.load();
  }

  @override
  void dispose() {
    bannerView.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('배너 광고 예제')),
      body: Column(
        children: [
          // 메인 콘텐츠
          Expanded(
            child: Center(
              child: const Text('메인 콘텐츠'),
            ),
          ),
          // 배너 광고
          if (isLoaded)
            SizedBox(
              width: MediaQuery.of(context).size.width,
              height: 80,
              child: bannerView,
            ),
        ],
      ),
    );
  }
}

AdropBannerListener

배너 광고 이벤트를 처리하는 리스너입니다.

콜백 함수

AdropBannerListener(
  onAdReceived: (String unitId, Map<String, dynamic>? metadata) {
    // 광고 수신 성공
  },
  onAdClicked: (String unitId, Map<String, dynamic>? metadata) {
    // 광고 클릭
  },
  onAdImpression: (String unitId, Map<String, dynamic>? metadata) {
    // 광고 노출
  },
  onAdFailedToReceive: (String unitId, AdropErrorCode errorCode) {
    // 광고 수신 실패
  },
)

콜백 설명

콜백설명
onAdReceived광고 수신 성공 시 호출
onAdClicked광고 클릭 시 호출
onAdImpression광고 노출 시 호출
onAdFailedToReceive광고 수신 실패 시 호출

메타데이터

onAdReceived, onAdClicked, onAdImpression 콜백에서 메타데이터를 받을 수 있습니다.
onAdReceived: (unitId, metadata) {
  debugPrint('크리에이티브 ID: ${metadata?['creativeId']}');
  debugPrint('트랜잭션 ID: ${metadata?['txId']}');
  debugPrint('캠페인 ID: ${metadata?['campaignId']}');
  debugPrint('목적지 URL: ${metadata?['destinationURL']}');
  debugPrint('브라우저 타겟: ${metadata?['browserTarget']}');
}
필드타입설명
creativeIdString크리에이티브 ID
txIdString트랜잭션 ID
campaignIdString캠페인 ID
destinationURLString목적지 URL
browserTargetint?브라우저 타겟 (0: 외부 브라우저, 1: 인앱 브라우저)
크리에이티브 크기는 메타데이터가 아닌 bannerView.creativeSize?.widthbannerView.creativeSize?.height로 접근합니다.

광고 크기

배너 광고는 유닛에 설정한 크기에 맞춰 컨테이너 크기를 지정해야 합니다.

일반적인 배너 크기

크기테스트 유닛 ID용도
320 x 50PUBLIC_TEST_UNIT_ID_320_50소형 배너
320 x 100PUBLIC_TEST_UNIT_ID_320_100중형 배너

고정 크기 사용

SizedBox(
  width: 320,
  height: 100,
  child: bannerView,
)

화면 너비에 맞추기

SizedBox(
  width: MediaQuery.of(context).size.width,
  height: 80,
  child: bannerView,
)

크기 설정

adSize 속성을 사용하여 배너 뷰의 크기를 명시적으로 설정할 수 있습니다.
@override
void initState() {
  super.initState();

  bannerView = AdropBannerView(
    unitId: 'YOUR_UNIT_ID',
    listener: AdropBannerListener(
      onAdReceived: (unitId, metadata) {
        setState(() {
          isLoaded = true;
        });
      },
    ),
  );

  // 컨텍스트가 준비된 후 크기 설정
  WidgetsBinding.instance.addPostFrameCallback((_) {
    bannerView.adSize = Size(
      MediaQuery.of(context).size.width,
      80,
    );
  });
}

에러 처리

광고 로드 실패 시 적절한 에러 처리를 구현하세요.
class _BannerExampleState extends State<BannerExample> {
  bool isLoaded = false;
  AdropErrorCode? errorCode;
  late AdropBannerView bannerView;

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

    bannerView = AdropBannerView(
      unitId: 'YOUR_UNIT_ID',
      listener: AdropBannerListener(
        onAdReceived: (unitId, metadata) {
          setState(() {
            isLoaded = true;
            errorCode = null;
          });
        },
        onAdFailedToReceive: (unitId, error) {
          setState(() {
            isLoaded = false;
            errorCode = error;
          });
        },
      ),
    );

    bannerView.load();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        if (isLoaded)
          SizedBox(
            width: MediaQuery.of(context).size.width,
            height: 80,
            child: bannerView,
          )
        else if (errorCode != null)
          Text('광고 로드 실패: ${errorCode?.code}'),
      ],
    );
  }

  @override
  void dispose() {
    bannerView.dispose();
    super.dispose();
  }
}

모범 사례

1. 리소스 해제

배너 뷰가 더 이상 필요하지 않을 때 반드시 dispose()를 호출하세요.
@override
void dispose() {
  bannerView.dispose();
  super.dispose();
}

2. 광고 새로고침

필요에 따라 광고를 새로고침할 수 있습니다.
void refreshAd() {
  setState(() {
    isLoaded = false;
  });
  bannerView.load();
}

3. 조건부 렌더링

광고가 로드될 때까지 배너 영역을 숨기거나 플레이스홀더를 표시하세요.
Widget buildBanner() {
  if (isLoaded) {
    return SizedBox(
      width: MediaQuery.of(context).size.width,
      height: 80,
      child: bannerView,
    );
  } else if (isLoading) {
    return const SizedBox(
      height: 80,
      child: Center(child: CircularProgressIndicator()),
    );
  } else {
    return const SizedBox.shrink();
  }
}

4. 여러 배너 관리

여러 배너를 사용할 때는 각각의 상태를 관리하세요.
class _MultiBannerState extends State<MultiBanner> {
  late AdropBannerView topBanner;
  late AdropBannerView bottomBanner;
  bool isTopLoaded = false;
  bool isBottomLoaded = false;

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

    topBanner = AdropBannerView(
      unitId: 'TOP_UNIT_ID',
      listener: AdropBannerListener(
        onAdReceived: (_, __) => setState(() => isTopLoaded = true),
      ),
    );

    bottomBanner = AdropBannerView(
      unitId: 'BOTTOM_UNIT_ID',
      listener: AdropBannerListener(
        onAdReceived: (_, __) => setState(() => isBottomLoaded = true),
      ),
    );

    topBanner.load();
    bottomBanner.load();
  }

  @override
  void dispose() {
    topBanner.dispose();
    bottomBanner.dispose();
    super.dispose();
  }

  // ...
}

전체 예제

import 'package:flutter/material.dart';
import 'package:adrop_ads_flutter/adrop_ads_flutter.dart';

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

  @override
  State<BannerAdPage> createState() => _BannerAdPageState();
}

class _BannerAdPageState extends State<BannerAdPage> {
  static const double bannerHeight = 80;
  bool isLoaded = false;
  bool isLoading = false;
  AdropErrorCode? errorCode;
  late AdropBannerView bannerView;

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

    bannerView = AdropBannerView(
      unitId: 'PUBLIC_TEST_UNIT_ID_320_100', // 테스트 유닛 ID
      listener: AdropBannerListener(
        onAdReceived: (unitId, metadata) {
          debugPrint('광고 수신 성공');
          debugPrint('크리에이티브 ID: ${metadata?['creativeId']}');
          debugPrint('캠페인 ID: ${metadata?['campaignId']}');
          debugPrint('크리에이티브 크기: ${bannerView.creativeSize?.width}x${bannerView.creativeSize?.height}');
          setState(() {
            isLoaded = true;
            isLoading = false;
            errorCode = null;
          });
        },
        onAdClicked: (unitId, metadata) {
          debugPrint('광고 클릭: ${metadata?['destinationURL']}');
        },
        onAdImpression: (unitId, metadata) {
          debugPrint('광고 노출: ${metadata?['creativeId']}');
        },
        onAdFailedToReceive: (unitId, error) {
          debugPrint('광고 수신 실패: $error');
          setState(() {
            isLoaded = false;
            isLoading = false;
            errorCode = error;
          });
        },
      ),
    );

    WidgetsBinding.instance.addPostFrameCallback((_) {
      bannerView.adSize = Size(
        MediaQuery.of(context).size.width,
        bannerHeight,
      );
    });
  }

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

  @override
  void dispose() {
    bannerView.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('배너 광고 예제'),
      ),
      body: SafeArea(
        child: Column(
          children: [
            // 로드 버튼
            Padding(
              padding: const EdgeInsets.all(16),
              child: ElevatedButton(
                onPressed: isLoading ? null : loadAd,
                child: Text(isLoading ? '로딩 중...' : '배너 광고 로드'),
              ),
            ),

            // 상태 표시
            if (errorCode != null)
              Padding(
                padding: const EdgeInsets.all(16),
                child: Text(
                  '에러: ${errorCode?.code}',
                  style: const TextStyle(color: Colors.red),
                ),
              ),

            // 메인 콘텐츠
            const Expanded(
              child: Center(
                child: Text('메인 콘텐츠 영역'),
              ),
            ),

            // 배너 광고
            Container(
              width: MediaQuery.of(context).size.width,
              height: bannerHeight,
              color: Colors.grey[200],
              child: isLoaded
                  ? bannerView
                  : isLoading
                      ? const Center(child: CircularProgressIndicator())
                      : const Center(child: Text('광고 영역')),
            ),
          ],
        ),
      ),
    );
  }
}

백필 광고

백필 광고가 활성화된 경우, 직광고가 없을 때 자동으로 백필 광고가 로드됩니다. 메타데이터의 isBackfilled 값으로 백필 광고 여부를 확인할 수 있습니다.
bannerView = AdropBannerView(
  unitId: 'YOUR_UNIT_ID',
  listener: AdropBannerListener(
    onAdReceived: (unitId, metadata) {
      final isBackfilled = metadata?['isBackfilled'] ?? false;
      if (isBackfilled == true) {
        debugPrint('백필 광고가 로드되었습니다');
      } else {
        debugPrint('직광고가 로드되었습니다');
      }
    },
    onAdFailedToReceive: (unitId, errorCode) {
      switch (errorCode) {
        case AdropErrorCode.adNoFill:
          debugPrint('직광고 없음, 백필 광고 요청 중...');
          break;
        case AdropErrorCode.backfillNoFill:
          debugPrint('백필 광고도 없습니다');
          // 광고 영역 숨김 처리
          break;
        default:
          debugPrint('광고 로드 실패: ${errorCode.code}');
      }
    },
  ),
);
백필 광고를 사용하려면 네이티브 플랫폼에 백필 의존성을 추가해야 합니다. 시작하기를 참고하세요.

다음 단계