Skip to content

Commit 7c10d9e

Browse files
authored
Add User Survey for Studienarbeit (#140)
# 🚀 Pull Request ## Brief Description Add a card, that user survey is online ## Screenshots <!-- Add screenshots if UI changes were made. Either new screens only or before/after --> | Hint on Homescreen | Dialog | WebView | |----------|----------| ----------| | ![image](https://github.com/user-attachments/assets/67138ca3-d380-42da-9340-3c45b68f20a8) | ![image](https://github.com/user-attachments/assets/705f4475-f500-4816-8f6f-47dfa2668598) | ![image](https://github.com/user-attachments/assets/2fc91ca5-b158-457e-85f1-f6d1d60893bf) [ ## GitHub Copilot Text This pull request introduces a new user survey feature in the MoveTopia app, including localized strings, UI components, and navigation updates. The changes are grouped into localization updates, UI components, and routing/navigation enhancements. ### Localization Updates: * Added survey-related strings in German (`lib/l10n/app_de.arb`) and English (`lib/l10n/app_en.arb`) for titles, descriptions, dialog options, and error messages. [[1]](diffhunk://#diff-36252c65ab82cbff4774b4983cb9027a2bef4cb738d5ea656c0b903939b3871aL483-R502) [[2]](diffhunk://#diff-9796fde3771f42a3a759ccc941731d83f96037a661e47dde27ce81d3447a69c2L502-R522) ### UI Components: * Introduced a `SurveyDebugSection` widget in `debug_settings_screen.dart` to manage survey-related debugging, including resetting and marking survey statuses (`dismissed` or `completed`). [[1]](diffhunk://#diff-8b192978bb952a1b80f775358d769ca31372d9e8c4b464f91dbffa182329cda5R13) [[2]](diffhunk://#diff-8b192978bb952a1b80f775358d769ca31372d9e8c4b464f91dbffa182329cda5R63-R65) [[3]](diffhunk://#diff-63a6b4b26da7aa4ed5edac78e69d5002e648beefa815905f070d281dc3e2c4daR1-R224) * Added a `SurveyCard` to the `TodayScreen` that displays the survey prompt and handles dismiss or completion actions. [[1]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566R16-R22) [[2]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566R46-R47) [[3]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566R119-R137) [[4]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566L145-R170) [[5]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566R193) [[6]](diffhunk://#diff-70a7a11b1888ccd7f44bd2466497659e9b19eb98003d549efd994b23d5c7f566R209-R218) * Created a `SurveyWebViewScreen` to display the survey in a WebView with options to open in a browser or mark as completed. ### Routing/Navigation Enhancements: * Updated `today/routes.dart` to include a new route for the `SurveyWebViewScreen`.
2 parents b426b67 + 35bac4b commit 7c10d9e

11 files changed

Lines changed: 797 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ app.*.map.json
5151
/android/fastlane/build/outputs/
5252
/android/fastlane/report.xml
5353
/lib/version.dart
54+
/.env

lib/l10n/app_de.arb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,5 +480,24 @@
480480
"type": "int"
481481
}
482482
}
483-
}
483+
},
484+
"@_SURVEY": {},
485+
"survey_card_title": "Nutzerumfrage",
486+
"survey_card_subtitle": "Studienarbeit MoveTopia",
487+
"survey_card_description": "Wir möchten gerne deine Meinung zu MoveTopia erfahren. Deine Teilnahme hilft uns, die App zu verbessern.",
488+
"survey_card_remind_later": "Später erinnern",
489+
"survey_card_take_survey": "Teilnehmen",
490+
"survey_dialog_title": "Umfrage öffnen",
491+
"survey_dialog_content": "Wie möchtest du die Umfrage öffnen?",
492+
"survey_dialog_not_interested": "Nicht teilnehmen",
493+
"survey_dialog_open_in_app": "In App öffnen",
494+
"survey_dialog_open_externally": "Im Browser öffnen",
495+
"survey_dialog_already_completed": "Bereits abgeschlossen",
496+
"survey_webview_title": "Nutzerumfrage",
497+
"survey_error_opening_url": "Konnte die URL nicht öffnen",
498+
"survey_completed_question": "Hast du die Umfrage abgeschlossen?",
499+
"survey_completed_yes": "Ja, habe ich",
500+
"survey_completed_no": "Nein, später",
501+
"common_back": "Zurück",
502+
"common_refresh": "Aktualisieren"
484503
}

lib/l10n/app_en.arb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,5 +499,25 @@
499499
"type": "int"
500500
}
501501
}
502-
}
502+
},
503+
504+
"@_SURVEY": {},
505+
"survey_card_title": "User Survey",
506+
"survey_card_subtitle": "MoveTopia Study Project",
507+
"survey_card_description": "We would like to hear your opinion about MoveTopia. Your participation helps us improve the app.",
508+
"survey_card_remind_later": "Remind me later",
509+
"survey_card_take_survey": "Participate",
510+
"survey_dialog_title": "Open Survey",
511+
"survey_dialog_content": "How would you like to open the survey?",
512+
"survey_dialog_not_interested": "Not interested",
513+
"survey_dialog_open_in_app": "Open in app",
514+
"survey_dialog_open_externally": "Open in browser",
515+
"survey_dialog_already_completed": "Already completed",
516+
"survey_webview_title": "User Survey",
517+
"survey_error_opening_url": "Could not open the URL",
518+
"survey_completed_question": "Did you complete the survey?",
519+
"survey_completed_yes": "Yes, I did",
520+
"survey_completed_no": "No, later",
521+
"common_back": "Back",
522+
"common_refresh": "Refresh"
503523
}

lib/presentation/profile/debug_settings/screen/debug_settings_screen.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import '../widgets/app_dates_section.dart';
1010
import '../widgets/badge_debug_section.dart';
1111
import '../widgets/cache_debug_section.dart';
1212
import '../widgets/streak_debug_section.dart';
13+
import '../widgets/survey_debug_section.dart';
1314

1415
class DebugSettingsScreen extends HookConsumerWidget {
1516
const DebugSettingsScreen({super.key});
@@ -59,6 +60,9 @@ class DebugSettingsScreen extends HookConsumerWidget {
5960
// Badge Debugging Sektion
6061
BadgeDebugSection(isLoading: isLoading),
6162

63+
// Survey Debugging Sektion
64+
SurveyDebugSection(isLoading: isLoading),
65+
6266
// Cache Debuggin Sektion
6367
CacheDebugSection(isLoading: isLoading)
6468
],
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
3+
import 'package:hooks_riverpod/hooks_riverpod.dart';
4+
import 'package:movetopia/presentation/today/widgets/survey_card.dart';
5+
import 'package:shared_preferences/shared_preferences.dart';
6+
7+
/// Debug Sektion for User Survey
8+
class SurveyDebugSection extends ConsumerWidget {
9+
final ValueNotifier<bool> isLoading;
10+
11+
const SurveyDebugSection({super.key, required this.isLoading});
12+
13+
@override
14+
Widget build(BuildContext context, WidgetRef ref) {
15+
final l10n = AppLocalizations.of(context)!;
16+
final theme = Theme.of(context);
17+
18+
return Card(
19+
margin: const EdgeInsets.symmetric(vertical: 8),
20+
child: Padding(
21+
padding: const EdgeInsets.all(16),
22+
child: Column(
23+
crossAxisAlignment: CrossAxisAlignment.start,
24+
children: [
25+
Text(
26+
'Survey Debug',
27+
style: theme.textTheme.titleLarge?.copyWith(
28+
fontWeight: FontWeight.bold,
29+
color: theme.colorScheme.primary,
30+
),
31+
),
32+
const SizedBox(height: 16),
33+
FutureBuilder<Map<String, bool>>(
34+
future: _getSurveyStatus(),
35+
builder: (context, snapshot) {
36+
if (snapshot.connectionState == ConnectionState.waiting) {
37+
return const CircularProgressIndicator();
38+
}
39+
40+
final status =
41+
snapshot.data ?? {'dismissed': false, 'completed': false};
42+
final dismissed = status['dismissed'] ?? false;
43+
final completed = status['completed'] ?? false;
44+
45+
return Column(
46+
crossAxisAlignment: CrossAxisAlignment.start,
47+
children: [
48+
Text('Aktueller Status:',
49+
style: theme.textTheme.titleMedium),
50+
const SizedBox(height: 8),
51+
_buildStatusChip(context, 'Dismissed', dismissed,
52+
theme.colorScheme.error),
53+
const SizedBox(height: 4),
54+
_buildStatusChip(context, 'Completed', completed,
55+
theme.colorScheme.primary),
56+
const SizedBox(height: 24),
57+
Row(
58+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
59+
children: [
60+
ElevatedButton.icon(
61+
icon: const Icon(Icons.refresh),
62+
label: const Text('Reset Status'),
63+
onPressed: () => _resetSurveyStatus(context),
64+
style: ElevatedButton.styleFrom(
65+
backgroundColor: theme.colorScheme.primary,
66+
foregroundColor: theme.colorScheme.onPrimary,
67+
),
68+
),
69+
ElevatedButton.icon(
70+
icon: const Icon(Icons.check_circle),
71+
label: const Text('Mark Completed'),
72+
onPressed: () => _markSurveyCompleted(context),
73+
style: ElevatedButton.styleFrom(
74+
backgroundColor: theme.colorScheme.secondary,
75+
foregroundColor: theme.colorScheme.onSecondary,
76+
),
77+
),
78+
],
79+
),
80+
const SizedBox(height: 12),
81+
Row(
82+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
83+
children: [
84+
ElevatedButton.icon(
85+
icon: const Icon(Icons.close),
86+
label: const Text('Mark Dismissed'),
87+
onPressed: () => _markSurveyDismissed(context),
88+
style: ElevatedButton.styleFrom(
89+
backgroundColor: theme.colorScheme.error,
90+
foregroundColor: theme.colorScheme.onError,
91+
),
92+
),
93+
ElevatedButton.icon(
94+
icon: const Icon(Icons.date_range),
95+
label: const Text('Set to Today'),
96+
onPressed: () => _setStartDateToToday(context),
97+
style: ElevatedButton.styleFrom(
98+
backgroundColor: theme.colorScheme.tertiary,
99+
foregroundColor: theme.colorScheme.onTertiary,
100+
),
101+
),
102+
],
103+
),
104+
],
105+
);
106+
},
107+
),
108+
],
109+
),
110+
),
111+
);
112+
}
113+
114+
/// Creates a status chip with a label and value
115+
Widget _buildStatusChip(
116+
BuildContext context, String label, bool value, Color color) {
117+
return Row(
118+
children: [
119+
Container(
120+
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
121+
decoration: BoxDecoration(
122+
color: value ? color : Colors.grey.shade300,
123+
borderRadius: BorderRadius.circular(16),
124+
),
125+
child: Text(
126+
label,
127+
style: TextStyle(
128+
color: value ? Colors.white : Colors.black54,
129+
fontWeight: FontWeight.bold,
130+
),
131+
),
132+
),
133+
const SizedBox(width: 8),
134+
Text(value ? 'Ja' : 'Nein'),
135+
],
136+
);
137+
}
138+
139+
/// Reads the survey status from SharedPreferences
140+
Future<Map<String, bool>> _getSurveyStatus() async {
141+
final prefs = await SharedPreferences.getInstance();
142+
final dismissed =
143+
prefs.getBool(SurveyConstants.prefKeySurveyDismissed) ?? false;
144+
final completed =
145+
prefs.getBool(SurveyConstants.prefKeySurveyCompleted) ?? false;
146+
147+
return {
148+
'dismissed': dismissed,
149+
'completed': completed,
150+
};
151+
}
152+
153+
/// Resets the survey status in SharedPreferences
154+
Future<void> _resetSurveyStatus(BuildContext context) async {
155+
isLoading.value = true;
156+
157+
try {
158+
final prefs = await SharedPreferences.getInstance();
159+
await prefs.remove(SurveyConstants.prefKeySurveyDismissed);
160+
await prefs.remove(SurveyConstants.prefKeySurveyCompleted);
161+
162+
if (context.mounted) {
163+
ScaffoldMessenger.of(context).showSnackBar(
164+
const SnackBar(content: Text('Survey-Status zurückgesetzt')),
165+
);
166+
}
167+
} finally {
168+
isLoading.value = false;
169+
}
170+
}
171+
172+
/// Marks the survey as completed
173+
Future<void> _markSurveyCompleted(BuildContext context) async {
174+
isLoading.value = true;
175+
176+
try {
177+
final prefs = await SharedPreferences.getInstance();
178+
await prefs.setBool(SurveyConstants.prefKeySurveyCompleted, true);
179+
180+
if (context.mounted) {
181+
ScaffoldMessenger.of(context).showSnackBar(
182+
const SnackBar(content: Text('Umfrage als abgeschlossen markiert')),
183+
);
184+
}
185+
} finally {
186+
isLoading.value = false;
187+
}
188+
}
189+
190+
/// Marks the survey as dismissed
191+
Future<void> _markSurveyDismissed(BuildContext context) async {
192+
isLoading.value = true;
193+
194+
try {
195+
final prefs = await SharedPreferences.getInstance();
196+
await prefs.setBool(SurveyConstants.prefKeySurveyDismissed, true);
197+
198+
if (context.mounted) {
199+
ScaffoldMessenger.of(context).showSnackBar(
200+
const SnackBar(content: Text('Umfrage als verworfen markiert')),
201+
);
202+
}
203+
} finally {
204+
isLoading.value = false;
205+
}
206+
}
207+
208+
/// Set the start date to today
209+
Future<void> _setStartDateToToday(BuildContext context) async {
210+
isLoading.value = true;
211+
212+
try {
213+
if (context.mounted) {
214+
ScaffoldMessenger.of(context).showSnackBar(
215+
const SnackBar(
216+
content: Text(
217+
'StartDate ist ein static final Feld und kann zur Laufzeit nicht geändert werden. Bitte ändere den Code direkt.')),
218+
);
219+
}
220+
} finally {
221+
isLoading.value = false;
222+
}
223+
}
224+
}

lib/presentation/today/routes.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import 'package:flutter/material.dart';
22
import 'package:go_router/go_router.dart';
3+
import 'package:movetopia/presentation/today/screen/survey_webview_screen.dart';
34
import 'package:movetopia/presentation/today/screen/today_screen.dart';
45

56
const todayPath = '/today';
67

8+
const String surveyWebViewPath = 'survey-webview';
9+
const String fullSurveyWebViewPath = '$todayPath/$surveyWebViewPath';
10+
711
class TodayRoutes {
812
static final navigatorKey = GlobalKey<NavigatorState>();
913

1014
static List<RouteBase> routes = [
1115
GoRoute(
1216
path: todayPath,
1317
builder: (context, state) => const TodayScreen(),
14-
)
18+
routes: [
19+
GoRoute(
20+
path: surveyWebViewPath,
21+
builder: (context, state) => SurveyWebViewScreen(
22+
surveyUrl: state.extra as String,
23+
),
24+
),
25+
],
26+
),
1527
];
1628
}

0 commit comments

Comments
 (0)