- 기록/통계 탭에 디버그 토글 배너 추가 및 테스트 데이터 주입 로직 상태화\n- 리스트 공유 화면에 디버그 프리뷰 토글, 광고 관문, 디버그 전송 흐름 반영\n- 모의 전면 광고는 대기 시간 종료 시 자동 완료되도록 변경\n- AGENTS.md에 코멘트는 한국어로 작성 규칙 명시\n\n테스트: flutter analyze; flutter test
134 lines
4.1 KiB
Dart
134 lines
4.1 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// 간단한 전면 광고(Interstitial Ad) 모의 서비스
|
|
class AdService {
|
|
/// 임시 광고 다이얼로그를 표시하고 사용자가 끝까지 시청했는지 여부를 반환한다.
|
|
Future<bool> showInterstitialAd(BuildContext context) async {
|
|
final result = await showDialog<bool>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (_) => const _MockInterstitialAdDialog(),
|
|
);
|
|
return result ?? false;
|
|
}
|
|
}
|
|
|
|
class _MockInterstitialAdDialog extends StatefulWidget {
|
|
const _MockInterstitialAdDialog();
|
|
|
|
@override
|
|
State<_MockInterstitialAdDialog> createState() =>
|
|
_MockInterstitialAdDialogState();
|
|
}
|
|
|
|
class _MockInterstitialAdDialogState extends State<_MockInterstitialAdDialog> {
|
|
static const int _adDurationSeconds = 4;
|
|
|
|
late Timer _timer;
|
|
int _elapsedSeconds = 0;
|
|
bool _completed = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_elapsedSeconds++;
|
|
});
|
|
if (_elapsedSeconds >= _adDurationSeconds && !_completed) {
|
|
_completed = true;
|
|
_timer.cancel();
|
|
Navigator.of(context).pop(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
bool get _canClose => _elapsedSeconds >= _adDurationSeconds;
|
|
|
|
double get _progress => (_elapsedSeconds / _adDurationSeconds).clamp(0, 1);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
return Dialog(
|
|
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 80),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
child: Stack(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(
|
|
Icons.ondemand_video,
|
|
size: 56,
|
|
color: Colors.deepPurple,
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
'광고 시청 중...',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDark ? Colors.white : Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_canClose ? '광고가 완료되었습니다.' : '잠시만 기다려 주세요.',
|
|
style: TextStyle(
|
|
color: isDark ? Colors.white70 : Colors.black54,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
LinearProgressIndicator(
|
|
value: _progress,
|
|
minHeight: 6,
|
|
borderRadius: BorderRadius.circular(999),
|
|
backgroundColor: Colors.grey.withValues(alpha: 0.2),
|
|
color: Colors.deepPurple,
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
_canClose
|
|
? '광고가 완료되었어요. 자동으로 계속합니다.'
|
|
: '남은 시간: ${_adDurationSeconds - _elapsedSeconds}초',
|
|
style: TextStyle(
|
|
color: isDark ? Colors.white70 : Colors.black54,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(false);
|
|
},
|
|
child: const Text('닫기'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Positioned(
|
|
right: 8,
|
|
top: 8,
|
|
child: IconButton(
|
|
onPressed: () => Navigator.of(context).pop(false),
|
|
icon: const Icon(Icons.close),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|