Skip to content

Commit 88f9257

Browse files
authored
Merge pull request #40 from flutter-news-app-full-source-code/Refactor-LocalAd-into-Specific-Ad-Subtypes
Refactor local ad into specific ad subtypes
2 parents e67adeb + 5c2ba56 commit 88f9257

24 files changed

+599
-267
lines changed

lib/src/fixtures/fixture_ids.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,11 @@ const kXhosaId = '6643c000a4a4e6e1a8e7b0b2';
522522
const kYiddishId = '6643c000a4a4e6e1a8e7b0b3';
523523
const kYorubaId = '6643c000a4a4e6e1a8e7b0b4';
524524
const kZhuangChuangId = '6643c000a4a4e6e1a8e7b0b5';
525+
526+
// --- LocalAds Fixture IDs ---
527+
const kLocalAd1Id = '1563c000a4a4e6e1a8e7f0f1';
528+
const kLocalAd2Id = '2563c000a4a4e6e1a8e7f0f2';
529+
const kLocalAd3Id = '3563c000a4a4e6e1a8e7f0f3';
530+
const kLocalAd4Id = '4563c000a4a4e6e1a8e7f0f4';
531+
const kLocalAd5Id = '5563c000a4a4e6e1a8e7f0f5';
532+
const kLocalAd6Id = '6563c000a4a4e6e1a8e7f0f6';

lib/src/fixtures/fixtures.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export 'dashboard_summary.dart';
33
export 'fixture_ids.dart';
44
export 'headlines.dart';
55
export 'languages.dart';
6+
export 'local_ads.dart';
67
export 'remote_configs.dart';
78
export 'sources.dart';
89
export 'topics.dart';

lib/src/fixtures/local_ads.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'package:core/core.dart';
2+
3+
/// A list of predefined local ads for fixture data.
4+
final localAdsFixturesData = <LocalAd>[
5+
const LocalNativeAd(
6+
id: kLocalAd1Id,
7+
title: 'Discover Our Premium Content',
8+
subtitle: 'Unlock exclusive articles and an ad-free experience.',
9+
imageUrl: 'https://example.com/ads/premium_native.png',
10+
targetUrl: 'https://example.com/premium',
11+
),
12+
const LocalBannerAd(
13+
id: kLocalAd2Id,
14+
imageUrl: 'https://example.com/ads/banner_ad_1.png',
15+
targetUrl: 'https://example.com/offer1',
16+
),
17+
const LocalInterstitialAd(
18+
id: kLocalAd3Id,
19+
imageUrl: 'https://example.com/ads/interstitial_ad_1.png',
20+
targetUrl: 'https://example.com/special_offer',
21+
),
22+
const LocalVideoAd(
23+
id: kLocalAd4Id,
24+
videoUrl: 'https://example.com/ads/promo_video.mp4',
25+
targetUrl: 'https://example.com/watch_more',
26+
),
27+
const LocalNativeAd(
28+
id: kLocalAd5Id,
29+
title: 'Stay Informed with Daily Briefings',
30+
subtitle: 'Get the top news delivered straight to your inbox.',
31+
imageUrl: 'https://example.com/ads/briefing_native.png',
32+
targetUrl: 'https://example.com/subscribe',
33+
),
34+
const LocalBannerAd(
35+
id: kLocalAd6Id,
36+
imageUrl: 'https://example.com/ads/banner_ad_2.png',
37+
targetUrl: 'https://example.com/offer2',
38+
),
39+
];
Lines changed: 35 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,47 @@
11
import 'package:core/core.dart';
2-
import 'package:json_annotation/json_annotation.dart';
32
import 'package:meta/meta.dart';
43

5-
part 'local_ad.g.dart';
6-
74
/// {@template local_ad}
8-
/// Defines a single custom ad that can be served locally.
9-
/// Includes title and subtitle to mimic native ads.
5+
/// An abstract base class for all local ad types.
6+
///
7+
/// This class acts as a router for deserializing specific local ad types
8+
/// (e.g., native, banner, interstitial, video) based on their `adType` field.
9+
/// Concrete implementations must provide their own `toJson` method.
1010
/// {@endtemplate}
1111
@immutable
12-
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
13-
class LocalAd extends FeedItem {
12+
abstract class LocalAd extends FeedItem {
1413
/// {@macro local_ad}
15-
const LocalAd({
16-
required this.id,
17-
required this.title,
18-
required this.subtitle,
19-
required this.imageUrl,
20-
required this.targetUrl,
21-
required this.adType,
22-
}) : super(type: 'localAd');
23-
24-
/// Creates a [LocalAd] from JSON data.
25-
factory LocalAd.fromJson(Map<String, dynamic> json) =>
26-
_$LocalAdFromJson(json);
27-
28-
/// Converts this [LocalAd] instance to JSON data.
29-
Map<String, dynamic> toJson() {
30-
final json = _$LocalAdToJson(this);
31-
json['type'] = type;
32-
return json;
14+
const LocalAd({required this.adType}) : super(type: 'localAd');
15+
16+
/// Factory method to create a [LocalAd] instance from a JSON map.
17+
///
18+
/// This factory uses the `adType` field in the JSON map to dispatch to the
19+
/// correct concrete `fromJson` constructor for each local ad type.
20+
///
21+
/// Throws [FormatException] if the `adType` field is missing or unknown.
22+
factory LocalAd.fromJson(Map<String, dynamic> json) {
23+
final adType = json['adType'] as String?;
24+
if (adType == null) {
25+
throw const FormatException('Missing "adType" field in LocalAd JSON.');
26+
}
27+
28+
switch (adType) {
29+
case 'native':
30+
return LocalNativeAd.fromJson(json);
31+
case 'banner':
32+
return LocalBannerAd.fromJson(json);
33+
case 'interstitial':
34+
return LocalInterstitialAd.fromJson(json);
35+
case 'video':
36+
return LocalVideoAd.fromJson(json);
37+
default:
38+
throw FormatException('Unknown LocalAd type: $adType');
39+
}
3340
}
3441

35-
/// Unique identifier for the local ad.
36-
final String id;
37-
38-
/// The title of the local ad.
39-
final String title;
40-
41-
/// The subtitle or description of the local ad.
42-
final String subtitle;
43-
44-
/// The URL of the image for the local ad.
45-
final String imageUrl;
46-
47-
/// The URL to navigate to when the local ad is clicked.
48-
final String targetUrl;
49-
50-
/// The type of the ad (e.g., banner, native, interstitial).
51-
final AdType adType;
42+
/// The type of the ad (e.g., banner, native, interstitial, video).
43+
final String adType;
5244

5345
@override
54-
List<Object?> get props => [
55-
id,
56-
title,
57-
subtitle,
58-
imageUrl,
59-
targetUrl,
60-
adType,
61-
type,
62-
];
63-
64-
/// Creates a copy of this [LocalAd] but with the given fields replaced with
65-
/// the new values.
66-
LocalAd copyWith({
67-
String? id,
68-
String? title,
69-
String? subtitle,
70-
String? imageUrl,
71-
String? targetUrl,
72-
AdType? adType,
73-
}) {
74-
return LocalAd(
75-
id: id ?? this.id,
76-
title: title ?? this.title,
77-
subtitle: subtitle ?? this.subtitle,
78-
imageUrl: imageUrl ?? this.imageUrl,
79-
targetUrl: targetUrl ?? this.targetUrl,
80-
adType: adType ?? this.adType,
81-
);
82-
}
46+
List<Object?> get props => [adType, type];
8347
}

lib/src/models/feed_decorators/local_ad.g.dart

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export 'local_banner_ad.dart';
2+
export 'local_interstitial_ad.dart';
3+
export 'local_native_ad.dart';
4+
export 'local_video_ad.dart';
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import 'package:core/core.dart';
2+
import 'package:json_annotation/json_annotation.dart';
3+
import 'package:meta/meta.dart';
4+
5+
part 'local_banner_ad.g.dart';
6+
7+
/// {@template local_banner_ad}
8+
/// Defines a custom banner ad that can be served locally.
9+
///
10+
/// Banner ads are typically rectangular images with a target URL.
11+
/// {@endtemplate}
12+
@immutable
13+
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
14+
class LocalBannerAd extends LocalAd {
15+
/// {@macro local_banner_ad}
16+
const LocalBannerAd({
17+
required this.id,
18+
required this.imageUrl,
19+
required this.targetUrl,
20+
}) : super(adType: 'banner');
21+
22+
/// Creates a [LocalBannerAd] from JSON data.
23+
factory LocalBannerAd.fromJson(Map<String, dynamic> json) =>
24+
_$LocalBannerAdFromJson(json);
25+
26+
/// Unique identifier for the local banner ad.
27+
final String id;
28+
29+
/// The URL of the image for the local banner ad.
30+
final String imageUrl;
31+
32+
/// The URL to navigate to when the local banner ad is clicked.
33+
final String targetUrl;
34+
35+
Map<String, dynamic> toJson() {
36+
final json = _$LocalBannerAdToJson(this);
37+
json['adType'] = adType;
38+
json['type'] = type;
39+
return json;
40+
}
41+
42+
@override
43+
List<Object?> get props => [id, imageUrl, targetUrl, adType, type];
44+
45+
/// Creates a copy of this [LocalBannerAd] but with the given fields replaced with
46+
/// the new values.
47+
LocalBannerAd copyWith({String? id, String? imageUrl, String? targetUrl}) {
48+
return LocalBannerAd(
49+
id: id ?? this.id,
50+
imageUrl: imageUrl ?? this.imageUrl,
51+
targetUrl: targetUrl ?? this.targetUrl,
52+
);
53+
}
54+
}

lib/src/models/local_ads/local_banner_ad.g.dart

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import 'package:core/core.dart';
2+
import 'package:json_annotation/json_annotation.dart';
3+
import 'package:meta/meta.dart';
4+
5+
part 'local_interstitial_ad.g.dart';
6+
7+
/// {@template local_interstitial_ad}
8+
/// Defines a custom interstitial ad that can be served locally.
9+
///
10+
/// Interstitial ads are full-screen advertisements, typically with an image
11+
/// and a target URL, that appear between content transitions.
12+
/// {@endtemplate}
13+
@immutable
14+
@JsonSerializable(explicitToJson: true, includeIfNull: true, checked: true)
15+
class LocalInterstitialAd extends LocalAd {
16+
/// {@macro local_interstitial_ad}
17+
const LocalInterstitialAd({
18+
required this.id,
19+
required this.imageUrl,
20+
required this.targetUrl,
21+
}) : super(adType: 'interstitial');
22+
23+
/// Creates a [LocalInterstitialAd] from JSON data.
24+
factory LocalInterstitialAd.fromJson(Map<String, dynamic> json) =>
25+
_$LocalInterstitialAdFromJson(json);
26+
27+
/// Unique identifier for the local interstitial ad.
28+
final String id;
29+
30+
/// The URL of the image for the local interstitial ad.
31+
final String imageUrl;
32+
33+
/// The URL to navigate to when the local interstitial ad is clicked.
34+
final String targetUrl;
35+
36+
Map<String, dynamic> toJson() {
37+
final json = _$LocalInterstitialAdToJson(this);
38+
json['adType'] = adType;
39+
json['type'] = type;
40+
return json;
41+
}
42+
43+
@override
44+
List<Object?> get props => [id, imageUrl, targetUrl, adType, type];
45+
46+
/// Creates a copy of this [LocalInterstitialAd] but with the given fields
47+
/// replaced with the new values.
48+
LocalInterstitialAd copyWith({
49+
String? id,
50+
String? imageUrl,
51+
String? targetUrl,
52+
}) {
53+
return LocalInterstitialAd(
54+
id: id ?? this.id,
55+
imageUrl: imageUrl ?? this.imageUrl,
56+
targetUrl: targetUrl ?? this.targetUrl,
57+
);
58+
}
59+
}

lib/src/models/local_ads/local_interstitial_ad.g.dart

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)