Compare commits
1 Commits
dev
...
accessibil
Author | SHA1 | Date | |
---|---|---|---|
|
d92767bda1 |
459
dialer/lib/features/accessibility/accessibility.dart
Normal file
459
dialer/lib/features/accessibility/accessibility.dart
Normal file
@ -0,0 +1,459 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class AccessibilityPage extends StatefulWidget {
|
||||
const AccessibilityPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_AccessibilityPageState createState() => _AccessibilityPageState();
|
||||
}
|
||||
|
||||
class _AccessibilityPageState extends State<AccessibilityPage> {
|
||||
// Font size setting
|
||||
double _fontSize = 1.0;
|
||||
|
||||
// Toggle settings
|
||||
bool _highContrastMode = false;
|
||||
bool _reduceAnimations = false;
|
||||
bool _enableVibration = true;
|
||||
bool _enableScreenReader = false;
|
||||
bool _useSimpleLayout = false;
|
||||
bool _autoAnswerCalls = false;
|
||||
|
||||
// Call display timeout
|
||||
int _callDisplayTimeout = 30; // seconds
|
||||
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
setState(() {
|
||||
_fontSize = prefs.getDouble('accessibility_font_size') ?? 1.0;
|
||||
_highContrastMode = prefs.getBool('accessibility_high_contrast') ?? false;
|
||||
_reduceAnimations = prefs.getBool('accessibility_reduce_animations') ?? false;
|
||||
_enableVibration = prefs.getBool('accessibility_vibration') ?? true;
|
||||
_enableScreenReader = prefs.getBool('accessibility_screen_reader') ?? false;
|
||||
_useSimpleLayout = prefs.getBool('accessibility_simple_layout') ?? false;
|
||||
_autoAnswerCalls = prefs.getBool('accessibility_auto_answer') ?? false;
|
||||
_callDisplayTimeout = prefs.getInt('accessibility_call_timeout') ?? 30;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _saveSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setDouble('accessibility_font_size', _fontSize);
|
||||
await prefs.setBool('accessibility_high_contrast', _highContrastMode);
|
||||
await prefs.setBool('accessibility_reduce_animations', _reduceAnimations);
|
||||
await prefs.setBool('accessibility_vibration', _enableVibration);
|
||||
await prefs.setBool('accessibility_screen_reader', _enableScreenReader);
|
||||
await prefs.setBool('accessibility_simple_layout', _useSimpleLayout);
|
||||
await prefs.setBool('accessibility_auto_answer', _autoAnswerCalls);
|
||||
await prefs.setInt('accessibility_call_timeout', _callDisplayTimeout);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Accessibility settings saved')),
|
||||
);
|
||||
}
|
||||
|
||||
String _getFontSizeLabel() {
|
||||
if (_fontSize <= 0.8) return 'Small';
|
||||
if (_fontSize <= 1.0) return 'Default';
|
||||
if (_fontSize <= 1.2) return 'Large';
|
||||
if (_fontSize <= 1.4) return 'Extra Large';
|
||||
return 'Maximum';
|
||||
}
|
||||
|
||||
String _getCallTimeoutLabel() {
|
||||
if (_callDisplayTimeout <= 15) return '15 seconds';
|
||||
if (_callDisplayTimeout <= 30) return '30 seconds';
|
||||
if (_callDisplayTimeout <= 45) return '45 seconds';
|
||||
if (_callDisplayTimeout <= 60) return '1 minute';
|
||||
return '${_callDisplayTimeout} seconds';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
title: const Text('Accessibility'),
|
||||
backgroundColor: Colors.black,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _saveSettings,
|
||||
child: const Text('Save', style: TextStyle(color: Colors.blue)),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header with icon
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.accessibility_new,
|
||||
size: 50,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'Accessibility Settings',
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Customize your experience',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[400],
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Text Size Settings
|
||||
const _SectionHeader(title: 'Display Settings'),
|
||||
_ContentCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'Text Size',
|
||||
style: TextStyle(color: Colors.white, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
_getFontSizeLabel(),
|
||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Slider(
|
||||
value: _fontSize,
|
||||
min: 0.8,
|
||||
max: 1.6,
|
||||
divisions: 4,
|
||||
label: _getFontSizeLabel(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_fontSize = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_ToggleOption(
|
||||
title: 'High Contrast Mode',
|
||||
subtitle: 'Increase color contrast for better visibility',
|
||||
value: _highContrastMode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_highContrastMode = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
_ToggleOption(
|
||||
title: 'Reduce Animations',
|
||||
subtitle: 'Minimize motion effects throughout the app',
|
||||
value: _reduceAnimations,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_reduceAnimations = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
_ToggleOption(
|
||||
title: 'Simple Layout',
|
||||
subtitle: 'Use a simplified interface with larger touch targets',
|
||||
value: _useSimpleLayout,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_useSimpleLayout = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Assistive Technology
|
||||
const _SectionHeader(title: 'Assistive Technology'),
|
||||
_ContentCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_ToggleOption(
|
||||
title: 'Enable Screen Reader',
|
||||
subtitle: 'Works with TalkBack and VoiceOver',
|
||||
value: _enableScreenReader,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_enableScreenReader = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
_ToggleOption(
|
||||
title: 'Haptic Feedback',
|
||||
subtitle: 'Vibration feedback for interactions',
|
||||
value: _enableVibration,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_enableVibration = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Call Settings
|
||||
const _SectionHeader(title: 'Call Settings'),
|
||||
_ContentCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_ToggleOption(
|
||||
title: 'Auto-Answer Calls',
|
||||
subtitle: 'Answer incoming calls automatically after timeout',
|
||||
value: _autoAnswerCalls,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_autoAnswerCalls = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'Call Display Timeout',
|
||||
style: TextStyle(color: Colors.white, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
_getCallTimeoutLabel(),
|
||||
style: TextStyle(color: Colors.blue, fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Slider(
|
||||
value: _callDisplayTimeout.toDouble(),
|
||||
min: 15,
|
||||
max: 60,
|
||||
divisions: 3,
|
||||
label: _getCallTimeoutLabel(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_callDisplayTimeout = value.toInt();
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Additional Resources
|
||||
const _SectionHeader(title: 'Additional Resources'),
|
||||
_ContentCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_LinkButton(
|
||||
icon: Icons.help_outline,
|
||||
text: 'Accessibility Help',
|
||||
onTap: () {
|
||||
// Add navigation or link to help page
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_LinkButton(
|
||||
icon: Icons.feedback_outlined,
|
||||
text: 'Send Accessibility Feedback',
|
||||
onTap: () {
|
||||
// Add feedback functionality
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SectionHeader extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
const _SectionHeader({Key? key, required this.title}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ContentCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const _ContentCard({Key? key, required this.child}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[900],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ToggleOption extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final bool value;
|
||||
final ValueChanged<bool> onChanged;
|
||||
|
||||
const _ToggleOption({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: const TextStyle(
|
||||
color: Colors.white70,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeColor: Colors.blue,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LinkButton extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String text;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _LinkButton({
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: Colors.blue),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const Spacer(),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: Colors.white70,
|
||||
size: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import 'package:dialer/features/settings/settings.dart';
|
||||
import '../../services/contact_service.dart';
|
||||
import 'package:dialer/features/voicemail/voicemail_page.dart';
|
||||
import '../contacts/widgets/contact_modal.dart';
|
||||
import 'package:dialer/features/accessibility/accessibility.dart'; // Add this import
|
||||
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage>
|
||||
@ -233,6 +234,10 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
value: 'settings',
|
||||
child: Text('Settings'),
|
||||
),
|
||||
const PopupMenuItem<String>(
|
||||
value: 'accessibility',
|
||||
child: Text('Accessibility'),
|
||||
),
|
||||
],
|
||||
onSelected: (String value) {
|
||||
if (value == 'settings') {
|
||||
@ -241,6 +246,12 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const SettingsPage()),
|
||||
);
|
||||
} else if (value == 'accessibility') {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AccessibilityPage()),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user