Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion lib/app/modules/home/views/home_page_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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.disableAllTutorials();
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<TaskwarriorColorTheme>()!;
return DoubleBackToCloseApp(
Expand Down
124 changes: 124 additions & 0 deletions lib/app/modules/home/views/tutorial_modal.dart
Original file line number Diff line number Diff line change
@@ -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<TaskwarriorColorTheme>()!;

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,
),
),
),
),
],
),
],
),
),
);
}
}
18 changes: 18 additions & 0 deletions lib/app/utils/app_settings/save_tour_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,22 @@ class SaveTourStatus {
static Future<bool> 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<bool> 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);
}
}