Skip to content

Commit 904c0bb

Browse files
committed
feat(app_configuration): implement role-based in-article ad slot visibility
- Add TabBar for selecting user roles - Implement role-specific configuration fields for in-article ad slots - Use CheckboxListTile to enable/disable ad slots per role - Update state management with TabController - Refactor UI layout for better readability
1 parent eabfc06 commit 904c0bb

File tree

1 file changed

+96
-22
lines changed

1 file changed

+96
-22
lines changed

lib/app_configuration/widgets/article_ad_settings_form.dart

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import 'package:core/core.dart';
22
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
34
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
45
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/banner_ad_shape_l10n.dart';
56
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/in_article_ad_slot_type_l10n.dart';
7+
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/app_user_role_l10n.dart';
68
import 'package:ui_kit/ui_kit.dart';
79

810
/// {@template article_ad_settings_form}
@@ -28,14 +30,28 @@ class ArticleAdSettingsForm extends StatefulWidget {
2830

2931
class _ArticleAdSettingsFormState extends State<ArticleAdSettingsForm>
3032
with SingleTickerProviderStateMixin {
33+
late TabController _tabController;
34+
3135
@override
3236
void initState() {
3337
super.initState();
38+
_tabController = TabController(
39+
length: AppUserRole.values.length,
40+
vsync: this,
41+
);
3442
}
3543

3644
@override
3745
void didUpdateWidget(covariant ArticleAdSettingsForm oldWidget) {
3846
super.didUpdateWidget(oldWidget);
47+
// No specific controller updates needed here as the UI rebuilds based on
48+
// the remoteConfig directly.
49+
}
50+
51+
@override
52+
void dispose() {
53+
_tabController.dispose();
54+
super.dispose();
3955
}
4056

4157
@override
@@ -134,34 +150,92 @@ class _ArticleAdSettingsFormState extends State<ArticleAdSettingsForm>
134150
textAlign: TextAlign.start,
135151
),
136152
const SizedBox(height: AppSpacing.lg),
137-
...articleAdConfig.inArticleAdSlotConfigurations.map(
138-
(slotConfig) => SwitchListTile(
139-
title: Text(slotConfig.slotType.l10n(context)),
140-
value: slotConfig.enabled,
141-
onChanged: (value) {
142-
final updatedSlots = articleAdConfig
143-
.inArticleAdSlotConfigurations
144-
.map(
145-
(e) => e.slotType == slotConfig.slotType
146-
? e.copyWith(enabled: value)
147-
: e,
148-
)
149-
.toList();
150-
widget.onConfigChanged(
151-
widget.remoteConfig.copyWith(
152-
adConfig: adConfig.copyWith(
153-
articleAdConfiguration: articleAdConfig.copyWith(
154-
inArticleAdSlotConfigurations: updatedSlots,
155-
),
153+
Align(
154+
alignment: AlignmentDirectional.centerStart,
155+
child: SizedBox(
156+
height: kTextTabBarHeight,
157+
child: TabBar(
158+
controller: _tabController,
159+
tabAlignment: TabAlignment.start,
160+
isScrollable: true,
161+
tabs: AppUserRole.values
162+
.map((role) => Tab(text: role.l10n(context)))
163+
.toList(),
164+
),
165+
),
166+
),
167+
const SizedBox(height: AppSpacing.lg),
168+
SizedBox(
169+
height: 250,
170+
child: TabBarView(
171+
controller: _tabController,
172+
children: AppUserRole.values
173+
.map(
174+
(role) => _buildRoleSpecificFields(
175+
context,
176+
l10n,
177+
role,
178+
articleAdConfig,
156179
),
157-
),
158-
);
159-
},
180+
)
181+
.toList(),
160182
),
161183
),
162184
],
163185
),
164186
],
165187
);
166188
}
189+
190+
/// Builds role-specific configuration fields for in-article ad slots.
191+
///
192+
/// This widget displays checkboxes for each [InArticleAdSlotType] for a
193+
/// given [AppUserRole], allowing to enable/disable specific ad slots.
194+
Widget _buildRoleSpecificFields(
195+
BuildContext context,
196+
AppLocalizations l10n,
197+
AppUserRole role,
198+
ArticleAdConfiguration config,
199+
) {
200+
final roleSlots = config.visibleTo[role];
201+
final isEnabled = role != AppUserRole.premiumUser;
202+
203+
return Column(
204+
children: [
205+
// Checkbox for each InArticleAdSlotType
206+
for (final slotType in InArticleAdSlotType.values)
207+
CheckboxListTile(
208+
title: Text(slotType.l10n(context)),
209+
value: (roleSlots != null && roleSlots[slotType] == true) &&
210+
isEnabled,
211+
onChanged: isEnabled
212+
? (value) {
213+
final newRoleSlots =
214+
Map<InArticleAdSlotType, bool>.from(roleSlots ?? {});
215+
if (value ?? false) {
216+
newRoleSlots[slotType] = true;
217+
} else {
218+
newRoleSlots.remove(slotType);
219+
}
220+
221+
final newVisibleTo =
222+
Map<AppUserRole, Map<InArticleAdSlotType, bool>>.from(
223+
config.visibleTo,
224+
)..[role] = newRoleSlots;
225+
226+
widget.onConfigChanged(
227+
widget.remoteConfig.copyWith(
228+
adConfig: widget.remoteConfig.adConfig.copyWith(
229+
articleAdConfiguration: config.copyWith(
230+
visibleTo: newVisibleTo,
231+
),
232+
),
233+
),
234+
);
235+
}
236+
: null,
237+
),
238+
],
239+
);
240+
}
167241
}

0 commit comments

Comments
 (0)