adding delete url

This commit is contained in:
2026-06-26 13:56:17 +02:00
parent a1a7f3a4d6
commit eb774b72f6
6 changed files with 599 additions and 0 deletions

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

43
ios/Podfile Normal file
View File

@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@@ -34,6 +34,7 @@ import 'package:qpay/screens/users/screens/user_list_screen.dart';
import 'package:qpay/screens/more/more_screen.dart';
import 'package:qpay/screens/uptime/uptime_status_screen.dart';
import 'package:qpay/screens/contact/contact_screen.dart';
import 'package:qpay/screens/delete_account/delete_account_screen.dart';
/// Tracks whether the splash animation is complete so that
/// GoRouter's redirect can show the splash first, then release
@@ -156,6 +157,11 @@ GoRouter buildRouter() {
path: '/splash',
builder: (context, state) => const SplashScreen(),
),
// Publicly accessible account deletion screen (Play Store requirement)
GoRoute(
path: '/delete-account',
builder: (context, state) => const DeleteAccountScreen(),
),
ShellRoute(
builder: (context, state, child) {
final location = GoRouterState.of(context).uri.toString();

View File

@@ -0,0 +1,541 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../models/responsive_policy.dart';
/// A publicly accessible screen that allows users to request account
/// deletion in compliance with Google Play Store Data Deletion
/// requirements.
///
/// This screen:
/// - Explains what data is deleted and retained.
/// - Provides a clear call-to-action for deletion.
/// - Includes a contact fallback for support.
class DeleteAccountScreen extends StatelessWidget {
const DeleteAccountScreen({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return Scaffold(
backgroundColor: isDark
? const Color(0xFF0D0D0D)
: const Color(0xFFF8F9FA),
appBar: AppBar(
leading: context.canPop()
? IconButton(
onPressed: () => context.pop(),
icon: const Icon(Icons.arrow_back_rounded),
color: theme.colorScheme.primary,
)
: const SizedBox.shrink(),
title: Text(
'Request Account Deletion',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
color: isDark ? Colors.white : Colors.black87,
),
),
centerTitle: true,
surfaceTintColor: Colors.transparent,
backgroundColor: isDark
? const Color(0xFF0D0D0D)
: const Color(0xFFF8F9FA),
),
body: Center(
child: LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth > ResponsivePolicy.md
? ResponsivePolicy.md.toDouble()
: constraints.maxWidth;
return SizedBox(
width: maxWidth,
child: ListView(
padding: const EdgeInsets.all(16),
children: [
// Header / intro card
_buildHeaderCard(theme, isDark),
const SizedBox(height: 16),
// What happens to your data
_buildDataDeletionCard(theme, isDark),
const SizedBox(height: 16),
// Action card
_buildActionCard(theme, isDark, context),
const SizedBox(height: 16),
// Contact fallback
_buildContactCard(theme, isDark),
const SizedBox(height: 32),
],
),
);
},
),
),
);
}
Widget _buildHeaderCard(ThemeData theme, bool isDark) {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 28, horizontal: 24),
decoration: _cardDecoration(theme, isDark),
child: Column(
children: [
Container(
width: 72,
height: 72,
decoration: BoxDecoration(
color: Colors.red.shade50,
shape: BoxShape.circle,
border: Border.all(color: Colors.red.shade200, width: 1.5),
),
child: Icon(
Icons.delete_forever_rounded,
size: 36,
color: Colors.red.shade600,
),
),
const SizedBox(height: 20),
Text(
'Delete Your Account',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 8),
Text(
'We\'re sorry to see you go. Please read the information '
'below before proceeding with your deletion request.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: isDark ? Colors.white60 : Colors.grey.shade600,
height: 1.5,
),
),
],
),
);
}
Widget _buildDataDeletionCard(ThemeData theme, bool isDark) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: _cardDecoration(theme, isDark),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.info_outline_rounded,
size: 22,
color: theme.colorScheme.primary,
),
const SizedBox(width: 10),
Text(
'What happens when you delete your account?',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 20),
_buildInfoPoint(
isDark: isDark,
icon: Icons.person_remove_rounded,
title: 'Profile & Personal Data',
description:
'Your profile information, including your name, phone number, '
'email address, and profile picture will be permanently erased.',
),
const SizedBox(height: 14),
_buildInfoPoint(
isDark: isDark,
icon: Icons.history_rounded,
title: 'Transaction History',
description:
'All transaction records, payment history, and receipts '
'associated with your account will be deleted.',
),
const SizedBox(height: 14),
_buildInfoPoint(
isDark: isDark,
icon: Icons.group_remove_rounded,
title: 'Group & Recipient Data',
description:
'Any groups you have created and recipient data you have '
'stored will be removed from our systems.',
),
const SizedBox(height: 14),
_buildInfoPoint(
isDark: isDark,
icon: Icons.schedule_rounded,
title: 'Retention Period',
description:
'Some data may be retained for up to 90 days as required by '
'legal and regulatory obligations. After this period, all '
'remaining data will be permanently deleted.',
),
const SizedBox(height: 14),
_buildInfoPoint(
isDark: isDark,
icon: Icons.undo_rounded,
title: 'Irreversible Action',
description:
'Account deletion is permanent and cannot be undone once '
'processed. Please ensure you have exported any data you '
'wish to keep before submitting your request.',
),
],
),
);
}
Widget _buildInfoPoint({
required bool isDark,
required IconData icon,
required String title,
required String description,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 2),
child: Icon(icon, size: 20, color: Colors.red.shade400),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
color: isDark ? Colors.white54 : Colors.grey.shade600,
height: 1.4,
),
),
],
),
),
],
);
}
Widget _buildActionCard(ThemeData theme, bool isDark, BuildContext context) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: _cardDecoration(theme, isDark),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.send_rounded,
size: 22,
color: theme.colorScheme.primary,
),
const SizedBox(width: 10),
Text(
'How to Request Deletion',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 16),
Text(
'To ensure the security of your account, all deletion '
'requests must be verified. Please use one of the '
'following methods to submit your request.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: isDark ? Colors.white60 : Colors.grey.shade600,
height: 1.5,
),
),
const SizedBox(height: 20),
// Email deletion request
_buildActionButton(
context: context,
isDark: isDark,
icon: Icons.email_rounded,
iconColor: const Color(0xFFEA4335),
title: 'Request via Email',
subtitle: 'Send us a deletion request from your registered email',
onTap: () {
launchUrl(
Uri(
scheme: 'mailto',
path: 'support@velocityafrica.net',
queryParameters: {
'subject': 'Account Deletion Request',
'body':
'Please delete my QPay account. My registered phone '
'number is: [enter your phone number]',
},
),
mode: LaunchMode.externalApplication,
);
},
),
const SizedBox(height: 12),
// Processing note
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
Icons.access_time_rounded,
size: 18,
color: isDark ? Colors.white60 : Colors.grey.shade700,
),
const SizedBox(width: 10),
Expanded(
child: Text(
'We process deletion requests within 7 business days. '
'You will receive a confirmation email once your '
'account has been deleted.',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white54 : Colors.grey.shade600,
height: 1.4,
),
),
),
],
),
),
],
),
);
}
Widget _buildActionButton({
required BuildContext context,
required bool isDark,
required IconData icon,
required Color iconColor,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: isDark
? Colors.white.withValues(alpha: 0.03)
: Colors.grey.shade50,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isDark
? Colors.white.withValues(alpha: 0.04)
: Colors.grey.shade100,
width: 1,
),
),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(14),
),
child: Center(child: Icon(icon, color: iconColor, size: 24)),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
color: isDark ? Colors.white54 : Colors.grey.shade600,
),
),
],
),
),
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: iconColor.withValues(alpha: 0.1),
),
child: const Icon(
Icons.arrow_forward_rounded,
size: 16,
color: Colors.grey,
),
),
],
),
),
),
);
}
Widget _buildContactCard(ThemeData theme, bool isDark) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: _cardDecoration(theme, isDark),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.help_outline_rounded,
size: 22,
color: theme.colorScheme.primary,
),
const SizedBox(width: 10),
Text(
'Need Help?',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 12),
Text(
'If you have any questions about the deletion process or '
'would like assistance, our support team is here to help.',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: isDark ? Colors.white60 : Colors.grey.shade600,
height: 1.5,
),
),
const SizedBox(height: 16),
Row(
children: [
Icon(
Icons.phone_rounded,
size: 18,
color: theme.colorScheme.primary,
),
const SizedBox(width: 8),
Text(
'+263 78 777 0295',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.email_outlined,
size: 18,
color: theme.colorScheme.primary,
),
const SizedBox(width: 8),
Text(
'support@qpay.co.zw',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
],
),
);
}
BoxDecoration _cardDecoration(ThemeData theme, bool isDark) {
return BoxDecoration(
color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isDark
? Colors.white.withValues(alpha: 0.06)
: Colors.grey.shade200,
width: 1,
),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.04),
blurRadius: 16,
offset: const Offset(0, 4),
),
],
);
}
}

View File

@@ -97,6 +97,13 @@ class _MoreScreenState extends State<MoreScreen> {
subtitle: 'Get in touch with our team',
onTap: () => context.push('/contact'),
),
_NavOptionTile(
icon: Icons.delete_forever_outlined,
title: 'Request Account Deletion',
subtitle: 'Permanently delete your account and data',
onTap: () => context.push('/delete-account'),
),
const SizedBox(height: 8),
_NavOptionTile(
icon: Icons.logout,
title: 'Logout',