Skip to content

Commit 2937c23

Browse files
authored
Merge pull request #72 from flutter-news-app-full-source-code/Extract-app-config-Tabs-into-Separate-Pages
Extract app config tabs into separate pages
2 parents c95ca53 + 929df02 commit 2937c23

15 files changed

+1528
-1446
lines changed

lib/app_configuration/view/app_configuration_page.dart

Lines changed: 24 additions & 1430 deletions
Large diffs are not rendered by default.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/ad_config_form.dart';
4+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
5+
import 'package:ui_kit/ui_kit.dart';
6+
7+
/// {@template advertisements_configuration_tab}
8+
/// A widget representing the "Advertisements" tab in the App Configuration page.
9+
///
10+
/// This tab allows configuration of various ad settings.
11+
/// {@endtemplate}
12+
class AdvertisementsConfigurationTab extends StatelessWidget {
13+
/// {@macro advertisements_configuration_tab}
14+
const AdvertisementsConfigurationTab({
15+
required this.remoteConfig,
16+
required this.onConfigChanged,
17+
super.key,
18+
});
19+
20+
/// The current [RemoteConfig] object.
21+
final RemoteConfig remoteConfig;
22+
23+
/// Callback to notify parent of changes to the [RemoteConfig].
24+
final ValueChanged<RemoteConfig> onConfigChanged;
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
final l10n = AppLocalizationsX(context).l10n;
29+
30+
return ListView(
31+
padding: const EdgeInsets.all(AppSpacing.lg),
32+
children: [
33+
// Top-level ExpansionTile for Ad Settings
34+
ExpansionTile(
35+
title: Text(l10n.adSettingsTitle),
36+
childrenPadding: const EdgeInsets.symmetric(
37+
horizontal: AppSpacing.xxl,
38+
),
39+
children: [
40+
AdConfigForm(
41+
remoteConfig: remoteConfig,
42+
onConfigChanged: onConfigChanged,
43+
),
44+
],
45+
),
46+
],
47+
);
48+
}
49+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/feed_decorator_form.dart';
4+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/user_preference_limits_form.dart';
5+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
6+
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/extensions/feed_decorator_type_l10n.dart';
7+
import 'package:ui_kit/ui_kit.dart';
8+
9+
/// {@template feed_configuration_tab}
10+
/// A widget representing the "Feed" tab in the App Configuration page.
11+
///
12+
/// This tab allows configuration of user content limits and feed decorators.
13+
/// {@endtemplate}
14+
class FeedConfigurationTab extends StatelessWidget {
15+
/// {@macro feed_configuration_tab}
16+
const FeedConfigurationTab({
17+
required this.remoteConfig,
18+
required this.onConfigChanged,
19+
super.key,
20+
});
21+
22+
/// The current [RemoteConfig] object.
23+
final RemoteConfig remoteConfig;
24+
25+
/// Callback to notify parent of changes to the [RemoteConfig].
26+
final ValueChanged<RemoteConfig> onConfigChanged;
27+
28+
@override
29+
Widget build(BuildContext context) {
30+
final l10n = AppLocalizationsX(context).l10n;
31+
32+
return ListView(
33+
padding: const EdgeInsets.all(AppSpacing.lg),
34+
children: [
35+
// Top-level ExpansionTile for User Content Limits
36+
ExpansionTile(
37+
title: Text(l10n.userContentLimitsTitle),
38+
childrenPadding: const EdgeInsets.symmetric(
39+
horizontal: AppSpacing.xxl,
40+
),
41+
children: [
42+
UserPreferenceLimitsForm(
43+
remoteConfig: remoteConfig,
44+
onConfigChanged: onConfigChanged,
45+
),
46+
],
47+
),
48+
const SizedBox(height: AppSpacing.lg),
49+
// New Top-level ExpansionTile for Feed Decorators
50+
ExpansionTile(
51+
title: Text(l10n.feedDecoratorsTitle),
52+
childrenPadding: const EdgeInsets.symmetric(
53+
horizontal: AppSpacing.xxl,
54+
vertical: AppSpacing.md,
55+
),
56+
children: [
57+
Text(
58+
l10n.feedDecoratorsDescription,
59+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
60+
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
61+
),
62+
),
63+
const SizedBox(height: AppSpacing.lg),
64+
// Individual ExpansionTiles for each Feed Decorator, nested
65+
for (final decoratorType in FeedDecoratorType.values)
66+
Padding(
67+
padding: const EdgeInsets.only(bottom: AppSpacing.md),
68+
child: ExpansionTile(
69+
title: Text(decoratorType.l10n(context)),
70+
childrenPadding: const EdgeInsets.symmetric(
71+
horizontal: AppSpacing.xxl,
72+
),
73+
children: [
74+
FeedDecoratorForm(
75+
decoratorType: decoratorType,
76+
remoteConfig: remoteConfig.copyWith(
77+
feedDecoratorConfig:
78+
Map.from(
79+
remoteConfig.feedDecoratorConfig,
80+
)..putIfAbsent(
81+
decoratorType,
82+
() => FeedDecoratorConfig(
83+
category:
84+
decoratorType ==
85+
FeedDecoratorType.suggestedTopics ||
86+
decoratorType ==
87+
FeedDecoratorType.suggestedSources
88+
? FeedDecoratorCategory.contentCollection
89+
: FeedDecoratorCategory.callToAction,
90+
enabled: false,
91+
visibleTo: const {},
92+
itemsToDisplay:
93+
decoratorType ==
94+
FeedDecoratorType.suggestedTopics ||
95+
decoratorType ==
96+
FeedDecoratorType.suggestedSources
97+
? 0
98+
: null,
99+
),
100+
),
101+
),
102+
onConfigChanged: onConfigChanged,
103+
),
104+
],
105+
),
106+
),
107+
],
108+
),
109+
],
110+
);
111+
}
112+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/app_config_form_fields.dart';
4+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
5+
import 'package:ui_kit/ui_kit.dart';
6+
7+
/// {@template general_configuration_tab}
8+
/// A widget representing the "General" tab in the App Configuration page.
9+
///
10+
/// This tab allows configuration of maintenance mode and force update settings.
11+
/// {@endtemplate}
12+
class GeneralConfigurationTab extends StatelessWidget {
13+
/// {@macro general_configuration_tab}
14+
const GeneralConfigurationTab({
15+
required this.remoteConfig,
16+
required this.onConfigChanged,
17+
super.key,
18+
});
19+
20+
/// The current [RemoteConfig] object.
21+
final RemoteConfig remoteConfig;
22+
23+
/// Callback to notify parent of changes to the [RemoteConfig].
24+
final ValueChanged<RemoteConfig> onConfigChanged;
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
final l10n = AppLocalizationsX(context).l10n;
29+
30+
return ListView(
31+
padding: const EdgeInsets.all(AppSpacing.lg),
32+
children: [
33+
// Top-level ExpansionTile for Maintenance Section
34+
ExpansionTile(
35+
title: Text(l10n.maintenanceModeTitle),
36+
childrenPadding: const EdgeInsets.symmetric(
37+
horizontal: AppSpacing.xxl,
38+
vertical: AppSpacing.md,
39+
),
40+
children: [
41+
Column(
42+
crossAxisAlignment: CrossAxisAlignment.start,
43+
children: [
44+
Text(
45+
l10n.maintenanceModeDescription,
46+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
47+
color: Theme.of(
48+
context,
49+
).colorScheme.onSurface.withOpacity(0.7),
50+
),
51+
),
52+
const SizedBox(height: AppSpacing.lg),
53+
SwitchListTile(
54+
title: Text(l10n.isUnderMaintenanceLabel),
55+
subtitle: Text(l10n.isUnderMaintenanceDescription),
56+
value: remoteConfig.appStatus.isUnderMaintenance,
57+
onChanged: (value) {
58+
onConfigChanged(
59+
remoteConfig.copyWith(
60+
appStatus: remoteConfig.appStatus.copyWith(
61+
isUnderMaintenance: value,
62+
),
63+
),
64+
);
65+
},
66+
),
67+
],
68+
),
69+
],
70+
),
71+
const SizedBox(height: AppSpacing.lg),
72+
// Top-level ExpansionTile for Force Update Section
73+
ExpansionTile(
74+
title: Text(l10n.forceUpdateTitle),
75+
childrenPadding: const EdgeInsets.symmetric(
76+
horizontal: AppSpacing.xxl,
77+
vertical: AppSpacing.md,
78+
),
79+
children: [
80+
Column(
81+
crossAxisAlignment: CrossAxisAlignment.start,
82+
children: [
83+
Text(
84+
l10n.forceUpdateDescription,
85+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
86+
color: Theme.of(
87+
context,
88+
).colorScheme.onSurface.withOpacity(0.7),
89+
),
90+
),
91+
const SizedBox(height: AppSpacing.lg),
92+
AppConfigTextField(
93+
label: l10n.latestAppVersionLabel,
94+
description: l10n.latestAppVersionDescription,
95+
value: remoteConfig.appStatus.latestAppVersion,
96+
onChanged: (value) {
97+
onConfigChanged(
98+
remoteConfig.copyWith(
99+
appStatus: remoteConfig.appStatus.copyWith(
100+
latestAppVersion: value,
101+
),
102+
),
103+
);
104+
},
105+
),
106+
SwitchListTile(
107+
title: Text(l10n.isLatestVersionOnlyLabel),
108+
subtitle: Text(l10n.isLatestVersionOnlyDescription),
109+
value: remoteConfig.appStatus.isLatestVersionOnly,
110+
onChanged: (value) {
111+
onConfigChanged(
112+
remoteConfig.copyWith(
113+
appStatus: remoteConfig.appStatus.copyWith(
114+
isLatestVersionOnly: value,
115+
),
116+
),
117+
);
118+
},
119+
),
120+
AppConfigTextField(
121+
label: l10n.iosUpdateUrlLabel,
122+
description: l10n.iosUpdateUrlDescription,
123+
value: remoteConfig.appStatus.iosUpdateUrl,
124+
onChanged: (value) {
125+
onConfigChanged(
126+
remoteConfig.copyWith(
127+
appStatus: remoteConfig.appStatus.copyWith(
128+
iosUpdateUrl: value,
129+
),
130+
),
131+
);
132+
},
133+
),
134+
AppConfigTextField(
135+
label: l10n.androidUpdateUrlLabel,
136+
description: l10n.androidUpdateUrlDescription,
137+
value: remoteConfig.appStatus.androidUpdateUrl,
138+
onChanged: (value) {
139+
onConfigChanged(
140+
remoteConfig.copyWith(
141+
appStatus: remoteConfig.appStatus.copyWith(
142+
androidUpdateUrl: value,
143+
),
144+
),
145+
);
146+
},
147+
),
148+
],
149+
),
150+
],
151+
),
152+
],
153+
);
154+
}
155+
}

0 commit comments

Comments
 (0)