add macOS support for ad loading and improve timestamp handling
Some checks failed
Build Android App / build (push) Failing after 49s
Some checks failed
Build Android App / build (push) Failing after 49s
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:quotegen_client/pages/home/page.dart';
|
||||
@@ -7,8 +8,8 @@ import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize mobile ads (AdMob) only on mobile platforms
|
||||
if (!kIsWeb) {
|
||||
// Initialize mobile ads (AdMob) only on mobile platforms (not web or macOS)
|
||||
if (!kIsWeb && !Platform.isMacOS) {
|
||||
await MobileAds.instance.initialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
@@ -17,6 +18,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:file_saver/file_saver.dart';
|
||||
|
||||
// number formatter for engagement fields
|
||||
class NumberTextInputFormatter extends TextInputFormatter {
|
||||
@@ -68,7 +70,8 @@ class _HomePageState extends State<HomePage> {
|
||||
"display_name": "",
|
||||
"handle": "",
|
||||
"content": "",
|
||||
"timestamp": 0,
|
||||
"date": DateTime.now(),
|
||||
"time": TimeOfDay.now(),
|
||||
"avatar_image": null,
|
||||
"post_image": null,
|
||||
"replies": "",
|
||||
@@ -134,6 +137,9 @@ class _HomePageState extends State<HomePage> {
|
||||
double topPadding = MediaQuery.of(context).padding.top;
|
||||
double bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
|
||||
// Check if we're on mobile (iOS/Android)
|
||||
bool isMobilePlatform = !kIsWeb && (Platform.isIOS || Platform.isAndroid);
|
||||
|
||||
if (isWideLayout) {
|
||||
return Scaffold(
|
||||
child: Center(
|
||||
@@ -142,15 +148,11 @@ class _HomePageState extends State<HomePage> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
Gap(max(topPadding, 14)),
|
||||
|
||||
SizedBox(
|
||||
height: 600,
|
||||
child: _buildPreview(context)
|
||||
),
|
||||
|
||||
|
||||
Gap(14),
|
||||
|
||||
ConstrainedBox(
|
||||
@@ -166,8 +168,6 @@ class _HomePageState extends State<HomePage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Gap(max(bottomPadding, 14)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -175,28 +175,55 @@ class _HomePageState extends State<HomePage> {
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// Mobile layout content
|
||||
Widget mainContent = SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
|
||||
Gap(max(topPadding, 14)),
|
||||
Gap(max(topPadding, 14)),
|
||||
|
||||
_buildPreview(context),
|
||||
_buildPreview(context),
|
||||
|
||||
Gap(14),
|
||||
Gap(14),
|
||||
|
||||
_buildForm(context),
|
||||
_buildForm(context),
|
||||
|
||||
Gap(14),
|
||||
Gap(14),
|
||||
|
||||
// Banner ad
|
||||
// Add bottom padding on mobile to avoid content being hidden behind ad
|
||||
if (isMobilePlatform)
|
||||
Gap(100), // Space for the overlay ad (80px min height + padding)
|
||||
|
||||
// Only show inline ad on non-mobile platforms
|
||||
if (!isMobilePlatform)
|
||||
AdBanner(),
|
||||
|
||||
Gap(max(bottomPadding, 14)),
|
||||
Gap(max(bottomPadding, 14)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Wrap in Stack with overlay ad for mobile platforms
|
||||
if (isMobilePlatform) {
|
||||
return Scaffold(
|
||||
child: Stack(
|
||||
children: [
|
||||
mainContent,
|
||||
|
||||
// Bottom overlay ad for mobile
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: max(bottomPadding, 0),
|
||||
child: AdBanner(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
child: mainContent,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,15 +245,110 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: IconButton.secondary(
|
||||
alignment: Alignment.topRight,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if ([TargetPlatform.iOS, TargetPlatform.android, TargetPlatform.macOS].contains(defaultTargetPlatform))...[
|
||||
IconButton.secondary(
|
||||
icon: Icon(
|
||||
Icons.share,
|
||||
),
|
||||
onPressed: () async {
|
||||
await shareImage();
|
||||
}
|
||||
),
|
||||
|
||||
Gap(8),
|
||||
],
|
||||
|
||||
IconButton.secondary(
|
||||
icon: Icon(
|
||||
LucideIcons.download,
|
||||
),
|
||||
onPressed: () async {
|
||||
if (postPreviewImage == null) {
|
||||
print("No image avalable to download");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// genrate filename with timestmp
|
||||
final fileName = "quote_${DateTime.now().millisecondsSinceEpoch}";
|
||||
|
||||
await FileSaver.instance.saveFile(
|
||||
name: fileName,
|
||||
bytes: postPreviewImage!,
|
||||
ext: "png",
|
||||
mimeType: MimeType.png,
|
||||
);
|
||||
|
||||
print("Image downloaded succesfully");
|
||||
} catch (e) {
|
||||
print("Error downloadin image: $e");
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
Gap(8),
|
||||
|
||||
IconButton.outline(
|
||||
icon: Icon(
|
||||
Icons.share,
|
||||
LucideIcons.link
|
||||
),
|
||||
onPressed: () async {
|
||||
await shareImage();
|
||||
}
|
||||
)
|
||||
if (_currentSessionId == null) {
|
||||
print("No session available");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// create snapshot link
|
||||
QuoteSnapshot snapshot = await QuoteGeneratorApiV2.createSnapshotLink(_currentSessionId!);
|
||||
|
||||
// copy URL to clipboard
|
||||
await Clipboard.setData(ClipboardData(text: snapshot.url));
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// show toast notification
|
||||
showToast(
|
||||
context: context,
|
||||
builder: (context, overlay) {
|
||||
return SurfaceCard(
|
||||
child: Basic(
|
||||
title: const Text('Snapshot Link Copied'),
|
||||
subtitle: const Text('The snapshot link has been copied to clipboard'),
|
||||
trailingAlignment: Alignment.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
print("Snapshot link created: ${snapshot.url}");
|
||||
} catch (e) {
|
||||
print("Error creating snapshot link: $e");
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
showToast(
|
||||
context: context,
|
||||
builder: (context, overlay) {
|
||||
return SurfaceCard(
|
||||
child: Basic(
|
||||
title: const Text('Error'),
|
||||
subtitle: const Text('Failed to create snapshot link'),
|
||||
trailingAlignment: Alignment.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
].reversed.toList(),
|
||||
)
|
||||
).withPadding(
|
||||
all: 12
|
||||
)
|
||||
@@ -258,6 +380,13 @@ class _HomePageState extends State<HomePage> {
|
||||
placeholder: Text(
|
||||
"Display Name"
|
||||
),
|
||||
features: [
|
||||
InputFeature.leading(
|
||||
Icon(
|
||||
LucideIcons.user
|
||||
),
|
||||
)
|
||||
],
|
||||
initialValue: formData["display_name"],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@@ -295,6 +424,13 @@ class _HomePageState extends State<HomePage> {
|
||||
placeholder: Text(
|
||||
"Handle"
|
||||
),
|
||||
features: [
|
||||
InputFeature.leading(
|
||||
Icon(
|
||||
LucideIcons.atSign
|
||||
)
|
||||
)
|
||||
],
|
||||
initialValue: formData["handle"],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@@ -413,44 +549,46 @@ class _HomePageState extends State<HomePage> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: DatePicker(
|
||||
value: formData["timestamp"] > 0
|
||||
? DateTime.fromMillisecondsSinceEpoch(formData["timestamp"])
|
||||
: DateTime.now(),
|
||||
value: formData["date"],
|
||||
onChanged: (DateTime? date) {
|
||||
if (date != null) {
|
||||
setState(() {
|
||||
formData["timestamp"] = date.millisecondsSinceEpoch;
|
||||
formData["date"] = date;
|
||||
});
|
||||
_saveFormData();
|
||||
onSubmit(changedField: "timestamp");
|
||||
onSubmit(changedField: "datetime");
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
TimePicker(
|
||||
value: formData["timestamp"] > 0
|
||||
? TimeOfDay.fromDateTime(DateTime.fromMillisecondsSinceEpoch(formData["timestamp"]))
|
||||
: TimeOfDay.now(),
|
||||
value: formData["time"],
|
||||
onChanged: (TimeOfDay? time) {
|
||||
if (time != null) {
|
||||
DateTime currentDate = formData["timestamp"] > 0
|
||||
? DateTime.fromMillisecondsSinceEpoch(formData["timestamp"])
|
||||
: DateTime.now();
|
||||
DateTime newDateTime = DateTime(
|
||||
currentDate.year,
|
||||
currentDate.month,
|
||||
currentDate.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
setState(() {
|
||||
formData["timestamp"] = newDateTime.millisecondsSinceEpoch;
|
||||
formData["time"] = time;
|
||||
});
|
||||
_saveFormData();
|
||||
onSubmit(changedField: "timestamp");
|
||||
onSubmit(changedField: "datetime");
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
Button.secondary(
|
||||
child: Text(
|
||||
"Now"
|
||||
),
|
||||
onPressed: () {
|
||||
DateTime now = DateTime.now();
|
||||
TimeOfDay nowTime = TimeOfDay.now();
|
||||
setState(() {
|
||||
formData["date"] = now;
|
||||
formData["time"] = nowTime;
|
||||
});
|
||||
_saveFormData();
|
||||
onSubmit(changedField: "datetime");
|
||||
},
|
||||
)
|
||||
|
||||
]
|
||||
@@ -786,6 +924,22 @@ class _HomePageState extends State<HomePage> {
|
||||
int _submitCount = 0;
|
||||
String? _currentSessionId;
|
||||
|
||||
// combine date and time into unix timestamp (in seconds for API)
|
||||
int _combineDateTime() {
|
||||
DateTime date = formData["date"];
|
||||
TimeOfDay time = formData["time"];
|
||||
|
||||
DateTime combined = DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
|
||||
return combined.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
void onSubmit({String? changedField}) async {
|
||||
|
||||
int currentSubmit = ++_submitCount;
|
||||
@@ -802,10 +956,6 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
print("Form Data Submitted: $formData");
|
||||
|
||||
if (!formData["handle"].startsWith("@")) {
|
||||
formData["handle"] = "@" + formData["handle"];
|
||||
}
|
||||
|
||||
// Wait for session to be ready
|
||||
while (_currentSessionId == null) {
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
@@ -831,7 +981,7 @@ class _HomePageState extends State<HomePage> {
|
||||
displayName: formData["display_name"],
|
||||
username: formData["handle"].toLowerCase(),
|
||||
text: formData["content"],
|
||||
timestamp: formData["timestamp"],
|
||||
timestamp: _combineDateTime(),
|
||||
avatarUrl: formData["avatar_image"],
|
||||
imageUrl: formData["post_image"],
|
||||
verified: formData["verified"] ?? false,
|
||||
@@ -846,7 +996,7 @@ class _HomePageState extends State<HomePage> {
|
||||
displayName: changedField == "display_name" ? formData["display_name"] : null,
|
||||
username: changedField == "handle" ? formData["handle"].toLowerCase() : null,
|
||||
text: changedField == "content" ? formData["content"] : null,
|
||||
timestamp: changedField == "timestamp" ? formData["timestamp"] : null,
|
||||
timestamp: changedField == "datetime" ? _combineDateTime() : null,
|
||||
avatarUrl: changedField == "avatar_image" ? formData["avatar_image"] : null,
|
||||
imageUrl: changedField == "post_image" ? formData["post_image"] : null,
|
||||
verified: changedField == "verified" ? (formData["verified"] ?? false) : null,
|
||||
@@ -1002,13 +1152,19 @@ class _HomePageState extends State<HomePage> {
|
||||
postImage = base64Decode(postBase64);
|
||||
}
|
||||
|
||||
// split timestamp into seperate date and time
|
||||
DateTime dateTime = timestamp > 0
|
||||
? DateTime.fromMillisecondsSinceEpoch(timestamp)
|
||||
: DateTime.now();
|
||||
|
||||
print("Loaded form data: name=$displayName, handle=$handle, content=$content");
|
||||
|
||||
setState(() {
|
||||
formData["display_name"] = displayName;
|
||||
formData["handle"] = handle;
|
||||
formData["content"] = content;
|
||||
formData["timestamp"] = timestamp;
|
||||
formData["date"] = DateTime(dateTime.year, dateTime.month, dateTime.day);
|
||||
formData["time"] = TimeOfDay(hour: dateTime.hour, minute: dateTime.minute);
|
||||
formData["verified"] = verified;
|
||||
formData["replies"] = replies;
|
||||
formData["retweets"] = retweets;
|
||||
@@ -1035,17 +1191,13 @@ class _HomePageState extends State<HomePage> {
|
||||
);
|
||||
}
|
||||
|
||||
// fix handle format
|
||||
if (handle.isNotEmpty && !handle.startsWith("@")) {
|
||||
handle = "@$handle";
|
||||
}
|
||||
|
||||
// create session with full form data
|
||||
// create session with full form data (API expects timestamp in seconds)
|
||||
// Use _combineDateTime() to get timestamp from formData (defaults to now if nothing saved)
|
||||
QuoteSessionRequest initialRequest = QuoteSessionRequest(
|
||||
displayName: displayName,
|
||||
username: handle.toLowerCase(),
|
||||
text: content,
|
||||
timestamp: timestamp,
|
||||
timestamp: _combineDateTime(),
|
||||
verified: verified,
|
||||
avatarUrl: avatarImage,
|
||||
imageUrl: postImage,
|
||||
@@ -1054,8 +1206,8 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
await _initializeSession(initialRequest);
|
||||
|
||||
// genrate preview image if we have data
|
||||
if (_currentSessionId != null && (displayName.isNotEmpty || content.isNotEmpty)) {
|
||||
// genrate preview image if we have session
|
||||
if (_currentSessionId != null) {
|
||||
Uint8List imageBytes = await QuoteGeneratorApiV2.generateImage(_currentSessionId!);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
@@ -1080,11 +1232,14 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
print("Saving form data: name=${formData["display_name"]}, handle=${formData["handle"]}, content=${formData["content"]}");
|
||||
|
||||
// combine date and time into timestamp (in milliseconds for storage)
|
||||
int timestampMillis = _combineDateTime() * 1000;
|
||||
|
||||
// save text felds
|
||||
await prefs.setString("display_name", formData["display_name"]);
|
||||
await prefs.setString("handle", formData["handle"]);
|
||||
await prefs.setString("content", formData["content"]);
|
||||
await prefs.setInt("timestamp", formData["timestamp"]);
|
||||
await prefs.setInt("timestamp", timestampMillis);
|
||||
await prefs.setString("replies", formData["replies"]);
|
||||
await prefs.setString("retweets", formData["retweets"]);
|
||||
await prefs.setString("likes", formData["likes"]);
|
||||
|
||||
@@ -13,13 +13,13 @@ class AdSenseService implements AdService {
|
||||
Future<void> initialize() async {
|
||||
// AdSense is loaded via script tag in index.html
|
||||
print("AdSense initialized (web)");
|
||||
_isLoaded = true;
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadBannerAd() async {
|
||||
// AdSense ads are loaded automatically when widget renders
|
||||
_isLoaded = true;
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -159,9 +159,36 @@ class QuoteSession {
|
||||
}
|
||||
|
||||
|
||||
class QuoteSnapshot {
|
||||
final String token;
|
||||
final String url;
|
||||
final String sessionId;
|
||||
final int createdAt;
|
||||
final int expiresAt;
|
||||
|
||||
QuoteSnapshot({
|
||||
required this.token,
|
||||
required this.url,
|
||||
required this.sessionId,
|
||||
required this.createdAt,
|
||||
required this.expiresAt,
|
||||
});
|
||||
|
||||
factory QuoteSnapshot.fromJson(Map<String, dynamic> json) {
|
||||
return QuoteSnapshot(
|
||||
token: json["token"],
|
||||
url: json["url"],
|
||||
sessionId: json["sessionId"],
|
||||
createdAt: json["createdAt"],
|
||||
expiresAt: json["expiresAt"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QuoteGeneratorApiV2 {
|
||||
static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
// static const String _baseUrl = "http://localhost:3000";
|
||||
// static const String _baseUrl = "https://quotes.imbenji.net";
|
||||
static const String _baseUrl = "http://localhost:3000";
|
||||
|
||||
|
||||
// create new session
|
||||
@@ -259,6 +286,40 @@ class QuoteGeneratorApiV2 {
|
||||
}
|
||||
|
||||
|
||||
// create persistant snapshot link
|
||||
static Future<QuoteSnapshot> createSnapshotLink(String sessionId) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/quote/$sessionId/snapshot-link");
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
final data = jsonDecode(response.body);
|
||||
return QuoteSnapshot.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to create snapshot link: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
// get snapshot image by token
|
||||
static Future<Uint8List> getSnapshot(String token) async {
|
||||
final url = Uri.parse("$_baseUrl/v2/snapshot/$token");
|
||||
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {"User-Agent": "QuoteGen-Flutter/1.0"},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.bodyBytes;
|
||||
} else {
|
||||
throw Exception("Failed to get snapshot: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// helper to get current timestamp in seconds
|
||||
static int getCurrentTimestamp() {
|
||||
return DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "dart:io" show Platform;
|
||||
import "package:flutter/foundation.dart" show kIsWeb;
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:shadcn_flutter/shadcn_flutter.dart";
|
||||
@@ -15,11 +16,19 @@ class AdBanner extends StatefulWidget {
|
||||
class _AdBannerState extends State<AdBanner> {
|
||||
late AdService _adService;
|
||||
bool _isLoading = true;
|
||||
bool _isMacOS = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeAds();
|
||||
|
||||
// check if running on macOS
|
||||
if (!kIsWeb && Platform.isMacOS) {
|
||||
_isMacOS = true;
|
||||
_isLoading = false;
|
||||
} else {
|
||||
_initializeAds();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeAds() async {
|
||||
@@ -46,12 +55,42 @@ class _AdBannerState extends State<AdBanner> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_adService.dispose();
|
||||
if (!_isMacOS) {
|
||||
_adService.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
// Check if we're on mobile (iOS/Android)
|
||||
bool isMobile = !kIsWeb && !_isMacOS && (Platform.isIOS || Platform.isAndroid);
|
||||
|
||||
// Show placeholder only on macOS and web when ads fail to load
|
||||
// On mobile, we want real ads or nothing
|
||||
if (_isMacOS || (!isMobile && !_isLoading && !_adService.isLoaded)) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 80,
|
||||
),
|
||||
child: OutlinedContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"ADVERTISEMENT SPACE",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.mutedForeground,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
).withPadding(
|
||||
horizontal: 8
|
||||
);
|
||||
}
|
||||
|
||||
if (_isLoading) {
|
||||
return SizedBox(
|
||||
height: 50,
|
||||
@@ -65,11 +104,6 @@ class _AdBannerState extends State<AdBanner> {
|
||||
);
|
||||
}
|
||||
|
||||
if (!_adService.isLoaded) {
|
||||
// Ad failed to load, show nothing
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return _adService.getBannerWidget();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user