mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Add localization support for various UI elements and messages
- Integrated localization for alert dialogs, snack bars, and button labels in PacketLogScreen and SettingsScreen. - Updated location tracking service messages to use localized strings. - Enhanced ContactTile and DirectMessageSheet with localized text for user interactions. - Implemented localization in DrawingLayer and DrawingToolbar for map-related actions. - Updated SAR Update Sheet to utilize localized strings for user prompts and actions. - Ensured consistent use of localization across all relevant widgets and screens.
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import '../providers/connection_provider.dart';
|
||||
import '../services/validation_service.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class DeviceConfigScreen extends StatefulWidget {
|
||||
const DeviceConfigScreen({super.key});
|
||||
@@ -174,8 +175,8 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Public info saved'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.save),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -244,8 +245,8 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Radio settings saved'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.save),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -268,7 +269,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!serviceEnabled) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Location services disabled')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.locationServicesDisabled)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -280,7 +281,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (permission == LocationPermission.denied) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Location permission denied')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.locationPermissionDenied)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -290,7 +291,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Location permission permanently denied')),
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.locationPermissionPermanentlyDenied)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -310,8 +311,11 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Location updated'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationBroadcast(
|
||||
position.latitude.toStringAsFixed(6),
|
||||
position.longitude.toStringAsFixed(6),
|
||||
)),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -331,8 +335,8 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (!connectionProvider.deviceInfo.isConnected) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Not connected to device'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.deviceNotConnected),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
@@ -347,9 +351,12 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
await connectionProvider.sendSelfAdvert(floodMode: true);
|
||||
|
||||
if (context.mounted) {
|
||||
final deviceInfo = connectionProvider.deviceInfo;
|
||||
final lat = deviceInfo.advLat != null ? (deviceInfo.advLat! / 1000000).toStringAsFixed(6) : '0.0';
|
||||
final lon = deviceInfo.advLon != null ? (deviceInfo.advLon! / 1000000).toStringAsFixed(6) : '0.0';
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Advertisement broadcast to mesh network'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.advertisedAtLocation(lat, lon)),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
@@ -358,7 +365,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to broadcast: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToAdvertise(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -377,7 +384,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Device Settings'),
|
||||
title: Text(AppLocalizations.of(context)!.settings),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -441,13 +448,13 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.sensors),
|
||||
tooltip: 'Broadcast',
|
||||
tooltip: AppLocalizations.of(context)!.settings,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton.filled(
|
||||
onPressed: _savePublicInfo,
|
||||
icon: const Icon(Icons.save),
|
||||
tooltip: 'Save',
|
||||
tooltip: AppLocalizations.of(context)!.save,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -555,7 +562,7 @@ class _DeviceConfigScreenState extends State<DeviceConfigScreen> {
|
||||
IconButton.filled(
|
||||
onPressed: _saveRadioSettings,
|
||||
icon: const Icon(Icons.save),
|
||||
tooltip: 'Save',
|
||||
tooltip: AppLocalizations.of(context)!.save,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'settings_screen.dart';
|
||||
import 'device_config_screen.dart';
|
||||
import 'packet_log_screen.dart';
|
||||
import '../utils/toast_logger.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
final Function(AppThemeMode) onThemeChanged;
|
||||
@@ -77,7 +78,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
|
||||
if (!connectionProvider.deviceInfo.isConnected) {
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(context, 'Device not connected');
|
||||
ToastLogger.error(context, AppLocalizations.of(context)!.deviceNotConnected);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -89,7 +90,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(
|
||||
context,
|
||||
'Location services are disabled. Please enable them in Settings.',
|
||||
AppLocalizations.of(context)!.locationServicesDisabled,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -101,7 +102,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(context, 'Location permission denied');
|
||||
ToastLogger.error(context, AppLocalizations.of(context)!.locationPermissionDenied);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -111,7 +112,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(
|
||||
context,
|
||||
'Location permission permanently denied. Please enable in Settings.',
|
||||
AppLocalizations.of(context)!.locationPermissionPermanentlyDenied,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -129,7 +130,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
} catch (e) {
|
||||
print('❌ Failed to get GPS position: $e');
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(context, 'Failed to get GPS location');
|
||||
ToastLogger.error(context, AppLocalizations.of(context)!.failedToGetGpsLocation);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -149,13 +150,16 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
if (context.mounted) {
|
||||
ToastLogger.success(
|
||||
context,
|
||||
'Advertised at ${position.latitude.toStringAsFixed(6)}, ${position.longitude.toStringAsFixed(6)}',
|
||||
AppLocalizations.of(context)!.advertisedAtLocation(
|
||||
position.latitude.toStringAsFixed(6),
|
||||
position.longitude.toStringAsFixed(6),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('❌ Failed to advertise device: $e');
|
||||
if (context.mounted) {
|
||||
ToastLogger.error(context, 'Failed to advertise: $e');
|
||||
ToastLogger.error(context, AppLocalizations.of(context)!.failedToAdvertise(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +207,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'MeshCore',
|
||||
AppLocalizations.of(context)!.appTitle,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
@@ -211,7 +215,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Scanning for devices...',
|
||||
AppLocalizations.of(context)!.scanningForDevices,
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -253,7 +257,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'The default pin for devices without a screen is 123456. Trouble pairing? Forget the bluetooth device in system settings.',
|
||||
AppLocalizations.of(context)!.defaultPinInfo,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
fontSize: 13,
|
||||
@@ -288,7 +292,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'No devices found',
|
||||
AppLocalizations.of(context)!.noDevicesFound,
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -303,7 +307,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
connectionProvider.startScan();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Scan Again'),
|
||||
label: Text(AppLocalizations.of(context)!.scanAgain),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -358,7 +362,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Text(
|
||||
'Tap to connect',
|
||||
AppLocalizations.of(context)!.tapToConnect,
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -450,7 +454,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
await provider.disconnect();
|
||||
},
|
||||
icon: const Icon(Icons.power_settings_new),
|
||||
tooltip: 'Disconnect',
|
||||
tooltip: AppLocalizations.of(context)!.disconnect,
|
||||
color: Colors.red.shade700,
|
||||
);
|
||||
}
|
||||
@@ -461,11 +465,11 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
child: const Row(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.map),
|
||||
SizedBox(width: 8),
|
||||
Text('Map Management'),
|
||||
const Icon(Icons.map),
|
||||
const SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)!.mapManagement),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
@@ -483,11 +487,11 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
},
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: const Row(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.settings),
|
||||
SizedBox(width: 8),
|
||||
Text('Settings'),
|
||||
const Icon(Icons.settings),
|
||||
const SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)!.settings),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
@@ -551,16 +555,16 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
Icons.message,
|
||||
unreadCount,
|
||||
),
|
||||
text: 'Messages',
|
||||
text: AppLocalizations.of(context)!.messages,
|
||||
),
|
||||
Tab(
|
||||
icon: _buildTabIconWithBadge(
|
||||
Icons.contacts,
|
||||
newContactsCount,
|
||||
),
|
||||
text: 'Contacts',
|
||||
text: AppLocalizations.of(context)!.contacts,
|
||||
),
|
||||
const Tab(icon: Icon(Icons.map), text: 'Map'),
|
||||
Tab(icon: const Icon(Icons.map), text: AppLocalizations.of(context)!.map),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -583,10 +587,10 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
// Disconnected state: show connect button
|
||||
return Row(
|
||||
children: [
|
||||
const Expanded(
|
||||
Expanded(
|
||||
child: Text(
|
||||
'MeshCore',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
AppLocalizations.of(context)!.appTitle,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
@@ -608,7 +612,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
label: Text(
|
||||
provider.isReconnecting
|
||||
? '${provider.reconnectionAttempt}/${provider.maxReconnectionAttempts}'
|
||||
: 'Connect',
|
||||
: AppLocalizations.of(context)!.connect,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
@@ -624,7 +628,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
IconButton(
|
||||
onPressed: () => provider.cancelReconnection(),
|
||||
icon: const Icon(Icons.close, size: 20),
|
||||
tooltip: 'Cancel reconnection',
|
||||
tooltip: AppLocalizations.of(context)!.cancelReconnection,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade700,
|
||||
foregroundColor: Colors.white,
|
||||
@@ -650,7 +654,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
deviceInfo.selfName ?? 'MeshCore',
|
||||
deviceInfo.selfName ?? AppLocalizations.of(context)!.appTitle,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -838,8 +842,8 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
Expanded(
|
||||
child: Text(
|
||||
isConnected
|
||||
? deviceInfo.displayName ?? 'Connected'
|
||||
: 'Not Connected',
|
||||
? deviceInfo.displayName ?? AppLocalizations.of(context)!.connect
|
||||
: AppLocalizations.of(context)!.deviceNotConnected,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
@@ -881,7 +885,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
? null
|
||||
: () => _showConnectionDialog(context),
|
||||
icon: const Icon(Icons.bluetooth_searching, size: 18),
|
||||
label: const Text('Connect'),
|
||||
label: Text(AppLocalizations.of(context)!.connect),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
@@ -896,7 +900,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
await provider.disconnect();
|
||||
},
|
||||
icon: const Icon(Icons.bluetooth_disabled, size: 18),
|
||||
label: const Text('Disconnect'),
|
||||
label: Text(AppLocalizations.of(context)!.disconnect),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:latlong2/latlong.dart';
|
||||
import '../services/tile_cache_service.dart';
|
||||
import '../services/validation_service.dart';
|
||||
import '../models/map_layer.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class MapManagementScreen extends StatefulWidget {
|
||||
final TileCacheService tileCacheService;
|
||||
@@ -217,8 +218,8 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Download cancelled'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.cancel),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
@@ -237,19 +238,19 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Clear Cache'),
|
||||
title: Text(AppLocalizations.of(context)!.clear),
|
||||
content: const Text(
|
||||
'Are you sure you want to delete all downloaded maps? This action cannot be undone.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
child: const Text('Clear'),
|
||||
child: Text(AppLocalizations.of(context)!.clear),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -295,7 +296,7 @@ class _MapManagementScreenState extends State<MapManagementScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Map Management'),
|
||||
title: Text(AppLocalizations.of(context)!.mapManagement),
|
||||
),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
|
||||
@@ -28,6 +28,7 @@ import '../widgets/map/detailed_compass_dialog.dart';
|
||||
import '../widgets/map/drawing_layer.dart';
|
||||
import '../widgets/map/drawing_toolbar.dart';
|
||||
import '../widgets/messages/sar_update_sheet.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'map_management_screen.dart';
|
||||
|
||||
class MapTab extends StatefulWidget {
|
||||
@@ -643,8 +644,8 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
_saveSettings();
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to start background tracking. Check permissions and BLE connection.'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.failedToStartBackgroundTracking),
|
||||
duration: Duration(seconds: 3),
|
||||
),
|
||||
);
|
||||
@@ -710,8 +711,8 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
if (!connectionProvider.deviceInfo.isConnected) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Not connected to device'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.deviceNotConnected),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../models/sar_marker.dart';
|
||||
import '../widgets/messages/sar_update_sheet.dart';
|
||||
import '../widgets/contacts/direct_message_sheet.dart';
|
||||
import '../utils/toast_logger.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class MessagesTab extends StatefulWidget {
|
||||
final VoidCallback onNavigateToMap;
|
||||
@@ -304,14 +305,14 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'No messages yet',
|
||||
AppLocalizations.of(context)!.noMessagesYet,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Pull down to sync messages',
|
||||
AppLocalizations.of(context)!.pullDownToSync,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium,
|
||||
@@ -374,7 +375,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
// SAR quick action button
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_location_alt),
|
||||
tooltip: 'Send SAR marker',
|
||||
tooltip: AppLocalizations.of(context)!.sendSarMarker,
|
||||
onPressed: _showSarDialog,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Theme.of(
|
||||
@@ -579,9 +580,9 @@ class _MessageBubble extends StatelessWidget {
|
||||
// Delete message option
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete, color: Colors.red),
|
||||
title: const Text(
|
||||
'Delete message',
|
||||
style: TextStyle(color: Colors.red),
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!.delete,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
@@ -633,6 +634,7 @@ class _MessageBubble extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _showDeleteConfirmation(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
@@ -641,7 +643,7 @@ class _MessageBubble extends StatelessWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(l10n.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:io';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import '../models/ble_packet_log.dart';
|
||||
import '../services/meshcore_ble_service.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class PacketLogScreen extends StatefulWidget {
|
||||
final MeshCoreBleService bleService;
|
||||
@@ -149,12 +150,12 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Clear Packet Logs'),
|
||||
title: Text(AppLocalizations.of(context)!.clearAllData),
|
||||
content: const Text('Are you sure you want to clear all packet logs? This cannot be undone.'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -166,7 +167,7 @@ class _PacketLogScreenState extends State<PacketLogScreen> {
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
child: const Text('Clear'),
|
||||
child: Text(AppLocalizations.of(context)!.clear),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -11,6 +11,7 @@ import '../services/location_tracking_service.dart';
|
||||
import '../services/locale_preferences.dart';
|
||||
import '../utils/sample_data_generator.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
final Function(AppThemeMode) onThemeChanged;
|
||||
@@ -97,7 +98,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Location broadcast: ${position.latitude.toStringAsFixed(5)}, ${position.longitude.toStringAsFixed(5)}',
|
||||
AppLocalizations.of(context)!.locationBroadcast(
|
||||
position.latitude.toStringAsFixed(5),
|
||||
position.longitude.toStringAsFixed(5),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: const Duration(seconds: 2),
|
||||
@@ -220,7 +224,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Loaded $teamCount team members, $channelCount channels, ${sarMessages.length} SAR markers, ${channelMessages.length} messages',
|
||||
AppLocalizations.of(context)!.loadedSampleData(
|
||||
teamCount,
|
||||
channelCount,
|
||||
sarMessages.length,
|
||||
channelMessages.length,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
@@ -229,7 +238,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to load sample data: $e'),
|
||||
content: Text(AppLocalizations.of(context)!.failedToLoadSampleData(e.toString())),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
@@ -247,11 +256,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
if (!success && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'Failed to start background tracking. Check permissions and BLE connection.',
|
||||
AppLocalizations.of(context)!.failedToStartBackgroundTracking,
|
||||
),
|
||||
duration: Duration(seconds: 3),
|
||||
duration: const Duration(seconds: 3),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -272,19 +281,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Clear All Data'),
|
||||
content: const Text(
|
||||
'This will clear all contacts and SAR markers. Are you sure?',
|
||||
title: Text(AppLocalizations.of(context)!.clearAllDataConfirmTitle),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.clearAllDataConfirmMessage,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
style: TextButton.styleFrom(foregroundColor: Colors.red),
|
||||
child: const Text('Clear'),
|
||||
child: Text(AppLocalizations.of(context)!.clear),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -307,8 +316,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('All data cleared'),
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.allDataCleared),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
@@ -317,22 +326,22 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Settings')),
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)),
|
||||
body: ListView(
|
||||
children: [
|
||||
// General Settings Section
|
||||
_buildSectionHeader('General'),
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.general),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.palette),
|
||||
title: const Text('Theme'),
|
||||
title: Text(AppLocalizations.of(context)!.theme),
|
||||
subtitle: Text(AppTheme.getThemeDisplayName(_selectedTheme)),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showThemeDialog(),
|
||||
),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(Icons.radar),
|
||||
title: const Text('Show RX/TX Indicators'),
|
||||
subtitle: const Text('Display packet activity indicators in top bar'),
|
||||
title: Text(AppLocalizations.of(context)!.showRxTxIndicators),
|
||||
subtitle: Text(AppLocalizations.of(context)!.displayPacketActivity),
|
||||
value: _showRxTxIndicators,
|
||||
onChanged: (value) async {
|
||||
setState(() {
|
||||
@@ -343,7 +352,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.language),
|
||||
title: const Text('Language'),
|
||||
title: Text(AppLocalizations.of(context)!.language),
|
||||
subtitle: Text(LocalePreferences.getDisplayName(_selectedLocale)),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showLanguageDialog(),
|
||||
@@ -351,13 +360,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
const Divider(),
|
||||
|
||||
// Location Settings Section
|
||||
_buildSectionHeader('Location Broadcasting'),
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.locationBroadcasting),
|
||||
|
||||
// Automatic tracking settings
|
||||
SwitchListTile(
|
||||
secondary: const Icon(Icons.location_on),
|
||||
title: const Text('Auto Location Tracking'),
|
||||
subtitle: const Text('Automatically broadcast position updates'),
|
||||
title: Text(AppLocalizations.of(context)!.autoLocationTracking),
|
||||
subtitle: Text(AppLocalizations.of(context)!.automaticallyBroadcastPosition),
|
||||
value: _locationService.isTracking,
|
||||
onChanged: (value) {
|
||||
if (value) {
|
||||
@@ -371,8 +380,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (_locationService.isTracking) ...[
|
||||
ListTile(
|
||||
leading: const Icon(Icons.tune),
|
||||
title: const Text('Configure Tracking'),
|
||||
subtitle: const Text('Distance and time thresholds'),
|
||||
title: Text(AppLocalizations.of(context)!.configureTracking),
|
||||
subtitle: Text(AppLocalizations.of(context)!.distanceAndTimeThresholds),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () => _showTrackingConfigDialog(),
|
||||
),
|
||||
@@ -381,10 +390,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
const Divider(),
|
||||
|
||||
// About Section
|
||||
_buildSectionHeader('About'),
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.about),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info),
|
||||
title: const Text('App Version'),
|
||||
title: Text(AppLocalizations.of(context)!.appVersion),
|
||||
subtitle: Text(
|
||||
_packageInfo != null
|
||||
? '${_packageInfo!.version} (${_packageInfo!.buildNumber})'
|
||||
@@ -393,34 +402,34 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.badge),
|
||||
title: const Text('App Name'),
|
||||
title: Text(AppLocalizations.of(context)!.appName),
|
||||
subtitle: Text(_packageInfo?.appName ?? 'MeshCore SAR'),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.description),
|
||||
title: const Text('About MeshCore SAR'),
|
||||
subtitle: const Text(
|
||||
'Search & Rescue application with BLE mesh networking and offline maps',
|
||||
title: Text(AppLocalizations.of(context)!.aboutMeshCoreSar),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.aboutDescription.split('\n\n')[0],
|
||||
),
|
||||
onTap: () => _showAboutDialog(),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
// Developer Section
|
||||
_buildSectionHeader('Developer'),
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.developer),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.bug_report),
|
||||
title: const Text('Package Name'),
|
||||
title: Text(AppLocalizations.of(context)!.packageName),
|
||||
subtitle: Text(_packageInfo?.packageName ?? 'com.meshcore.sar'),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
// Sample Data Section
|
||||
_buildSectionHeader('Sample Data'),
|
||||
_buildSectionHeader(AppLocalizations.of(context)!.sampleData),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Text(
|
||||
'Load or clear sample contacts, channel messages, and SAR markers for testing',
|
||||
AppLocalizations.of(context)!.sampleDataDescription,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
@@ -440,7 +449,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Icon(Icons.add_circle_outline),
|
||||
label: const Text('Load Sample Data'),
|
||||
label: Text(AppLocalizations.of(context)!.loadSampleData),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
@@ -451,7 +460,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: _isLoadingSampleData ? null : _clearSampleData,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
label: const Text('Clear All Data'),
|
||||
label: Text(AppLocalizations.of(context)!.clearAllData),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
@@ -488,7 +497,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setDialogState) => AlertDialog(
|
||||
title: const Text('Location Tracking Configuration'),
|
||||
title: Text(AppLocalizations.of(context)!.locationTrackingConfiguration),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -496,7 +505,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
children: [
|
||||
// Description
|
||||
Text(
|
||||
'Configure when location broadcasts are sent to the mesh network',
|
||||
AppLocalizations.of(context)!.configureWhenLocationBroadcasts,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
@@ -505,14 +514,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
// Minimum Distance
|
||||
Text(
|
||||
'Minimum Distance',
|
||||
AppLocalizations.of(context)!.minimumDistance,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Broadcast only after moving ${tempMinDistance.toStringAsFixed(0)} meters',
|
||||
AppLocalizations.of(context)!.broadcastAfterMoving(tempMinDistance.toStringAsFixed(0)),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -551,14 +560,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
// Maximum Distance
|
||||
Text(
|
||||
'Maximum Distance',
|
||||
AppLocalizations.of(context)!.maximumDistance,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Always broadcast after moving ${tempMaxDistance.toStringAsFixed(0)} meters',
|
||||
AppLocalizations.of(context)!.alwaysBroadcastAfterMoving(tempMaxDistance.toStringAsFixed(0)),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -594,14 +603,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
// Minimum Time Interval
|
||||
Text(
|
||||
'Minimum Time Interval',
|
||||
AppLocalizations.of(context)!.minimumTimeInterval,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Always broadcast every ${_formatDuration(tempTimeInterval)}',
|
||||
AppLocalizations.of(context)!.alwaysBroadcastEvery(_formatDuration(tempTimeInterval)),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -638,7 +647,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
@@ -663,7 +672,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: const Text('Save'),
|
||||
child: Text(AppLocalizations.of(context)!.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -689,14 +698,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Choose Theme'),
|
||||
title: Text(AppLocalizations.of(context)!.chooseTheme),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RadioListTile<AppThemeMode>(
|
||||
title: const Text('Light'),
|
||||
subtitle: const Text('Blue light theme'),
|
||||
title: Text(AppLocalizations.of(context)!.light),
|
||||
subtitle: Text(AppLocalizations.of(context)!.blueLightTheme),
|
||||
value: AppThemeMode.light,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -705,8 +714,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
},
|
||||
),
|
||||
RadioListTile<AppThemeMode>(
|
||||
title: const Text('Dark'),
|
||||
subtitle: const Text('Blue dark theme'),
|
||||
title: Text(AppLocalizations.of(context)!.dark),
|
||||
subtitle: Text(AppLocalizations.of(context)!.blueDarkTheme),
|
||||
value: AppThemeMode.dark,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -718,7 +727,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
RadioListTile<AppThemeMode>(
|
||||
title: Row(
|
||||
children: [
|
||||
const Text('SAR Red'),
|
||||
Text(AppLocalizations.of(context)!.sarRed),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 16,
|
||||
@@ -731,7 +740,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: const Text('Alert/Emergency mode'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.alertEmergencyMode),
|
||||
value: AppThemeMode.sarRed,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -742,7 +751,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
RadioListTile<AppThemeMode>(
|
||||
title: Row(
|
||||
children: [
|
||||
const Text('SAR Green'),
|
||||
Text(AppLocalizations.of(context)!.sarGreen),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 16,
|
||||
@@ -755,7 +764,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: const Text('Safe/All Clear mode'),
|
||||
subtitle: Text(AppLocalizations.of(context)!.safeAllClearMode),
|
||||
value: AppThemeMode.sarGreen,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -765,8 +774,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
const Divider(),
|
||||
RadioListTile<AppThemeMode>(
|
||||
title: const Text('Auto (System)'),
|
||||
subtitle: const Text('Follow system theme'),
|
||||
title: Text(AppLocalizations.of(context)!.autoSystem),
|
||||
subtitle: Text(AppLocalizations.of(context)!.followSystemTheme),
|
||||
value: AppThemeMode.system,
|
||||
groupValue: _selectedTheme,
|
||||
onChanged: (value) {
|
||||
@@ -780,7 +789,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -791,14 +800,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Choose Language'),
|
||||
title: Text(AppLocalizations.of(context)!.chooseLanguage),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RadioListTile<Locale?>(
|
||||
title: const Text('System Default'),
|
||||
subtitle: const Text('Follow system language'),
|
||||
title: Text(LocalePreferences.getDisplayName(null)),
|
||||
subtitle: Text(LocalePreferences.getDisplayName(null)),
|
||||
value: null,
|
||||
groupValue: _selectedLocale,
|
||||
onChanged: (value) {
|
||||
@@ -824,7 +833,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -835,7 +844,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('About MeshCore SAR'),
|
||||
title: Text(AppLocalizations.of(context)!.aboutMeshCoreSar),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -853,31 +862,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'A Search & Rescue application designed for emergency response teams. '
|
||||
'Features include:\n\n'
|
||||
'• BLE mesh networking for device-to-device communication\n'
|
||||
'• Offline maps with multiple layer options\n'
|
||||
'• Real-time team member tracking\n'
|
||||
'• SAR tactical markers (found person, fire, staging)\n'
|
||||
'• Contact management and messaging\n'
|
||||
'• GPS tracking with compass heading\n'
|
||||
'• Map tile caching for offline use',
|
||||
Text(
|
||||
AppLocalizations.of(context)!.aboutDescription,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Technologies Used:',
|
||||
AppLocalizations.of(context)!.technologiesUsed,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'• Flutter for cross-platform development\n'
|
||||
'• BLE (Bluetooth Low Energy) for mesh networking\n'
|
||||
'• OpenStreetMap for mapping\n'
|
||||
'• Provider for state management\n'
|
||||
'• SharedPreferences for local storage',
|
||||
Text(
|
||||
AppLocalizations.of(context)!.technologiesList,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -885,7 +882,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Close'),
|
||||
child: Text(AppLocalizations.of(context)!.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user