feat: Add Settings Screen and related functionality

- Implemented SettingsScreen with tabs for Account, Social, and Devices.
- Added various settings pages including Email, Notifications, Privacy, Security, Display Mode, Language, Text Size, and Terms of Service.
- Integrated navigation to new settings pages from the main settings screen.
- Updated navigation menu to include the new SettingsScreen.
- Refactored authentication flow to navigate to the main navigation menu after successful login.
- Enhanced UI with theme consistency across new settings pages.
This commit is contained in:
vergiLgood1 2025-05-27 06:20:45 +07:00
parent 1967fdb6b8
commit 61fb40bc0f
15 changed files with 1325 additions and 20 deletions

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sigap/src/features/panic/presentation/pages/panic_button_page.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/setting_screen.dart';
import 'package:sigap/src/shared/widgets/navigation/custom_bottom_navigation_bar.dart';
class NavigationMenu extends StatelessWidget {
@ -10,8 +11,10 @@ class NavigationMenu extends StatelessWidget {
Widget build(BuildContext context) {
// Using GetX controller to manage navigation state
final controller = Get.put(NavigationController());
final theme = Theme.of(context);
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
body: Obx(
() => IndexedStack(
index: controller.selectedIndex.value,
@ -20,7 +23,7 @@ class NavigationMenu extends StatelessWidget {
// SearchPage(),
PanicButtonPage(),
// HistoryPage(),
// AccountPage(),
SettingsScreen(),
],
),
),

View File

@ -96,8 +96,6 @@ class AuthenticationRepository extends GetxController {
}
}
Logger().d('Available session: $session');
if (session != null) {
if (!isEmailVerified) {
_navigateToRoute(AppRoutes.emailVerification);
@ -109,7 +107,7 @@ class AuthenticationRepository extends GetxController {
}
_navigateToRoute(AppRoutes.registrationForm);
} else {
_navigateToRoute(AppRoutes.panicButton);
_navigateToRoute(AppRoutes.navigationMenu);
}
} else {
await _handleUnauthenticatedUser(isFirstTime);
@ -135,8 +133,7 @@ class AuthenticationRepository extends GetxController {
// Pisahkan logic untuk user yang belum login
Future<void> _handleUnauthenticatedUser(bool isFirstTime) async {
if (Get.currentRoute != AppRoutes.signIn &&
Get.currentRoute != AppRoutes.onboarding) {
if (Get.currentRoute != AppRoutes.onboarding) {
bool biometricSuccess = await attemptBiometricLogin();
if (!biometricSuccess) {
if (isFirstTime) {
@ -210,9 +207,12 @@ class AuthenticationRepository extends GetxController {
// Check for errors
if (user == null) {
Logger().e('User is null after sign in');
throw 'User is null after sign in';
}
Logger().i('User signed in: ${user.email}');
// Return user
return user;
} on AuthException catch (e) {

View File

@ -0,0 +1,284 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/contact_screen.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/display_mode_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/email_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/language_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/notifications_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/privacy_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/security_setting.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/terms_services_screen.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/text_size_setting.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
_SettingsScreenState createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: Text('Settings', style: theme.textTheme.headlineMedium),
backgroundColor: theme.scaffoldBackgroundColor,
elevation: 0,
),
body: Column(
children: [
// Profile Section
Container(
padding: const EdgeInsets.all(20),
color: theme.scaffoldBackgroundColor,
child: Row(
children: [
CircleAvatar(
radius: 30,
backgroundColor: theme.hintColor,
child: Icon(
Icons.person,
size: 35,
color: theme.scaffoldBackgroundColor,
),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Anita Rose', style: theme.textTheme.titleLarge),
Text('anitarose', style: theme.textTheme.bodySmall),
],
),
),
Icon(Icons.chevron_right, color: theme.hintColor),
],
),
),
// Tab Bar
Container(
color: theme.scaffoldBackgroundColor,
child: TabBar(
controller: _tabController,
labelColor: theme.primaryColor,
unselectedLabelColor: theme.hintColor,
indicatorColor: theme.primaryColor,
indicatorWeight: 2,
labelStyle: theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
),
tabs: const [
Tab(text: 'ACCOUNT'),
Tab(text: 'SOCIAL'),
Tab(text: 'DEVICES'),
],
),
),
// Tab Content
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildAccountTab(),
_buildSocialTab(),
_buildDevicesTab(),
],
),
),
],
),
);
}
Widget _buildAccountTab() {
final theme = Theme.of(context);
return Container(
color: theme.scaffoldBackgroundColor,
child: ListView(
children: [
_buildSettingsItem(
icon: Icons.email_outlined,
title: 'Email',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EmailSettingsScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.notifications_outlined,
title: 'Notifications',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NotificationsSettingsScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.lock_outline,
title: 'Privacy',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PrivacySettingsScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.security_outlined,
title: 'Security',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecuritySettingsScreen(),
),
);
},
),
const SizedBox(height: 20),
_buildSettingsItem(
icon: Icons.dark_mode_outlined,
title: 'Display mode',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DisplayModeSettingsScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.text_fields_outlined,
title: 'Text size',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TextSizeSettingsScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.language_outlined,
title: 'Language',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LanguageSettingsScreen(),
),
);
},
),
const SizedBox(height: 20),
_buildSettingsItem(
icon: Icons.description_outlined,
title: 'Terms and services',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TermsServicesScreen(),
),
);
},
),
_buildSettingsItem(
icon: Icons.help_outline,
title: 'Contact',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ContactScreen()),
);
},
),
],
),
);
}
Widget _buildSocialTab() {
final theme = Theme.of(context);
return Container(
color: theme.scaffoldBackgroundColor,
child: Center(
child: Text(
'Social Settings',
style: theme.textTheme.titleLarge?.copyWith(color: theme.hintColor),
),
),
);
}
Widget _buildDevicesTab() {
final theme = Theme.of(context);
return Container(
color: theme.scaffoldBackgroundColor,
child: Center(
child: Text(
'Device Settings',
style: theme.textTheme.titleLarge?.copyWith(color: theme.hintColor),
),
),
);
}
Widget _buildSettingsItem({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Icon(Icons.chevron_right, color: theme.hintColor),
],
),
),
);
}
}

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class BaseDetailScreen extends StatelessWidget {
final String title;
final Widget content;
const BaseDetailScreen({
super.key,
required this.title,
required this.content,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: theme.iconTheme.color),
onPressed: () => Navigator.pop(context),
),
title: Text(title, style: theme.textTheme.titleLarge),
backgroundColor: theme.scaffoldBackgroundColor,
elevation: 0,
),
body: Container(color: theme.scaffoldBackgroundColor, child: content),
);
}
}

View File

@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class ContactScreen extends StatelessWidget {
const ContactScreen({super.key});
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Contact',
content: ListView(
padding: const EdgeInsets.all(20),
children: [
_buildContactItem(
context,
icon: Icons.email_outlined,
title: 'Email',
value: 'support@sigap.com',
),
_buildContactItem(
context,
icon: Icons.phone_outlined,
title: 'Phone',
value: '+62 21 1234 5678',
),
_buildContactItem(
context,
icon: Icons.location_on_outlined,
title: 'Address',
value: 'Jl. Sudirman No. 123, Jakarta 12190, Indonesia',
),
const SizedBox(height: 40),
_buildSupportSection(context),
],
),
);
}
Widget _buildContactItem(
BuildContext context, {
required IconData icon,
required String title,
required String value,
}) {
final theme = Theme.of(context);
return Container(
margin: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
border: Border.all(color: theme.dividerColor),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: theme.primaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 24, color: theme.primaryColor),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.hintColor,
),
),
const SizedBox(height: 4),
Text(value, style: theme.textTheme.bodyLarge),
],
),
),
],
),
);
}
Widget _buildSupportSection(BuildContext context) {
final theme = Theme.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Need Help?', style: theme.textTheme.titleLarge),
const SizedBox(height: 15),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: theme.cardColor,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'Our support team is available Monday through Friday, 9:00 AM to 5:00 PM (GMT+7).',
style: theme.textTheme.bodyMedium?.copyWith(height: 1.5),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
style: theme.elevatedButtonTheme.style,
child: const Text('Contact Support'),
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class DisplayModeSettingsScreen extends StatefulWidget {
const DisplayModeSettingsScreen({super.key});
@override
_DisplayModeSettingsScreenState createState() =>
_DisplayModeSettingsScreenState();
}
class _DisplayModeSettingsScreenState extends State<DisplayModeSettingsScreen> {
String selectedMode = 'System default';
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Display Mode',
content: ListView(
children: [
_buildSectionHeader('Theme'),
_buildRadioItem('System default'),
_buildRadioItem('Light mode'),
_buildRadioItem('Dark mode'),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildRadioItem(String mode) {
final theme = Theme.of(context);
IconData icon;
switch (mode) {
case 'System default':
icon = Icons.brightness_auto;
break;
case 'Light mode':
icon = Icons.light_mode_outlined;
break;
case 'Dark mode':
icon = Icons.dark_mode_outlined;
break;
default:
icon = Icons.brightness_auto;
}
return InkWell(
onTap: () {
setState(() {
selectedMode = mode;
});
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(mode, style: theme.textTheme.bodyLarge)),
Radio<String>(
value: mode,
groupValue: selectedMode,
activeColor: theme.primaryColor,
onChanged: (value) {
setState(() {
selectedMode = value!;
});
},
),
],
),
),
);
}
}

View File

@ -0,0 +1,124 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class EmailSettingsScreen extends StatefulWidget {
const EmailSettingsScreen({super.key});
@override
_EmailSettingsScreenState createState() => _EmailSettingsScreenState();
}
class _EmailSettingsScreenState extends State<EmailSettingsScreen> {
String currentEmail = "anitarose@example.com";
bool receiveNewsletter = true;
bool receivePromotions = false;
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Email Settings',
content: ListView(
children: [
_buildSectionHeader('Email Address'),
_buildEmailItem(),
_buildSectionHeader('Email Preferences'),
_buildSwitchItem(
icon: Icons.newspaper_outlined,
title: 'Receive newsletter',
value: receiveNewsletter,
onChanged: (value) {
setState(() {
receiveNewsletter = value;
});
},
),
_buildSwitchItem(
icon: Icons.local_offer_outlined,
title: 'Receive promotions',
value: receivePromotions,
onChanged: (value) {
setState(() {
receivePromotions = value;
});
},
),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildEmailItem() {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(Icons.email_outlined, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Primary Email', style: theme.textTheme.bodyLarge),
const SizedBox(height: 4),
Text(
currentEmail,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.hintColor,
),
),
],
),
),
TextButton(
onPressed: () {},
style: TextButton.styleFrom(foregroundColor: theme.primaryColor),
child: const Text('Change'),
),
],
),
);
}
Widget _buildSwitchItem({
required IconData icon,
required String title,
required bool value,
required ValueChanged<bool> onChanged,
}) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Switch(
value: value,
onChanged: onChanged,
activeColor: theme.primaryColor,
activeTrackColor: theme.primaryColor.withOpacity(0.4),
),
],
),
);
}
}

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class LanguageSettingsScreen extends StatefulWidget {
const LanguageSettingsScreen({super.key});
@override
_LanguageSettingsScreenState createState() => _LanguageSettingsScreenState();
}
class _LanguageSettingsScreenState extends State<LanguageSettingsScreen> {
String selectedLanguage = 'English (US)';
final List<Map<String, String>> languages = [
{'name': 'English (US)', 'code': 'en_US'},
{'name': 'Bahasa Indonesia', 'code': 'id_ID'},
{'name': 'Español', 'code': 'es_ES'},
{'name': 'Français', 'code': 'fr_FR'},
{'name': '日本語', 'code': 'ja_JP'},
{'name': '한국어', 'code': 'ko_KR'},
];
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Language',
content: ListView(
children: [
_buildSectionHeader('Select Language'),
...languages.map((lang) => _buildLanguageItem(lang['name']!)),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildLanguageItem(String language) {
final theme = Theme.of(context);
return InkWell(
onTap: () {
setState(() {
selectedLanguage = language;
});
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: theme.dividerColor.withOpacity(0.5)),
),
),
child: Row(
children: [
Expanded(child: Text(language, style: theme.textTheme.bodyLarge)),
if (language == selectedLanguage)
Icon(Icons.check, color: theme.primaryColor),
],
),
),
);
}
}

View File

@ -0,0 +1,121 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class NotificationsSettingsScreen extends StatefulWidget {
const NotificationsSettingsScreen({super.key});
@override
_NotificationsSettingsScreenState createState() =>
_NotificationsSettingsScreenState();
}
class _NotificationsSettingsScreenState
extends State<NotificationsSettingsScreen> {
bool messagesNotify = true;
bool commentsNotify = true;
bool followersNotify = true;
bool mentionsNotify = false;
bool systemNotify = true;
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Notifications',
content: ListView(
children: [
_buildSectionHeader('Push Notifications'),
_buildSwitchItem(
icon: Icons.message_outlined,
title: 'Messages',
value: messagesNotify,
onChanged: (value) {
setState(() {
messagesNotify = value;
});
},
),
_buildSwitchItem(
icon: Icons.comment_outlined,
title: 'Comments',
value: commentsNotify,
onChanged: (value) {
setState(() {
commentsNotify = value;
});
},
),
_buildSwitchItem(
icon: Icons.person_add_outlined,
title: 'New followers',
value: followersNotify,
onChanged: (value) {
setState(() {
followersNotify = value;
});
},
),
_buildSwitchItem(
icon: Icons.alternate_email,
title: 'Mentions',
value: mentionsNotify,
onChanged: (value) {
setState(() {
mentionsNotify = value;
});
},
),
_buildSwitchItem(
icon: Icons.info_outline,
title: 'System notifications',
value: systemNotify,
onChanged: (value) {
setState(() {
systemNotify = value;
});
},
),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildSwitchItem({
required IconData icon,
required String title,
required bool value,
required ValueChanged<bool> onChanged,
}) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Switch(
value: value,
onChanged: onChanged,
activeColor: theme.primaryColor,
activeTrackColor: theme.primaryColor.withOpacity(0.4),
),
],
),
);
}
}

View File

@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class PrivacySettingsScreen extends StatefulWidget {
const PrivacySettingsScreen({super.key});
@override
_PrivacySettingsScreenState createState() => _PrivacySettingsScreenState();
}
class _PrivacySettingsScreenState extends State<PrivacySettingsScreen> {
bool showActivityStatus = true;
bool allowComments = false;
bool shareUsageData = true;
bool allowPersonalizedAds = false;
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Privacy Settings',
content: ListView(
children: [
_buildSectionHeader('Profile Visibility'),
_buildNavigationItem(
icon: Icons.visibility_outlined,
title: 'Who can see my profile?',
onTap: () {},
),
_buildSwitchItem(
icon: Icons.access_time_outlined,
title: 'Show activity status',
value: showActivityStatus,
onChanged: (value) {
setState(() {
showActivityStatus = value;
});
},
),
_buildSectionHeader('Interactions'),
_buildNavigationItem(
icon: Icons.chat_bubble_outline,
title: 'Who can message me?',
onTap: () {},
),
_buildSwitchItem(
icon: Icons.comment_outlined,
title: 'Allow comments',
value: allowComments,
onChanged: (value) {
setState(() {
allowComments = value;
});
},
),
_buildSectionHeader('Data Sharing'),
_buildSwitchItem(
icon: Icons.analytics_outlined,
title: 'Share usage data',
value: shareUsageData,
onChanged: (value) {
setState(() {
shareUsageData = value;
});
},
),
_buildSwitchItem(
icon: Icons.ads_click_outlined,
title: 'Allow personalized ads',
value: allowPersonalizedAds,
onChanged: (value) {
setState(() {
allowPersonalizedAds = value;
});
},
),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildNavigationItem({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Icon(Icons.chevron_right, color: theme.hintColor),
],
),
),
);
}
Widget _buildSwitchItem({
required IconData icon,
required String title,
required bool value,
required ValueChanged<bool> onChanged,
}) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Switch(
value: value,
onChanged: onChanged,
activeColor: theme.primaryColor,
activeTrackColor: theme.primaryColor.withOpacity(0.4),
),
],
),
);
}
}

View File

@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class SecuritySettingsScreen extends StatefulWidget {
const SecuritySettingsScreen({super.key});
@override
_SecuritySettingsScreenState createState() => _SecuritySettingsScreenState();
}
class _SecuritySettingsScreenState extends State<SecuritySettingsScreen> {
bool requireBiometric = true;
bool enable2FA = false;
bool sendEmailAlerts = true;
bool sendPushNotification = false;
@override
Widget build(BuildContext context) {
return BaseDetailScreen(
title: 'Security Settings',
content: ListView(
children: [
_buildSectionHeader('App Lock'),
_buildSwitchItem(
icon: Icons.fingerprint_outlined,
title: 'Require biometric',
value: requireBiometric,
onChanged: (value) {
setState(() {
requireBiometric = value;
});
},
),
_buildNavigationItem(
icon: Icons.lock_outline,
title: 'Change passcode',
onTap: () {},
),
_buildSectionHeader('Two-Factor Authentication'),
_buildSwitchItem(
icon: Icons.security_outlined,
title: 'Enable 2FA',
value: enable2FA,
onChanged: (value) {
setState(() {
enable2FA = value;
});
},
),
_buildNavigationItem(
icon: Icons.backup_outlined,
title: 'Manage backup codes',
onTap: () {},
),
_buildSectionHeader('Login Alerts'),
_buildSwitchItem(
icon: Icons.email_outlined,
title: 'Send email alerts',
value: sendEmailAlerts,
onChanged: (value) {
setState(() {
sendEmailAlerts = value;
});
},
),
_buildSwitchItem(
icon: Icons.notifications_outlined,
title: 'Send push notification',
value: sendPushNotification,
onChanged: (value) {
setState(() {
sendPushNotification = value;
});
},
),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
Widget _buildNavigationItem({
required IconData icon,
required String title,
required VoidCallback onTap,
}) {
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Icon(Icons.chevron_right, color: theme.hintColor),
],
),
),
);
}
Widget _buildSwitchItem({
required IconData icon,
required String title,
required bool value,
required ValueChanged<bool> onChanged,
}) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Icon(icon, size: 24, color: theme.iconTheme.color),
const SizedBox(width: 15),
Expanded(child: Text(title, style: theme.textTheme.bodyLarge)),
Switch(
value: value,
onChanged: onChanged,
activeColor: theme.primaryColor,
activeTrackColor: theme.primaryColor.withOpacity(0.4),
),
],
),
);
}
}

View File

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class TermsServicesScreen extends StatelessWidget {
const TermsServicesScreen({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return BaseDetailScreen(
title: 'Terms and Services',
content: ListView(
padding: const EdgeInsets.all(20),
children: [
Text('Terms of Service', style: theme.textTheme.headlineSmall),
const SizedBox(height: 15),
Text(
'Last updated: January 1, 2023',
style: theme.textTheme.bodyMedium?.copyWith(color: theme.hintColor),
),
const SizedBox(height: 25),
_buildSectionText(
context,
'Introduction',
'Welcome to SIGAP. These Terms of Service govern your use of our application and website. By using our services, you agree to these terms.',
),
_buildSectionText(
context,
'Using Our Services',
'You must follow any policies made available to you within the Services. You may use our Services only as permitted by law. We may suspend or stop providing our Services to you if you do not comply with our terms or policies or if we are investigating suspected misconduct.',
),
_buildSectionText(
context,
'Privacy Policy',
'Our privacy policy explains how we treat your personal data and protect your privacy when you use our Services. By using our Services, you agree that we can use such data in accordance with our privacy policy.',
),
_buildSectionText(
context,
'About These Terms',
'We may modify these terms or any additional terms that apply to a Service to, for example, reflect changes to the law or changes to our Services.',
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () {},
style: theme.elevatedButtonTheme.style,
child: const Text('Download Full Terms'),
),
],
),
);
}
Widget _buildSectionText(BuildContext context, String title, String content) {
final theme = Theme.of(context);
return Container(
margin: const EdgeInsets.only(bottom: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
content,
style: theme.textTheme.bodyMedium?.copyWith(height: 1.5),
),
],
),
);
}
}

View File

@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:sigap/src/features/personalization/presentasion/pages/settings/widgets/base_detail_screen.dart';
class TextSizeSettingsScreen extends StatefulWidget {
const TextSizeSettingsScreen({super.key});
@override
_TextSizeSettingsScreenState createState() => _TextSizeSettingsScreenState();
}
class _TextSizeSettingsScreenState extends State<TextSizeSettingsScreen> {
double _currentTextSize = 1.0; // 1.0 is default
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return BaseDetailScreen(
title: 'Text Size',
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionHeader('Adjust Text Size'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
'Sample text at current size',
style: theme.textTheme.bodyLarge?.copyWith(
fontSize: 16 * _currentTextSize,
),
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Row(
children: [
Text('A', style: theme.textTheme.bodyMedium),
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: theme.primaryColor,
thumbColor: theme.primaryColor,
inactiveTrackColor: theme.primaryColor.withOpacity(0.2),
),
child: Slider(
value: _currentTextSize,
min: 0.8,
max: 1.4,
divisions: 6,
onChanged: (value) {
setState(() {
_currentTextSize = value;
});
},
),
),
),
Text('A', style: theme.textTheme.titleLarge),
],
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
'This is a preview of how text will appear throughout the app. '
'Adjust the slider to make text smaller or larger.',
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 14 * _currentTextSize,
),
),
),
],
),
);
}
Widget _buildSectionHeader(String title) {
final theme = Theme.of(context);
return Container(
padding: EdgeInsets.fromLTRB(20, 30, 20, 10),
child: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_tabler_icons/flutter_tabler_icons.dart';
import 'package:get/get.dart';
import 'package:sigap/navigation_menu.dart';
@ -8,15 +9,15 @@ class CustomBottomNavigationBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = NavigationController.instance;
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final theme = Theme.of(context);
return Container(
decoration: BoxDecoration(
color: Colors.white,
color: theme.scaffoldBackgroundColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: theme.shadowColor.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, -5),
),
@ -74,6 +75,8 @@ class CustomBottomNavigationBar extends StatelessWidget {
int index,
bool isSelected,
) {
final theme = Theme.of(context);
return GestureDetector(
onTap: () => NavigationController.instance.changeIndex(index),
behavior: HitTestBehavior.opaque,
@ -82,14 +85,14 @@ class CustomBottomNavigationBar extends StatelessWidget {
children: [
Icon(
icon,
color: isSelected ? const Color(0xFF5E39F1) : Colors.grey,
color: isSelected ? theme.primaryColor : theme.hintColor,
size: 24,
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
color: isSelected ? const Color(0xFF5E39F1) : Colors.grey,
color: isSelected ? theme.primaryColor : theme.hintColor,
fontSize: 12,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
@ -100,23 +103,22 @@ class CustomBottomNavigationBar extends StatelessWidget {
}
Widget _buildPanicButton(BuildContext context) {
final theme = Theme.of(context);
return GestureDetector(
onTap: () => NavigationController.instance.changeIndex(2),
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: const Color(0xFF5E39F1),
color: theme.primaryColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF5E39F1).withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: const Icon(Icons.qr_code_scanner, color: Colors.white, size: 30),
child: Icon(
TablerIcons.plus,
color: theme.scaffoldBackgroundColor,
size: 30,
),
),
);
}