Skip to content

Commit fc8f927

Browse files
authored
Merge pull request #73 from flutter-news-app-full-source-code/sync-app-config-with-teh-remote-config-new-structure
Sync app config with teh remote config new structure
2 parents 2937c23 + 5a92e21 commit fc8f927

26 files changed

+2584
-354
lines changed

lib/app/bloc/app_bloc.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
7878
'User app settings not found for user ${user.id}. Creating default.',
7979
);
8080
final defaultSettings = UserAppSettings(
81-
id: user.id, // Use actual user ID for default settings
81+
id: user.id,
8282
displaySettings: const DisplaySettings(
8383
baseTheme: AppBaseTheme.system,
8484
accentTheme: AppAccentTheme.defaultBlue,

lib/app_configuration/bloc/app_configuration_bloc.dart

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ class AppConfigurationBloc
3333
state.copyWith(
3434
status: AppConfigurationStatus.success,
3535
remoteConfig: remoteConfig,
36-
originalRemoteConfig: remoteConfig, // Store the original config
36+
originalRemoteConfig: remoteConfig,
3737
isDirty: false,
38-
clearShowSaveSuccess:
39-
true, // Clear any previous success snackbar flag
38+
clearShowSaveSuccess: true,
4039
),
4140
);
4241
} on HttpException catch (e) {
@@ -82,9 +81,9 @@ class AppConfigurationBloc
8281
state.copyWith(
8382
status: AppConfigurationStatus.success,
8483
remoteConfig: updatedConfig,
85-
originalRemoteConfig: updatedConfig, // Update original config on save
84+
originalRemoteConfig: updatedConfig,
8685
isDirty: false,
87-
showSaveSuccess: true, // Set flag to show success snackbar
86+
showSaveSuccess: true,
8887
),
8988
);
9089
} on HttpException catch (e) {
@@ -109,8 +108,8 @@ class AppConfigurationBloc
109108
state.copyWith(
110109
remoteConfig: event.remoteConfig,
111110
isDirty: true,
112-
clearException: true, // Clear any previous error messages
113-
clearShowSaveSuccess: true, // Clear success snackbar on field change
111+
clearException: true,
112+
clearShowSaveSuccess: true,
114113
),
115114
);
116115
}
@@ -121,10 +120,10 @@ class AppConfigurationBloc
121120
) {
122121
emit(
123122
state.copyWith(
124-
remoteConfig: state.originalRemoteConfig, // Revert to original config
123+
remoteConfig: state.originalRemoteConfig,
125124
isDirty: false,
126-
clearException: true, // Clear any previous error messages
127-
clearShowSaveSuccess: true, // Clear success snackbar
125+
clearException: true,
126+
clearShowSaveSuccess: true,
128127
),
129128
);
130129
}

lib/app_configuration/view/tabs/advertisements_configuration_tab.dart

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:core/core.dart';
22
import 'package:flutter/material.dart';
3-
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/ad_config_form.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/ad_platform_config_form.dart';
4+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/article_ad_settings_form.dart';
5+
import 'package:flutter_news_app_web_dashboard_full_source_code/app_configuration/widgets/feed_ad_settings_form.dart';
46
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
57
import 'package:ui_kit/ui_kit.dart';
68

@@ -9,7 +11,7 @@ import 'package:ui_kit/ui_kit.dart';
911
///
1012
/// This tab allows configuration of various ad settings.
1113
/// {@endtemplate}
12-
class AdvertisementsConfigurationTab extends StatelessWidget {
14+
class AdvertisementsConfigurationTab extends StatefulWidget {
1315
/// {@macro advertisements_configuration_tab}
1416
const AdvertisementsConfigurationTab({
1517
required this.remoteConfig,
@@ -23,25 +25,113 @@ class AdvertisementsConfigurationTab extends StatelessWidget {
2325
/// Callback to notify parent of changes to the [RemoteConfig].
2426
final ValueChanged<RemoteConfig> onConfigChanged;
2527

28+
@override
29+
State<AdvertisementsConfigurationTab> createState() =>
30+
_AdvertisementsConfigurationTabState();
31+
}
32+
33+
class _AdvertisementsConfigurationTabState
34+
extends State<AdvertisementsConfigurationTab> {
35+
/// Notifier for the index of the currently expanded top-level ExpansionTile.
36+
///
37+
/// A value of `null` means no tile is expanded.
38+
final ValueNotifier<int?> _expandedTileIndex = ValueNotifier<int?>(null);
39+
40+
@override
41+
void dispose() {
42+
_expandedTileIndex.dispose();
43+
super.dispose();
44+
}
45+
2646
@override
2747
Widget build(BuildContext context) {
2848
final l10n = AppLocalizationsX(context).l10n;
2949

3050
return ListView(
3151
padding: const EdgeInsets.all(AppSpacing.lg),
3252
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-
],
53+
// Top-level ExpansionTile for Ad Platform Configuration
54+
ValueListenableBuilder<int?>(
55+
valueListenable: _expandedTileIndex,
56+
builder: (context, expandedIndex, child) {
57+
const tileIndex = 0;
58+
return ExpansionTile(
59+
key: ValueKey('adPlatformConfigTile_$expandedIndex'),
60+
title: Text(l10n.adPlatformConfigurationTitle),
61+
childrenPadding: const EdgeInsetsDirectional.only(
62+
start: AppSpacing.lg,
63+
top: AppSpacing.md,
64+
bottom: AppSpacing.md,
65+
),
66+
expandedCrossAxisAlignment: CrossAxisAlignment.start,
67+
onExpansionChanged: (isExpanded) {
68+
_expandedTileIndex.value = isExpanded ? tileIndex : null;
69+
},
70+
initiallyExpanded: expandedIndex == tileIndex,
71+
children: [
72+
AdPlatformConfigForm(
73+
remoteConfig: widget.remoteConfig,
74+
onConfigChanged: widget.onConfigChanged,
75+
),
76+
],
77+
);
78+
},
79+
),
80+
const SizedBox(height: AppSpacing.lg),
81+
// Top-level ExpansionTile for Feed Ad Settings
82+
ValueListenableBuilder<int?>(
83+
valueListenable: _expandedTileIndex,
84+
builder: (context, expandedIndex, child) {
85+
const tileIndex = 1;
86+
return ExpansionTile(
87+
key: ValueKey('feedAdSettingsTile_$expandedIndex'),
88+
title: Text(l10n.feedAdSettingsTitle),
89+
childrenPadding: const EdgeInsetsDirectional.only(
90+
start: AppSpacing.lg,
91+
top: AppSpacing.md,
92+
bottom: AppSpacing.md,
93+
),
94+
expandedCrossAxisAlignment: CrossAxisAlignment.start,
95+
onExpansionChanged: (isExpanded) {
96+
_expandedTileIndex.value = isExpanded ? tileIndex : null;
97+
},
98+
initiallyExpanded: expandedIndex == tileIndex,
99+
children: [
100+
FeedAdSettingsForm(
101+
remoteConfig: widget.remoteConfig,
102+
onConfigChanged: widget.onConfigChanged,
103+
),
104+
],
105+
);
106+
},
107+
),
108+
const SizedBox(height: AppSpacing.lg),
109+
// Top-level ExpansionTile for Article Ad Settings
110+
ValueListenableBuilder<int?>(
111+
valueListenable: _expandedTileIndex,
112+
builder: (context, expandedIndex, child) {
113+
const tileIndex = 2;
114+
return ExpansionTile(
115+
key: ValueKey('articleAdSettingsTile_$expandedIndex'),
116+
title: Text(l10n.articleAdSettingsTitle),
117+
childrenPadding: const EdgeInsetsDirectional.only(
118+
start: AppSpacing.lg,
119+
top: AppSpacing.md,
120+
bottom: AppSpacing.md,
121+
),
122+
expandedCrossAxisAlignment: CrossAxisAlignment.start,
123+
onExpansionChanged: (isExpanded) {
124+
_expandedTileIndex.value = isExpanded ? tileIndex : null;
125+
},
126+
initiallyExpanded: expandedIndex == tileIndex,
127+
children: [
128+
ArticleAdSettingsForm(
129+
remoteConfig: widget.remoteConfig,
130+
onConfigChanged: widget.onConfigChanged,
131+
),
132+
],
133+
);
134+
},
45135
),
46136
],
47137
);

0 commit comments

Comments
 (0)