From 1e9fec7d3e5950e13fe554da47fa46bba3331c0f Mon Sep 17 00:00:00 2001 From: Mohit Maulekhi Date: Sun, 21 Dec 2025 16:02:47 +0530 Subject: [PATCH 1/3] Add user prompt to optionally skip tutorial on first launch --- .../modules/home/views/home_page_body.dart | 38 +++++- .../modules/home/views/tutorial_modal.dart | 124 ++++++++++++++++++ .../utils/app_settings/save_tour_status.dart | 8 ++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 lib/app/modules/home/views/tutorial_modal.dart diff --git a/lib/app/modules/home/views/home_page_body.dart b/lib/app/modules/home/views/home_page_body.dart index a409e8ee..9314333e 100644 --- a/lib/app/modules/home/views/home_page_body.dart +++ b/lib/app/modules/home/views/home_page_body.dart @@ -4,6 +4,8 @@ import 'package:get/get.dart'; import 'package:taskwarrior/app/modules/home/views/show_tasks.dart'; import 'package:taskwarrior/app/modules/home/views/show_tasks_replica.dart'; import 'package:taskwarrior/app/modules/home/views/tasks_builder.dart'; +import 'package:taskwarrior/app/modules/home/views/tutorial_modal.dart'; +import 'package:taskwarrior/app/utils/app_settings/app_settings.dart'; import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart'; import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; import 'package:taskwarrior/app/utils/language/sentence_manager.dart'; @@ -13,10 +15,44 @@ class HomePageBody extends StatelessWidget { final HomeController controller; const HomePageBody({required this.controller, super.key}); + void _showTutorialModal(BuildContext context) { + Future.delayed( + const Duration(milliseconds: 500), + () async { + bool promptShown = await SaveTourStatus.getTutorialPromptShown(); + if (!promptShown && context.mounted) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return TutorialModal( + onYes: () async { + await SaveTourStatus.saveTutorialPromptShown(true); + if (dialogContext.mounted) { + Navigator.of(dialogContext).pop(); + controller.initInAppTour(); + controller.tutorialCoachMark.show(context: context); + } + }, + onNo: () async { + await SaveTourStatus.saveTutorialPromptShown(true); + await SaveTourStatus.saveInAppTourStatus(true); + if (dialogContext.mounted) { + Navigator.of(dialogContext).pop(); + } + }, + ); + }, + ); + } + }, + ); + } + @override Widget build(BuildContext context) { controller.initInAppTour(); - controller.showInAppTour(context); + _showTutorialModal(context); TaskwarriorColorTheme tColors = Theme.of(context).extension()!; return DoubleBackToCloseApp( diff --git a/lib/app/modules/home/views/tutorial_modal.dart b/lib/app/modules/home/views/tutorial_modal.dart new file mode 100644 index 00000000..dc0e7d02 --- /dev/null +++ b/lib/app/modules/home/views/tutorial_modal.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; + +class TutorialModal extends StatelessWidget { + final VoidCallback onYes; + final VoidCallback onNo; + + const TutorialModal({ + super.key, + required this.onYes, + required this.onNo, + }); + + @override + Widget build(BuildContext context) { + TaskwarriorColorTheme tColors = + Theme.of(context).extension()!; + + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + elevation: 10, + backgroundColor: tColors.dialogBackgroundColor, + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Icon + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: tColors.primaryBackgroundColor?.withValues(alpha: 0.3), + shape: BoxShape.circle, + ), + child: Icon( + Icons.school_outlined, + size: 48, + color: tColors.primaryTextColor, + ), + ), + const SizedBox(height: 20), + + // Title + Text( + 'Welcome!', + style: GoogleFonts.poppins( + fontSize: 24, + fontWeight: FontWeight.bold, + color: tColors.primaryTextColor, + ), + ), + const SizedBox(height: 12), + + // Message + Text( + 'Would you like to see a quick tutorial to learn how to use this app?', + textAlign: TextAlign.center, + style: GoogleFonts.poppins( + fontSize: 16, + color: tColors.primaryTextColor?.withValues(alpha: 0.8), + height: 1.5, + ), + ), + const SizedBox(height: 28), + + // Buttons + Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: onNo, + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + side: BorderSide( + color: tColors.primaryTextColor!.withValues(alpha: 0.3), + width: 1.5, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Text( + 'No, thanks', + style: GoogleFonts.poppins( + fontSize: 15, + fontWeight: FontWeight.w500, + color: tColors.primaryTextColor, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: ElevatedButton( + onPressed: onYes, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: tColors.primaryBackgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 2, + ), + child: Text( + 'Show Tutorial', + style: GoogleFonts.poppins( + fontSize: 15, + fontWeight: FontWeight.w600, + color: tColors.primaryTextColor, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/utils/app_settings/save_tour_status.dart b/lib/app/utils/app_settings/save_tour_status.dart index 11b23689..adc45daf 100644 --- a/lib/app/utils/app_settings/save_tour_status.dart +++ b/lib/app/utils/app_settings/save_tour_status.dart @@ -62,4 +62,12 @@ class SaveTourStatus { static Future getTaskSwipeTutorialStatus() async { return _preferences?.getBool('task_swipe_tutorial_completed') ?? false; } + + static Future saveTutorialPromptShown(bool status) async { + await _preferences?.setBool('tutorial_prompt_shown', status); + } + + static Future getTutorialPromptShown() async { + return _preferences?.getBool('tutorial_prompt_shown') ?? false; + } } From b7e26a7866d564d041cc177d7a27348418a9d4c2 Mon Sep 17 00:00:00 2001 From: Mohit Maulekhi Date: Sun, 21 Dec 2025 16:12:02 +0530 Subject: [PATCH 2/3] all tutorial will be turned off if user selects no --- lib/app/modules/home/views/home_page_body.dart | 2 +- lib/app/utils/app_settings/save_tour_status.dart | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/app/modules/home/views/home_page_body.dart b/lib/app/modules/home/views/home_page_body.dart index 9314333e..508c831d 100644 --- a/lib/app/modules/home/views/home_page_body.dart +++ b/lib/app/modules/home/views/home_page_body.dart @@ -36,7 +36,7 @@ class HomePageBody extends StatelessWidget { }, onNo: () async { await SaveTourStatus.saveTutorialPromptShown(true); - await SaveTourStatus.saveInAppTourStatus(true); + await SaveTourStatus.disableAllTutorials(); if (dialogContext.mounted) { Navigator.of(dialogContext).pop(); } diff --git a/lib/app/utils/app_settings/save_tour_status.dart b/lib/app/utils/app_settings/save_tour_status.dart index adc45daf..1d669f34 100644 --- a/lib/app/utils/app_settings/save_tour_status.dart +++ b/lib/app/utils/app_settings/save_tour_status.dart @@ -70,4 +70,14 @@ class SaveTourStatus { static Future getTutorialPromptShown() async { return _preferences?.getBool('tutorial_prompt_shown') ?? false; } + + static Future disableAllTutorials() async { + await saveReportsTourStatus(true); + await saveInAppTourStatus(true); + await saveFilterTourStatus(true); + await saveProfileTourStatus(true); + await saveDetailsTourStatus(true); + await saveManageTaskServerTourStatus(true); + await saveTaskSwipeTutorialStatus(true); + } } From 3af0963c4f0c0eb11f1ddf60e6786a173f8c8aa1 Mon Sep 17 00:00:00 2001 From: mohitmaulekhi Date: Thu, 8 Jan 2026 21:41:03 +0530 Subject: [PATCH 3/3] Changes according to the suggestions --- .../modules/home/views/tutorial_modal.dart | 66 +++++++++++-------- lib/app/utils/language/bengali_sentences.dart | 10 +++ lib/app/utils/language/english_sentences.dart | 10 +++ lib/app/utils/language/french_sentences.dart | 10 +++ lib/app/utils/language/hindi_sentences.dart | 10 +++ lib/app/utils/language/marathi_sentences.dart | 10 +++ lib/app/utils/language/sentences.dart | 6 ++ lib/app/utils/language/spanish_sentences.dart | 10 +++ 8 files changed, 104 insertions(+), 28 deletions(-) diff --git a/lib/app/modules/home/views/tutorial_modal.dart b/lib/app/modules/home/views/tutorial_modal.dart index dc0e7d02..be748142 100644 --- a/lib/app/modules/home/views/tutorial_modal.dart +++ b/lib/app/modules/home/views/tutorial_modal.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:taskwarrior/app/utils/app_settings/app_settings.dart'; +import 'package:taskwarrior/app/utils/language/sentence_manager.dart'; import 'package:taskwarrior/app/utils/themes/theme_extension.dart'; class TutorialModal extends StatelessWidget { @@ -16,6 +18,9 @@ class TutorialModal extends StatelessWidget { Widget build(BuildContext context) { TaskwarriorColorTheme tColors = Theme.of(context).extension()!; + final sentences = + SentenceManager(currentLanguage: AppSettings.selectedLanguage) + .sentences; return Dialog( shape: RoundedRectangleBorder( @@ -45,7 +50,7 @@ class TutorialModal extends StatelessWidget { // Title Text( - 'Welcome!', + sentences.tutorialModalWelcome, style: GoogleFonts.poppins( fontSize: 24, fontWeight: FontWeight.bold, @@ -56,7 +61,7 @@ class TutorialModal extends StatelessWidget { // Message Text( - 'Would you like to see a quick tutorial to learn how to use this app?', + sentences.tutorialModalMessage, textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 16, @@ -66,50 +71,55 @@ class TutorialModal extends StatelessWidget { ), const SizedBox(height: 28), - // Buttons - Row( + // Buttons - Vertical Layout + Column( + mainAxisSize: MainAxisSize.min, children: [ - Expanded( - child: OutlinedButton( - onPressed: onNo, - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 14), - side: BorderSide( - color: tColors.primaryTextColor!.withValues(alpha: 0.3), - width: 1.5, - ), + // Primary Action: Keep Tutorials (with outline and filled background) + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: onYes, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: tColors.primaryBackgroundColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), + side: BorderSide( + color: tColors.primaryBackgroundColor ?? Colors.blue, + width: 2, + ), ), + elevation: 2, ), child: Text( - 'No, thanks', + sentences.tutorialModalKeepTutorials, style: GoogleFonts.poppins( - fontSize: 15, - fontWeight: FontWeight.w500, + fontSize: 16, + fontWeight: FontWeight.w600, color: tColors.primaryTextColor, ), ), ), ), - const SizedBox(width: 12), - Expanded( - child: ElevatedButton( - onPressed: onYes, - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 14), - backgroundColor: tColors.primaryBackgroundColor, + const SizedBox(height: 12), + // Secondary Action: Skip all Tutorials (text button, no outline, smaller font) + SizedBox( + width: double.infinity, + child: TextButton( + onPressed: onNo, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), - elevation: 2, ), child: Text( - 'Show Tutorial', + sentences.tutorialModalSkipAllTutorials, style: GoogleFonts.poppins( - fontSize: 15, - fontWeight: FontWeight.w600, - color: tColors.primaryTextColor, + fontSize: 14, + fontWeight: FontWeight.w500, + color: tColors.primaryTextColor?.withValues(alpha: 0.7), ), ), ), diff --git a/lib/app/utils/language/bengali_sentences.dart b/lib/app/utils/language/bengali_sentences.dart index 77f8584c..58161bd3 100644 --- a/lib/app/utils/language/bengali_sentences.dart +++ b/lib/app/utils/language/bengali_sentences.dart @@ -664,4 +664,14 @@ class BengaliSentences extends Sentences { String get logs => 'লগস'; @override String get checkAllDebugLogsHere => 'এখানে সমস্ত ডিবাগ লগ পরীক্ষা করুন'; + + @override + String get tutorialModalWelcome => 'স্বাগতম!'; + @override + String get tutorialModalMessage => + 'আপনি কি এই অ্যাপটি কীভাবে ব্যবহার করতে হয় তা শেখার জন্য একটি দ্রুত টিউটোরিয়াল দেখতে চান?'; + @override + String get tutorialModalKeepTutorials => 'টিউটোরিয়াল রাখুন'; + @override + String get tutorialModalSkipAllTutorials => 'সমস্ত টিউটোরিয়াল এড়িয়ে যান'; } diff --git a/lib/app/utils/language/english_sentences.dart b/lib/app/utils/language/english_sentences.dart index 36852521..ae2622f6 100644 --- a/lib/app/utils/language/english_sentences.dart +++ b/lib/app/utils/language/english_sentences.dart @@ -653,4 +653,14 @@ class EnglishSentences extends Sentences { String get logs => 'Logs'; @override String get checkAllDebugLogsHere => 'Check all debug logs here'; + + @override + String get tutorialModalWelcome => 'Welcome!'; + @override + String get tutorialModalMessage => + 'Would you like to see a quick tutorial to learn how to use this app?'; + @override + String get tutorialModalKeepTutorials => 'Keep Tutorials'; + @override + String get tutorialModalSkipAllTutorials => 'Skip all Tutorials'; } diff --git a/lib/app/utils/language/french_sentences.dart b/lib/app/utils/language/french_sentences.dart index c9d37bcf..38459c3b 100644 --- a/lib/app/utils/language/french_sentences.dart +++ b/lib/app/utils/language/french_sentences.dart @@ -681,4 +681,14 @@ class FrenchSentences extends Sentences { @override String get checkAllDebugLogsHere => 'Vérifiez tous les journaux de débogage ici'; + + @override + String get tutorialModalWelcome => 'Bienvenue!'; + @override + String get tutorialModalMessage => + 'Souhaitez-vous voir un tutoriel rapide pour apprendre à utiliser cette application?'; + @override + String get tutorialModalKeepTutorials => 'Conserver les tutoriels'; + @override + String get tutorialModalSkipAllTutorials => 'Ignorer tous les tutoriels'; } diff --git a/lib/app/utils/language/hindi_sentences.dart b/lib/app/utils/language/hindi_sentences.dart index 99e78cb7..87402ab4 100644 --- a/lib/app/utils/language/hindi_sentences.dart +++ b/lib/app/utils/language/hindi_sentences.dart @@ -642,4 +642,14 @@ class HindiSentences extends Sentences { String get logs => 'लॉग्स'; @override String get checkAllDebugLogsHere => 'यहाँ सभी डिबग लॉग्स देखें'; + + @override + String get tutorialModalWelcome => 'स्वागत है!'; + @override + String get tutorialModalMessage => + 'क्या आप इस ऐप का उपयोग करना सीखने के लिए एक त्वरित ट्यूटोरियल देखना चाहेंगे?'; + @override + String get tutorialModalKeepTutorials => 'ट्यूटोरियल रखें'; + @override + String get tutorialModalSkipAllTutorials => 'सभी ट्यूटोरियल छोड़ें'; } diff --git a/lib/app/utils/language/marathi_sentences.dart b/lib/app/utils/language/marathi_sentences.dart index d7758f12..18bf4e71 100644 --- a/lib/app/utils/language/marathi_sentences.dart +++ b/lib/app/utils/language/marathi_sentences.dart @@ -664,4 +664,14 @@ class MarathiSentences extends Sentences { String get logs => 'लॉग्ज'; @override String get checkAllDebugLogsHere => 'येथे सर्व डीबग लॉग्ज तपासा'; + + @override + String get tutorialModalWelcome => 'स्वागत आहे!'; + @override + String get tutorialModalMessage => + 'तुम्हाला हे अॅप वापरण्याचे शिकण्यासाठी एक जलद ट्यूटोरियल पहायचा आहे का?'; + @override + String get tutorialModalKeepTutorials => 'ट्यूटोरियल ठेवा'; + @override + String get tutorialModalSkipAllTutorials => 'सर्व ट्यूटोरियल सोडा'; } diff --git a/lib/app/utils/language/sentences.dart b/lib/app/utils/language/sentences.dart index 5fbd2e85..5a903f42 100644 --- a/lib/app/utils/language/sentences.dart +++ b/lib/app/utils/language/sentences.dart @@ -345,4 +345,10 @@ abstract class Sentences { String get profilePageChangeProfileMode; String get profilePageSelectProfileMode; String get profilePageSuccessfullyChangedProfileModeTo; + + // Tutorial modal strings + String get tutorialModalWelcome; + String get tutorialModalMessage; + String get tutorialModalKeepTutorials; + String get tutorialModalSkipAllTutorials; } diff --git a/lib/app/utils/language/spanish_sentences.dart b/lib/app/utils/language/spanish_sentences.dart index a85e76b7..9ec768ae 100644 --- a/lib/app/utils/language/spanish_sentences.dart +++ b/lib/app/utils/language/spanish_sentences.dart @@ -669,4 +669,14 @@ class SpanishSentences extends Sentences { @override String get checkAllDebugLogsHere => 'Consulta todos los registros de depuración aquí'; + + @override + String get tutorialModalWelcome => '¡Bienvenido!'; + @override + String get tutorialModalMessage => + '¿Te gustaría ver un tutorial rápido para aprender a usar esta aplicación?'; + @override + String get tutorialModalKeepTutorials => 'Mantener tutoriales'; + @override + String get tutorialModalSkipAllTutorials => 'Omitir todos los tutoriales'; }