Skip to content

Commit 94980db

Browse files
authored
Merge pull request #432 from rohansen856/permission_screen
feat: added permission request screen
2 parents 1933da4 + 5434792 commit 94980db

File tree

9 files changed

+285
-6
lines changed

9 files changed

+285
-6
lines changed

lib/app/modules/onboarding/views/onboarding_page_start_button.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class OnboardingPageStartButton extends StatelessWidget {
1616
child: ElevatedButton(
1717
onPressed: () {
1818
controller.markOnboardingAsCompleted();
19-
Get.offNamed(Routes.HOME);
19+
Get.offNamed(Routes.PERMISSION);
2020
},
2121
style: ElevatedButton.styleFrom(
2222
backgroundColor: TaskWarriorColors.black,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:get/get.dart';
2+
3+
import '../controllers/permission_controller.dart';
4+
5+
class PermissionBinding extends Bindings {
6+
@override
7+
void dependencies() {
8+
Get.lazyPut<PermissionController>(
9+
() => PermissionController(),
10+
);
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get/get.dart';
3+
import 'package:permission_handler/permission_handler.dart';
4+
import 'package:taskwarrior/app/utils/permissions/permissions_manager.dart';
5+
6+
class PermissionController extends GetxController {
7+
final RxBool isStorageGranted = false.obs;
8+
final RxBool isNotificationGranted = false.obs;
9+
final RxBool isExteternalStorageGranted = false.obs;
10+
final RxBool isLoading = false.obs;
11+
12+
@override
13+
void onInit() {
14+
super.onInit();
15+
checkPermissions();
16+
}
17+
18+
Future<void> checkPermissions() async {
19+
try {
20+
isStorageGranted.value = await Permission.storage.status.isGranted;
21+
isNotificationGranted.value =
22+
await Permission.notification.status.isGranted;
23+
isExteternalStorageGranted.value =
24+
await Permission.manageExternalStorage.status.isGranted;
25+
} catch (e) {
26+
debugPrint('Error checking permissions: $e');
27+
}
28+
}
29+
30+
Future<void> requestPermissions() async {
31+
try {
32+
isLoading.value = true;
33+
34+
if (!isStorageGranted.value &&
35+
!isNotificationGranted.value &&
36+
!isExteternalStorageGranted.value) {
37+
await PermissionsManager.requestAllPermissions();
38+
}
39+
Get.offNamed('/home');
40+
} catch (e) {
41+
debugPrint('Error requesting permissions: $e');
42+
} finally {
43+
isLoading.value = false;
44+
}
45+
}
46+
47+
void gotoHome() async {
48+
try {
49+
await Get.offNamed('/home');
50+
} catch (e) {
51+
debugPrint('Error opening home screen: $e');
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart';
3+
4+
class PermissionSection extends StatelessWidget {
5+
final IconData icon;
6+
final String title;
7+
final String description;
8+
final bool isDarkMode;
9+
10+
const PermissionSection({
11+
super.key,
12+
required this.icon,
13+
required this.title,
14+
required this.description,
15+
required this.isDarkMode,
16+
});
17+
18+
@override
19+
Widget build(BuildContext context) {
20+
return Container(
21+
padding: const EdgeInsets.all(16),
22+
decoration: BoxDecoration(
23+
border: Border.all(
24+
color: isDarkMode
25+
? TaskWarriorColors.ksecondaryBackgroundColor
26+
: TaskWarriorColors.borderColor,
27+
),
28+
borderRadius: BorderRadius.circular(12),
29+
color: isDarkMode
30+
? TaskWarriorColors.kdialogBackGroundColor
31+
: TaskWarriorColors.kLightDialogBackGroundColor,
32+
),
33+
child: Column(
34+
crossAxisAlignment: CrossAxisAlignment.start,
35+
children: [
36+
Row(
37+
children: [
38+
Icon(icon, color: TaskWarriorColors.black),
39+
const SizedBox(width: 12),
40+
Expanded(
41+
child: Text(
42+
title,
43+
style: Theme.of(context).textTheme.titleMedium?.copyWith(
44+
fontWeight: FontWeight.bold,
45+
color: isDarkMode
46+
? TaskWarriorColors.kprimaryTextColor
47+
: TaskWarriorColors.kLightPrimaryTextColor,
48+
),
49+
),
50+
),
51+
],
52+
),
53+
const SizedBox(height: 8),
54+
Text(
55+
description,
56+
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
57+
color: isDarkMode
58+
? TaskWarriorColors.ksecondaryTextColor
59+
: TaskWarriorColors.kLightSecondaryTextColor,
60+
),
61+
),
62+
],
63+
),
64+
);
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:get/get.dart';
5+
import 'package:taskwarrior/app/modules/permission/views/permission_section.dart';
6+
import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
7+
import 'package:taskwarrior/app/utils/constants/taskwarrior_colors.dart';
8+
import '../controllers/permission_controller.dart';
9+
10+
class PermissionView extends GetView<PermissionController> {
11+
const PermissionView({super.key});
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
16+
17+
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
18+
WidgetsBinding.instance.addPostFrameCallback((_) {
19+
Get.offAllNamed('/home');
20+
});
21+
return const SizedBox.shrink();
22+
}
23+
24+
return Scaffold(
25+
backgroundColor: isDarkMode
26+
? TaskWarriorColors.kprimaryBackgroundColor
27+
: TaskWarriorColors.kLightPrimaryBackgroundColor,
28+
body: SafeArea(
29+
child: SingleChildScrollView(
30+
child: Padding(
31+
padding: const EdgeInsets.all(24.0),
32+
child: Column(
33+
crossAxisAlignment: CrossAxisAlignment.stretch,
34+
children: [
35+
const SizedBox(height: 24),
36+
Text(
37+
'Why We Need Your Permission',
38+
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
39+
fontWeight: FontWeight.bold,
40+
color: isDarkMode
41+
? TaskWarriorColors.kprimaryTextColor
42+
: TaskWarriorColors.kLightPrimaryTextColor,
43+
),
44+
textAlign: TextAlign.center,
45+
),
46+
const SizedBox(height: 32),
47+
Icon(
48+
Icons.security,
49+
size: 64,
50+
color: AppSettings.isDarkMode
51+
? TaskWarriorColors.black
52+
: TaskWarriorColors.white,
53+
),
54+
const SizedBox(height: 32),
55+
PermissionSection(
56+
icon: Icons.folder_outlined,
57+
title: 'Storage Permission',
58+
description:
59+
'We use storage access to save your tasks, preferences, '
60+
'and app data securely on your device. This ensures that you can '
61+
'pick up where you left off seamlessly, even offline.',
62+
isDarkMode: isDarkMode,
63+
),
64+
const SizedBox(height: 24),
65+
PermissionSection(
66+
icon: Icons.notifications_outlined,
67+
title: 'Notification Permission',
68+
description:
69+
'Notifications keep you updated with important reminders '
70+
'and updates, ensuring you stay on top of your tasks effortlessly.',
71+
isDarkMode: isDarkMode,
72+
),
73+
const SizedBox(height: 24),
74+
Text(
75+
'Your privacy is our top priority. We never access or share your '
76+
'personal files or data without your consent.',
77+
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
78+
color: AppSettings.isDarkMode
79+
? TaskWarriorColors.black
80+
: TaskWarriorColors.white,
81+
),
82+
textAlign: TextAlign.center,
83+
),
84+
const SizedBox(height: 48),
85+
Obx(() => ElevatedButton(
86+
onPressed: controller.isLoading.value
87+
? null
88+
: controller.requestPermissions,
89+
style: ElevatedButton.styleFrom(
90+
padding: const EdgeInsets.all(16),
91+
backgroundColor: AppSettings.isDarkMode
92+
? TaskWarriorColors.black
93+
: TaskWarriorColors.white,
94+
foregroundColor: AppSettings.isDarkMode
95+
? TaskWarriorColors.kprimaryTextColor
96+
: TaskWarriorColors.kLightPrimaryTextColor,
97+
shape: RoundedRectangleBorder(
98+
borderRadius: BorderRadius.circular(12),
99+
),
100+
),
101+
child: controller.isLoading.value
102+
? CircularProgressIndicator(
103+
color: AppSettings.isDarkMode
104+
? TaskWarriorColors.black
105+
: TaskWarriorColors.white,
106+
)
107+
: Text(
108+
'Grant Permissions',
109+
style: TextStyle(
110+
color: AppSettings.isDarkMode
111+
? TaskWarriorColors.kprimaryTextColor
112+
: TaskWarriorColors.kLightPrimaryTextColor,
113+
fontSize: 16,
114+
),
115+
),
116+
)),
117+
const SizedBox(height: 16),
118+
TextButton(
119+
onPressed: () => controller.gotoHome(),
120+
style: ButtonStyle(
121+
backgroundColor:
122+
WidgetStateProperty.all(TaskWarriorColors.grey),
123+
),
124+
child: Text(
125+
'You can manage your permissions anytime later in Settings',
126+
style: Theme.of(context).textTheme.bodySmall?.copyWith(
127+
color: AppSettings.isDarkMode
128+
? TaskWarriorColors.black
129+
: TaskWarriorColors.white,
130+
),
131+
textAlign: TextAlign.center,
132+
),
133+
),
134+
const SizedBox(height: 24),
135+
],
136+
),
137+
),
138+
),
139+
),
140+
);
141+
}
142+
}

lib/app/routes/app_pages.dart

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import '../modules/manageTaskServer/bindings/manage_task_server_binding.dart';
1313
import '../modules/manageTaskServer/views/manage_task_server_view.dart';
1414
import '../modules/onboarding/bindings/onboarding_binding.dart';
1515
import '../modules/onboarding/views/onboarding_view.dart';
16+
import '../modules/permission/bindings/permission_binding.dart';
17+
import '../modules/permission/views/permission_view.dart';
1618
import '../modules/profile/bindings/profile_binding.dart';
1719
import '../modules/profile/views/profile_view.dart';
1820
import '../modules/reports/bindings/reports_binding.dart';
@@ -75,5 +77,10 @@ class AppPages {
7577
page: () => const SettingsView(),
7678
binding: SettingsBinding(),
7779
),
80+
GetPage(
81+
name: _Paths.PERMISSION,
82+
page: () => const PermissionView(),
83+
binding: PermissionBinding(),
84+
),
7885
];
7986
}

lib/app/routes/app_routes.dart

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ abstract class Routes {
1414
static const ABOUT = _Paths.ABOUT;
1515
static const REPORTS = _Paths.REPORTS;
1616
static const SETTINGS = _Paths.SETTINGS;
17+
static const PERMISSION = _Paths.PERMISSION;
1718
}
1819

1920
abstract class _Paths {
@@ -27,4 +28,5 @@ abstract class _Paths {
2728
static const ABOUT = '/about';
2829
static const REPORTS = '/reports';
2930
static const SETTINGS = '/settings';
31+
static const PERMISSION = '/permission';
3032
}

lib/main.dart

-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import 'package:flutter/material.dart';
22
import 'package:get/get.dart';
33
import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
4-
import 'package:taskwarrior/app/utils/permissions/permissions_manager.dart';
54
import 'app/routes/app_pages.dart';
65

76
void main() async {
87
WidgetsFlutterBinding.ensureInitialized();
98
await AppSettings.init();
109

11-
await PermissionsManager.requestAllPermissions();
12-
13-
1410
runApp(
1511
GetMaterialApp(
1612
title: "Application",

test/routes/app_pages_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ void main() {
3030
test('All routes should be defined correctly', () {
3131
final routes = AppPages.routes;
3232

33-
expect(routes.length, 9);
33+
expect(routes.length, 10);
3434

3535
expect(
3636
routes.any((route) =>

0 commit comments

Comments
 (0)