Skip to content

Commit 658b26b

Browse files
authored
Merge pull request #84 from flutter-news-app-full-source-code/Fix-the-Ad-Initialization-Crash
Fix the ad initialization crash
2 parents 0867a37 + 6b8ced7 commit 658b26b

File tree

5 files changed

+131
-3
lines changed

5 files changed

+131
-3
lines changed

lib/ads/models/native_ad.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import 'package:flutter/foundation.dart';
1010
enum AdProviderType {
1111
/// Google AdMob provider.
1212
admob,
13-
// Add other providers here in the future, e.g., meta, appLovin
13+
14+
/// A placeholder provider for platforms where native ads are not supported.
15+
///
16+
/// This is primarily used for the web demo environment to maintain UI
17+
/// consistency without relying on native SDKs.
18+
placeholder,
1419
}
1520

1621
/// {@template native_ad}

lib/ads/no_op_ad_provider.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/ad_provider.dart';
3+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/ad_theme_style.dart';
4+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/native_ad.dart';
5+
import 'package:logging/logging.dart';
6+
import 'package:uuid/uuid.dart';
7+
8+
/// {@template no_op_ad_provider}
9+
/// A "no-operation" implementation of [AdProvider] for platforms where
10+
/// native ad SDKs are not supported (e.g., web).
11+
///
12+
/// This provider's `initialize` method does nothing, and its `loadNativeAd`
13+
/// method returns a [NativeAd] with [AdProviderType.placeholder]. This ensures
14+
/// that the application's UI can still render an "ad slot" for visual
15+
/// consistency in demo environments, without attempting to load actual native
16+
/// ads that would cause platform exceptions.
17+
/// {@endtemplate}
18+
class NoOpAdProvider implements AdProvider {
19+
/// {@macro no_op_ad_provider}
20+
NoOpAdProvider({Logger? logger})
21+
: _logger = logger ?? Logger('NoOpAdProvider');
22+
23+
final Logger _logger;
24+
final Uuid _uuid = const Uuid();
25+
26+
/// This method does nothing as there's no SDK to initialize.
27+
///
28+
/// It logs a message to indicate its no-op nature.
29+
@override
30+
Future<void> initialize() async {
31+
_logger.info('No-Op Ad Provider initialized (no actual SDK to init).');
32+
}
33+
34+
/// Loads a placeholder native ad.
35+
///
36+
/// This method does not interact with any external ad network. Instead, it
37+
/// creates a [NativeAd] object with [AdProviderType.placeholder], which
38+
/// signals to the UI that a generic placeholder should be rendered.
39+
///
40+
/// The [imageStyle] and [adThemeStyle] parameters are accepted for API
41+
/// compatibility but are not used to load a real ad.
42+
@override
43+
Future<NativeAd?> loadNativeAd({
44+
required HeadlineImageStyle imageStyle,
45+
required AdThemeStyle adThemeStyle,
46+
}) async {
47+
_logger.info('Loading placeholder native ad.');
48+
// Return a dummy NativeAd object with a placeholder type.
49+
// The `adObject` can be any non-null object, as it won't be used by
50+
// the placeholder rendering widget.
51+
return NativeAd(
52+
id: _uuid.v4(),
53+
provider: AdProviderType.placeholder,
54+
adObject: Object(), // Dummy object
55+
);
56+
}
57+
}

lib/ads/widgets/ad_feed_item_widget.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/models.dart';
3+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/widgets/placeholder_ad_widget.dart';
34
import 'package:flutter_news_app_mobile_client_full_source_code/ads/widgets/widgets.dart';
45
import 'package:ui_kit/ui_kit.dart';
56

@@ -57,6 +58,10 @@ class _AdDispatcher extends StatelessWidget {
5758
case AdProviderType.admob:
5859
// If the provider is AdMob, render the AdmobNativeAdWidget.
5960
return AdmobNativeAdWidget(nativeAd: nativeAd);
61+
case AdProviderType.placeholder:
62+
// If the provider is a placeholder, render the PlaceholderAdWidget.
63+
// This is used for web or other unsupported platforms to maintain UI.
64+
return const PlaceholderAdWidget();
6065
}
6166
}
6267
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:ui_kit/ui_kit.dart';
3+
4+
/// {@template placeholder_ad_widget}
5+
/// A widget that displays a generic placeholder for an advertisement.
6+
///
7+
/// This is used on platforms (like web) where actual native ad SDKs are not
8+
/// supported, but a visual representation of an ad slot is desired for UI
9+
/// consistency or demo purposes.
10+
/// {@endtemplate}
11+
class PlaceholderAdWidget extends StatelessWidget {
12+
/// {@macro placeholder_ad_widget}
13+
const PlaceholderAdWidget({super.key});
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
final theme = Theme.of(context);
18+
return Container(
19+
padding: const EdgeInsets.all(AppSpacing.lg),
20+
decoration: BoxDecoration(
21+
color: theme.colorScheme.surfaceVariant,
22+
borderRadius: BorderRadius.circular(AppSpacing.md),
23+
border: Border.all(color: theme.colorScheme.outlineVariant),
24+
),
25+
child: Column(
26+
mainAxisAlignment: MainAxisAlignment.center,
27+
children: [
28+
Icon(
29+
Icons.ad_units,
30+
size: 48,
31+
color: theme.colorScheme.onSurfaceVariant,
32+
),
33+
const SizedBox(height: AppSpacing.md),
34+
Text(
35+
'Ad Placeholder',
36+
style: theme.textTheme.titleMedium?.copyWith(
37+
color: theme.colorScheme.onSurfaceVariant,
38+
),
39+
textAlign: TextAlign.center,
40+
),
41+
const SizedBox(height: AppSpacing.xs),
42+
Text(
43+
'Native ads are not supported on this platform.',
44+
style: theme.textTheme.bodySmall?.copyWith(
45+
color: theme.colorScheme.onSurfaceVariant.withOpacity(0.7),
46+
),
47+
textAlign: TextAlign.center,
48+
),
49+
],
50+
),
51+
);
52+
}
53+
}

lib/bootstrap.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import 'package:flutter/material.dart';
1111
import 'package:flutter_bloc/flutter_bloc.dart';
1212
import 'package:flutter_news_app_mobile_client_full_source_code/ads/ad_provider.dart';
1313
import 'package:flutter_news_app_mobile_client_full_source_code/ads/ad_service.dart';
14+
import 'package:flutter/foundation.dart';
1415
import 'package:flutter_news_app_mobile_client_full_source_code/ads/admob_ad_provider.dart';
16+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/no_op_ad_provider.dart';
1517
import 'package:flutter_news_app_mobile_client_full_source_code/app/app.dart';
1618
import 'package:flutter_news_app_mobile_client_full_source_code/app/config/config.dart'
1719
as app_config;
@@ -42,9 +44,15 @@ Future<Widget> bootstrap(
4244
HttpClient? httpClient;
4345

4446
// Initialize AdProvider and AdService
45-
final AdProvider adProvider = AdMobAdProvider(logger: logger);
47+
// Initialize AdProvider based on platform.
48+
// On web, use a No-Op provider to prevent MissingPluginException,
49+
// as Google Mobile Ads SDK does not support native ads on web.
50+
final AdProvider adProvider = kIsWeb
51+
? NoOpAdProvider(logger: logger)
52+
: AdMobAdProvider(logger: logger);
53+
4654
final adService = AdService(adProvider: adProvider, logger: logger);
47-
await adService.initialize(); // Initialize AdMob SDK early
55+
await adService.initialize(); // Initialize the selected AdProvider early
4856

4957
if (appConfig.environment == app_config.AppEnvironment.demo) {
5058
authClient = AuthInmemory();

0 commit comments

Comments
 (0)