Skip to content

Commit 078ae68

Browse files
authored
Merge pull request #84 from flutter-news-app-full-source-code/app-config-Premium-User-Ad-Deactivation-and-local-ads-Tab-Order-Synchronization
App config premium user ad deactivation and local ads tab order synchronization
2 parents fb622a6 + 809a2ab commit 078ae68

File tree

5 files changed

+112
-17
lines changed

5 files changed

+112
-17
lines changed

lib/app_configuration/widgets/app_config_form_fields.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class AppConfigIntField extends StatelessWidget {
1313
required this.value,
1414
required this.onChanged,
1515
this.controller,
16+
this.enabled = true,
1617
super.key,
1718
});
1819

@@ -31,6 +32,9 @@ class AppConfigIntField extends StatelessWidget {
3132
/// Optional text editing controller for more control.
3233
final TextEditingController? controller;
3334

35+
/// Whether the input field is enabled. Defaults to true.
36+
final bool enabled;
37+
3438
@override
3539
Widget build(BuildContext context) {
3640
final theme = Theme.of(context);
@@ -50,6 +54,7 @@ class AppConfigIntField extends StatelessWidget {
5054
),
5155
const SizedBox(height: AppSpacing.xs),
5256
TextFormField(
57+
enabled: enabled,
5358
controller: controller,
5459
initialValue: controller == null ? value.toString() : null,
5560
keyboardType: TextInputType.number,

lib/app_configuration/widgets/feed_ad_settings_form.dart

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,37 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
4343
vsync: this,
4444
);
4545
_initializeControllers();
46+
_tabController.addListener(_onTabChanged);
47+
}
48+
49+
void _onTabChanged() {
50+
if (_tabController.indexIsChanging) return;
51+
52+
final selectedRole = AppUserRole.values[_tabController.index];
53+
if (selectedRole == AppUserRole.premiumUser) {
54+
final adConfig = widget.remoteConfig.adConfig;
55+
final feedAdConfig = adConfig.feedAdConfiguration;
56+
57+
// If the values for premium are not 0, update the config.
58+
// This enforces the business rule that premium users do not see ads.
59+
if (feedAdConfig.frequencyConfig.premiumAdFrequency != 0 ||
60+
feedAdConfig.frequencyConfig.premiumAdPlacementInterval != 0) {
61+
final updatedFrequencyConfig = feedAdConfig.frequencyConfig.copyWith(
62+
premiumAdFrequency: 0,
63+
premiumAdPlacementInterval: 0,
64+
);
65+
final updatedFeedAdConfig = feedAdConfig.copyWith(
66+
frequencyConfig: updatedFrequencyConfig,
67+
);
68+
widget.onConfigChanged(
69+
widget.remoteConfig.copyWith(
70+
adConfig: adConfig.copyWith(
71+
feedAdConfiguration: updatedFeedAdConfig,
72+
),
73+
),
74+
);
75+
}
76+
}
4677
}
4778

4879
@override
@@ -109,7 +140,9 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
109140

110141
@override
111142
void dispose() {
112-
_tabController.dispose();
143+
_tabController
144+
..removeListener(_onTabChanged)
145+
..dispose();
113146
for (final controller in _adFrequencyControllers.values) {
114147
controller.dispose();
115148
}
@@ -257,6 +290,9 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
257290
AppUserRole role,
258291
FeedAdConfiguration config,
259292
) {
293+
// Premium users do not see ads, so their settings are disabled.
294+
final isEnabled = role != AppUserRole.premiumUser;
295+
260296
return Column(
261297
children: [
262298
AppConfigIntField(
@@ -273,6 +309,7 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
273309
);
274310
},
275311
controller: _adFrequencyControllers[role],
312+
enabled: isEnabled,
276313
),
277314
AppConfigIntField(
278315
label: l10n.adPlacementIntervalLabel,
@@ -292,6 +329,7 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
292329
);
293330
},
294331
controller: _adPlacementIntervalControllers[role],
332+
enabled: isEnabled,
295333
),
296334
],
297335
);
@@ -338,9 +376,11 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
338376
),
339377
);
340378
case AppUserRole.premiumUser:
379+
// Premium users should not see ads, so their frequency is always 0.
380+
// The UI field is disabled, but this is a safeguard.
341381
return config.copyWith(
342382
frequencyConfig: config.frequencyConfig.copyWith(
343-
premiumAdFrequency: value,
383+
premiumAdFrequency: 0,
344384
),
345385
);
346386
}
@@ -365,9 +405,11 @@ class _FeedAdSettingsFormState extends State<FeedAdSettingsForm>
365405
),
366406
);
367407
case AppUserRole.premiumUser:
408+
// Premium users should not see ads, so their interval is always 0.
409+
// The UI field is disabled, but this is a safeguard.
368410
return config.copyWith(
369411
frequencyConfig: config.frequencyConfig.copyWith(
370-
premiumAdPlacementInterval: value,
412+
premiumAdPlacementInterval: 0,
371413
),
372414
);
373415
}

lib/app_configuration/widgets/interstitial_ad_settings_form.dart

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,40 @@ class _InterstitialAdSettingsFormState extends State<InterstitialAdSettingsForm>
4242
vsync: this,
4343
);
4444
_initializeControllers();
45+
_tabController.addListener(_onTabChanged);
46+
}
47+
48+
void _onTabChanged() {
49+
if (_tabController.indexIsChanging) return;
50+
51+
final selectedRole = AppUserRole.values[_tabController.index];
52+
if (selectedRole == AppUserRole.premiumUser) {
53+
final adConfig = widget.remoteConfig.adConfig;
54+
final interstitialAdConfig = adConfig.interstitialAdConfiguration;
55+
56+
// If the value for premium is not 0, update the config.
57+
// This enforces the business rule that premium users do not see ads.
58+
if (interstitialAdConfig
59+
.feedInterstitialAdFrequencyConfig
60+
.premiumUserTransitionsBeforeShowingInterstitialAds !=
61+
0) {
62+
final updatedFrequencyConfig = interstitialAdConfig
63+
.feedInterstitialAdFrequencyConfig
64+
.copyWith(
65+
premiumUserTransitionsBeforeShowingInterstitialAds: 0,
66+
);
67+
final updatedInterstitialAdConfig = interstitialAdConfig.copyWith(
68+
feedInterstitialAdFrequencyConfig: updatedFrequencyConfig,
69+
);
70+
widget.onConfigChanged(
71+
widget.remoteConfig.copyWith(
72+
adConfig: adConfig.copyWith(
73+
interstitialAdConfiguration: updatedInterstitialAdConfig,
74+
),
75+
),
76+
);
77+
}
78+
}
4579
}
4680

4781
@override
@@ -96,7 +130,9 @@ class _InterstitialAdSettingsFormState extends State<InterstitialAdSettingsForm>
96130

97131
@override
98132
void dispose() {
99-
_tabController.dispose();
133+
_tabController
134+
..removeListener(_onTabChanged)
135+
..dispose();
100136
for (final controller
101137
in _transitionsBeforeShowingInterstitialAdsControllers.values) {
102138
controller.dispose();
@@ -190,6 +226,9 @@ class _InterstitialAdSettingsFormState extends State<InterstitialAdSettingsForm>
190226
AppUserRole role,
191227
InterstitialAdConfiguration config,
192228
) {
229+
// Premium users do not see ads, so their settings are disabled.
230+
final isEnabled = role != AppUserRole.premiumUser;
231+
193232
return Column(
194233
children: [
195234
AppConfigIntField(
@@ -211,6 +250,7 @@ class _InterstitialAdSettingsFormState extends State<InterstitialAdSettingsForm>
211250
);
212251
},
213252
controller: _transitionsBeforeShowingInterstitialAdsControllers[role],
253+
enabled: isEnabled,
214254
),
215255
],
216256
);
@@ -255,8 +295,10 @@ class _InterstitialAdSettingsFormState extends State<InterstitialAdSettingsForm>
255295
standardUserTransitionsBeforeShowingInterstitialAds: value,
256296
);
257297
case AppUserRole.premiumUser:
298+
// Premium users should not see ads, so their frequency is always 0.
299+
// The UI field is disabled, but this is a safeguard.
258300
newFrequencyConfig = currentFrequencyConfig.copyWith(
259-
premiumUserTransitionsBeforeShowingInterstitialAds: value,
301+
premiumUserTransitionsBeforeShowingInterstitialAds: 0,
260302
);
261303
}
262304

lib/local_ads_management/view/archived_local_ads_page.dart

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,19 @@ class _ArchivedLocalAdsViewState extends State<_ArchivedLocalAdsView>
5151
with SingleTickerProviderStateMixin {
5252
late TabController _tabController;
5353

54+
// Statically define the tab order to ensure consistency.
55+
final List<AdType> _tabs = [
56+
AdType.native,
57+
AdType.banner,
58+
AdType.interstitial,
59+
AdType.video,
60+
];
61+
5462
@override
5563
void initState() {
5664
super.initState();
5765
_tabController = TabController(
58-
length: AdType.values.length,
66+
length: _tabs.length,
5967
vsync: this,
6068
);
6169
}
@@ -76,9 +84,7 @@ class _ArchivedLocalAdsViewState extends State<_ArchivedLocalAdsView>
7684
controller: _tabController,
7785
tabAlignment: TabAlignment.start,
7886
isScrollable: true,
79-
tabs: AdType.values
80-
.map((type) => Tab(text: type.l10n(context)))
81-
.toList(),
87+
tabs: _tabs.map((type) => Tab(text: type.l10n(context))).toList(),
8288
),
8389
),
8490
body: BlocListener<ArchiveLocalAdsBloc, ArchiveLocalAdsState>(
@@ -149,9 +155,9 @@ class _ArchivedLocalAdsViewState extends State<_ArchivedLocalAdsView>
149155
},
150156
child: TabBarView(
151157
controller: _tabController,
152-
children: AdType.values.map((type) {
153-
return _ArchivedLocalAdsDataTable(adType: type);
154-
}).toList(),
158+
children: _tabs
159+
.map((type) => _ArchivedLocalAdsDataTable(adType: type))
160+
.toList(),
155161
),
156162
),
157163
);

pubspec.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ packages:
9090
description:
9191
path: "."
9292
ref: HEAD
93-
resolved-ref: dd37d11352124113978ebeda3b509d932db61cf5
93+
resolved-ref: b6a3fb4ad862901a56b424fd92a9702f30513404
9494
url: "https://github.com/flutter-news-app-full-source-code/core.git"
9595
source: git
9696
version: "0.0.0"
@@ -269,10 +269,10 @@ packages:
269269
dependency: "direct main"
270270
description:
271271
name: go_router
272-
sha256: eb059dfe59f08546e9787f895bd01652076f996bcbf485a8609ef990419ad227
272+
sha256: b1488741c9ce37b72e026377c69a59c47378493156fc38efb5a54f6def3f92a3
273273
url: "https://pub.dev"
274274
source: hosted
275-
version: "16.2.1"
275+
version: "16.2.2"
276276
google_fonts:
277277
dependency: "direct main"
278278
description:
@@ -488,10 +488,10 @@ packages:
488488
dependency: transitive
489489
description:
490490
name: shared_preferences_android
491-
sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
491+
sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e
492492
url: "https://pub.dev"
493493
source: hosted
494-
version: "2.4.12"
494+
version: "2.4.13"
495495
shared_preferences_foundation:
496496
dependency: transitive
497497
description:

0 commit comments

Comments
 (0)