125 lines
3.2 KiB
Dart
125 lines
3.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
|
import 'package:lunchpick/core/utils/ad_helper.dart';
|
|
|
|
/// 실제 구글 전면 광고(Interstitial Ad) 서비스.
|
|
class AdService {
|
|
InterstitialAd? _interstitialAd;
|
|
Completer<bool>? _loadingCompleter;
|
|
|
|
/// 광고를 로드하고 재생한 뒤 완료 여부를 반환한다.
|
|
Future<bool> showInterstitialAd(BuildContext context) async {
|
|
if (!AdHelper.isMobilePlatform) return true;
|
|
|
|
final closeLoading = _showLoadingOverlay(context);
|
|
await _enterImmersiveMode();
|
|
final loaded = await _ensureAdLoaded();
|
|
closeLoading();
|
|
if (!loaded) {
|
|
await _restoreSystemUi();
|
|
return false;
|
|
}
|
|
|
|
final ad = _interstitialAd;
|
|
if (ad == null) {
|
|
await _restoreSystemUi();
|
|
return false;
|
|
}
|
|
|
|
_interstitialAd = null;
|
|
|
|
final completer = Completer<bool>();
|
|
ad.fullScreenContentCallback = FullScreenContentCallback(
|
|
onAdDismissedFullScreenContent: (ad) {
|
|
ad.dispose();
|
|
_preload();
|
|
unawaited(_restoreSystemUi());
|
|
completer.complete(true);
|
|
},
|
|
onAdFailedToShowFullScreenContent: (ad, error) {
|
|
ad.dispose();
|
|
_preload();
|
|
unawaited(_restoreSystemUi());
|
|
completer.complete(false);
|
|
},
|
|
);
|
|
|
|
// 상하단 여백 없이 전체 화면으로 표시하도록 immersive 모드 설정.
|
|
ad.setImmersiveMode(true);
|
|
try {
|
|
ad.show();
|
|
} catch (_) {
|
|
unawaited(_restoreSystemUi());
|
|
completer.complete(false);
|
|
}
|
|
return completer.future;
|
|
}
|
|
|
|
Future<void> _enterImmersiveMode() async {
|
|
try {
|
|
await SystemChrome.setEnabledSystemUIMode(
|
|
SystemUiMode.immersiveSticky,
|
|
overlays: [],
|
|
);
|
|
} catch (_) {}
|
|
}
|
|
|
|
Future<void> _restoreSystemUi() async {
|
|
try {
|
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
|
} catch (_) {}
|
|
}
|
|
|
|
VoidCallback _showLoadingOverlay(BuildContext context) {
|
|
final navigator = Navigator.of(context, rootNavigator: true);
|
|
showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
barrierColor: Colors.black.withOpacity(0.35),
|
|
builder: (_) => const Center(child: CircularProgressIndicator()),
|
|
);
|
|
return () {
|
|
if (navigator.mounted && navigator.canPop()) {
|
|
navigator.pop();
|
|
}
|
|
};
|
|
}
|
|
|
|
Future<bool> _ensureAdLoaded() async {
|
|
if (_interstitialAd != null) return true;
|
|
|
|
if (_loadingCompleter != null) {
|
|
return _loadingCompleter!.future;
|
|
}
|
|
|
|
final completer = Completer<bool>();
|
|
_loadingCompleter = completer;
|
|
|
|
InterstitialAd.load(
|
|
adUnitId: AdHelper.interstitialAdUnitId,
|
|
request: const AdRequest(),
|
|
adLoadCallback: InterstitialAdLoadCallback(
|
|
onAdLoaded: (ad) {
|
|
_interstitialAd = ad;
|
|
completer.complete(true);
|
|
_loadingCompleter = null;
|
|
},
|
|
onAdFailedToLoad: (error) {
|
|
completer.complete(false);
|
|
_loadingCompleter = null;
|
|
},
|
|
),
|
|
);
|
|
|
|
return completer.future;
|
|
}
|
|
|
|
void _preload() {
|
|
if (_interstitialAd != null || _loadingCompleter != null) return;
|
|
_ensureAdLoaded();
|
|
}
|
|
}
|