mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Enable profiles switch UI
This commit is contained in:
@@ -20,6 +20,7 @@ import 'settings_screen.dart';
|
||||
import 'device_config_screen.dart';
|
||||
import 'packet_log_screen.dart';
|
||||
import 'live_traffic_screen.dart';
|
||||
import 'profiles_screen.dart';
|
||||
import 'spectrum_scan_screen.dart';
|
||||
import '../utils/toast_logger.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
@@ -28,6 +29,8 @@ import '../widgets/connection_dialog.dart';
|
||||
import '../utils/battery_display_helper.dart';
|
||||
import '../services/developer_mode_service.dart';
|
||||
import '../services/mesh_map_nodes_service.dart';
|
||||
import '../services/profile_manager.dart';
|
||||
import '../services/profiles_feature_service.dart';
|
||||
|
||||
enum _HomeTab { messages, contacts, sensors, map }
|
||||
|
||||
@@ -237,7 +240,11 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_showRxTxIndicators = prefs.getBool('show_rx_tx_indicators') ?? true;
|
||||
_showRxTxIndicators =
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('show_rx_tx_indicators'),
|
||||
) ??
|
||||
true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -626,6 +633,9 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
final items = <PopupMenuEntry<void>>[];
|
||||
final profilesEnabled = context
|
||||
.read<ProfileManager>()
|
||||
.profilesEnabled;
|
||||
|
||||
if (_isDeveloperModeEnabled) {
|
||||
items.add(
|
||||
@@ -776,6 +786,31 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
);
|
||||
|
||||
if (profilesEnabled) {
|
||||
items.add(
|
||||
PopupMenuItem(
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.layers_outlined),
|
||||
SizedBox(width: 8),
|
||||
Text('Profiles'),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
final navigator = Navigator.of(context);
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (!mounted) return;
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfilesScreen(),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
),
|
||||
|
||||
@@ -28,6 +28,7 @@ import '../services/location_tracking_service.dart';
|
||||
import '../services/map_marker_service.dart';
|
||||
import '../services/message_destination_preferences.dart';
|
||||
import '../services/trail_color_service.dart';
|
||||
import '../services/profiles_feature_service.dart';
|
||||
import '../widgets/map_debug_info.dart';
|
||||
import '../widgets/map/compass_widget.dart';
|
||||
import '../widgets/map/detailed_compass_dialog.dart';
|
||||
@@ -244,25 +245,49 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (mounted) {
|
||||
// Load last map position if available
|
||||
final lastLat = prefs.getDouble('map_last_latitude');
|
||||
final lastLon = prefs.getDouble('map_last_longitude');
|
||||
final lastZoom = prefs.getDouble('map_last_zoom');
|
||||
final lastLat = prefs.getDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_latitude'),
|
||||
);
|
||||
final lastLon = prefs.getDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_longitude'),
|
||||
);
|
||||
final lastZoom = prefs.getDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_zoom'),
|
||||
);
|
||||
|
||||
// Load last map layer
|
||||
final lastLayerType = prefs.getInt('map_last_layer_type');
|
||||
final lastLayerType = prefs.getInt(
|
||||
ProfileStorageScope.scopedKey('map_last_layer_type'),
|
||||
);
|
||||
setState(() {
|
||||
_rotateMarkerWithHeading =
|
||||
prefs.getBool('map_rotate_with_heading') ?? false;
|
||||
_showMapDebugInfo = prefs.getBool('map_show_debug_info') ?? false;
|
||||
_isFullscreen = prefs.getBool('map_fullscreen') ?? false;
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('map_rotate_with_heading'),
|
||||
) ??
|
||||
false;
|
||||
_showMapDebugInfo =
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('map_show_debug_info'),
|
||||
) ??
|
||||
false;
|
||||
_isFullscreen =
|
||||
prefs.getBool(ProfileStorageScope.scopedKey('map_fullscreen')) ??
|
||||
false;
|
||||
|
||||
// Notify parent about initial fullscreen state
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onFullscreenChanged?.call(_isFullscreen);
|
||||
});
|
||||
_gpsUpdateDistance = prefs.getDouble('map_gps_update_distance') ?? 3.0;
|
||||
_gpsUpdateDistance =
|
||||
prefs.getDouble(
|
||||
ProfileStorageScope.scopedKey('map_gps_update_distance'),
|
||||
) ??
|
||||
3.0;
|
||||
_backgroundTrackingEnabled =
|
||||
prefs.getBool('background_tracking_enabled') ?? false;
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('background_tracking_enabled'),
|
||||
) ??
|
||||
false;
|
||||
|
||||
// Store saved position for use in build
|
||||
if (lastLat != null && lastLon != null && lastZoom != null) {
|
||||
@@ -298,18 +323,33 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
|
||||
Future<void> _saveSettings() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('map_rotate_with_heading', _rotateMarkerWithHeading);
|
||||
await prefs.setBool('map_show_debug_info', _showMapDebugInfo);
|
||||
await prefs.setBool('map_fullscreen', _isFullscreen);
|
||||
await prefs.setDouble('map_gps_update_distance', _gpsUpdateDistance);
|
||||
await prefs.setBool(
|
||||
'background_tracking_enabled',
|
||||
ProfileStorageScope.scopedKey('map_rotate_with_heading'),
|
||||
_rotateMarkerWithHeading,
|
||||
);
|
||||
await prefs.setBool(
|
||||
ProfileStorageScope.scopedKey('map_show_debug_info'),
|
||||
_showMapDebugInfo,
|
||||
);
|
||||
await prefs.setBool(
|
||||
ProfileStorageScope.scopedKey('map_fullscreen'),
|
||||
_isFullscreen,
|
||||
);
|
||||
await prefs.setDouble(
|
||||
ProfileStorageScope.scopedKey('map_gps_update_distance'),
|
||||
_gpsUpdateDistance,
|
||||
);
|
||||
await prefs.setBool(
|
||||
ProfileStorageScope.scopedKey('background_tracking_enabled'),
|
||||
_backgroundTrackingEnabled,
|
||||
);
|
||||
|
||||
// Save layer type.
|
||||
await prefs.setInt('map_last_layer_type', _currentLayer.type.index);
|
||||
await prefs.remove('map_last_layer_name');
|
||||
await prefs.setInt(
|
||||
ProfileStorageScope.scopedKey('map_last_layer_type'),
|
||||
_currentLayer.type.index,
|
||||
);
|
||||
await prefs.remove(ProfileStorageScope.scopedKey('map_last_layer_name'));
|
||||
}
|
||||
|
||||
Future<void> _saveMapPosition() async {
|
||||
@@ -318,9 +358,18 @@ class _MapTabState extends State<MapTab> with AutomaticKeepAliveClientMixin {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final camera = _mapController.camera;
|
||||
await prefs.setDouble('map_last_latitude', camera.center.latitude);
|
||||
await prefs.setDouble('map_last_longitude', camera.center.longitude);
|
||||
await prefs.setDouble('map_last_zoom', camera.zoom);
|
||||
await prefs.setDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_latitude'),
|
||||
camera.center.latitude,
|
||||
);
|
||||
await prefs.setDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_longitude'),
|
||||
camera.center.longitude,
|
||||
);
|
||||
await prefs.setDouble(
|
||||
ProfileStorageScope.scopedKey('map_last_zoom'),
|
||||
camera.zoom,
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('Error saving map position: $e');
|
||||
}
|
||||
|
||||
258
lib/screens/profiles_screen.dart
Normal file
258
lib/screens/profiles_screen.dart
Normal file
@@ -0,0 +1,258 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../models/config_profile.dart';
|
||||
import '../services/profile_manager.dart';
|
||||
import '../services/profile_workspace_coordinator.dart';
|
||||
|
||||
class ProfilesScreen extends StatelessWidget {
|
||||
const ProfilesScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ProfileManager>(
|
||||
builder: (context, profileManager, child) {
|
||||
final profiles = profileManager.visibleProfiles;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Profiles'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.importProfileFromFile();
|
||||
},
|
||||
icon: const Icon(Icons.file_open),
|
||||
tooltip: 'Import profile',
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () => _createProfile(context),
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('New Profile'),
|
||||
),
|
||||
body: profiles.isEmpty
|
||||
? const Center(
|
||||
child: Text('Enable profiles to start managing them.'),
|
||||
)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: profiles.length,
|
||||
itemBuilder: (context, index) {
|
||||
final profile = profiles[index];
|
||||
final isActive =
|
||||
profileManager.activeProfileId == profile.id;
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
profile.name,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
if (isActive)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: const Text('Active'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(_summary(profile)),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.openProfile(profile.id);
|
||||
},
|
||||
child: const Text('Open'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.applyProfile(profile.id);
|
||||
},
|
||||
child: const Text('Apply'),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () async {
|
||||
final resolved =
|
||||
profile.id ==
|
||||
ConfigProfile.defaultProfileId
|
||||
? await context
|
||||
.read<
|
||||
ProfileWorkspaceCoordinator
|
||||
>()
|
||||
.snapshotCurrentProfile(
|
||||
id: profile.id,
|
||||
name: profile.name,
|
||||
)
|
||||
: profile;
|
||||
if (!context.mounted) return;
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.exportProfile(resolved);
|
||||
},
|
||||
child: const Text('Share'),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
onSelected: (value) async {
|
||||
switch (value) {
|
||||
case 'duplicate':
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.duplicateProfile(profile);
|
||||
break;
|
||||
case 'rename':
|
||||
await _renameProfile(context, profile);
|
||||
break;
|
||||
case 'delete':
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.deleteProfile(profile);
|
||||
break;
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'duplicate',
|
||||
child: Text('Duplicate'),
|
||||
),
|
||||
if (!profile.isDefault)
|
||||
const PopupMenuItem(
|
||||
value: 'rename',
|
||||
child: Text('Rename'),
|
||||
),
|
||||
if (!profile.isDefault)
|
||||
const PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _summary(ConfigProfile profile) {
|
||||
if (profile.isDefault) {
|
||||
return 'Current app state and history.';
|
||||
}
|
||||
|
||||
final sections = <String>[];
|
||||
if (profile.sections.deviceConfig?.isEmpty == false) {
|
||||
sections.add('Device');
|
||||
}
|
||||
if (profile.sections.channels.isNotEmpty) {
|
||||
sections.add('${profile.sections.channels.length} channels');
|
||||
}
|
||||
if (profile.sections.appSettings?.isEmpty == false) {
|
||||
sections.add('App settings');
|
||||
}
|
||||
if (profile.sections.mapWorkspace?.isEmpty == false) {
|
||||
sections.add('Map workspace');
|
||||
}
|
||||
return sections.isEmpty ? 'Empty profile' : sections.join(' | ');
|
||||
}
|
||||
|
||||
Future<void> _createProfile(BuildContext context) async {
|
||||
final controller = TextEditingController();
|
||||
final name = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Create Profile'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(labelText: 'Profile name'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(controller.text.trim()),
|
||||
child: const Text('Create'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (name == null || name.isEmpty || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
await context.read<ProfileWorkspaceCoordinator>().createProfileFromCurrent(
|
||||
name: name,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _renameProfile(
|
||||
BuildContext context,
|
||||
ConfigProfile profile,
|
||||
) async {
|
||||
final controller = TextEditingController(text: profile.name);
|
||||
final name = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Rename Profile'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(labelText: 'Profile name'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(controller.text.trim()),
|
||||
child: const Text('Save'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (name == null || name.isEmpty || !context.mounted) {
|
||||
return;
|
||||
}
|
||||
await context.read<ProfileWorkspaceCoordinator>().renameProfile(
|
||||
profile,
|
||||
name,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import '../providers/app_provider.dart';
|
||||
import '../providers/connection_provider.dart';
|
||||
import '../providers/drawing_provider.dart';
|
||||
import '../providers/map_provider.dart';
|
||||
import '../models/config_profile.dart';
|
||||
import '../services/location_tracking_service.dart';
|
||||
import '../services/locale_preferences.dart';
|
||||
import '../services/mesh_map_nodes_service.dart';
|
||||
@@ -26,6 +27,9 @@ import '../services/route_hash_preferences.dart';
|
||||
import '../services/image_codec_service.dart';
|
||||
import '../services/developer_mode_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../services/profile_manager.dart';
|
||||
import '../services/profile_workspace_coordinator.dart';
|
||||
import '../services/profiles_feature_service.dart';
|
||||
import '../utils/sample_data_generator.dart';
|
||||
import '../utils/image_message_parser.dart';
|
||||
import '../utils/voice_message_parser.dart';
|
||||
@@ -33,6 +37,7 @@ import '../theme/app_theme.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import '../widgets/update_dialog.dart';
|
||||
import 'sar_template_management_screen.dart';
|
||||
import 'profiles_screen.dart';
|
||||
import 'welcome_wizard_screen.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
@@ -83,6 +88,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
bool _updateNotificationsEnabled = true;
|
||||
bool _muteForegroundNotifications = true;
|
||||
bool _isDeveloperModeEnabled = false;
|
||||
bool _profilesEnabled = false;
|
||||
DateTime? _onlineTraceCacheUpdatedAt;
|
||||
bool _isClearingOnlineTraceCache = false;
|
||||
int _versionTapCount = 0;
|
||||
@@ -102,6 +108,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
_loadImagePreferences();
|
||||
_loadFastLocationSettings();
|
||||
_loadDeveloperMode();
|
||||
_loadProfilesEnabled();
|
||||
_loadOnlineTraceCacheStatus();
|
||||
_loadMapPreferences();
|
||||
_loadNotificationPreferences();
|
||||
@@ -129,7 +136,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_showRxTxIndicators = prefs.getBool('show_rx_tx_indicators') ?? true;
|
||||
_showRxTxIndicators =
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('show_rx_tx_indicators'),
|
||||
) ??
|
||||
true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -142,6 +153,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadProfilesEnabled() async {
|
||||
final isEnabled = await ProfilesFeatureService.isEnabled();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_profilesEnabled = isEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadNotificationPreferences() async {
|
||||
final service = NotificationService();
|
||||
await service.initialize();
|
||||
@@ -199,22 +218,33 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
Future<void> _saveRxTxPreference(bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool('show_rx_tx_indicators', value);
|
||||
await prefs.setBool(
|
||||
ProfileStorageScope.scopedKey('show_rx_tx_indicators'),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadMapPreferences() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_rotateMapWithHeading = prefs.getBool('map_rotate_with_heading') ?? false;
|
||||
_showMapDebugInfo = prefs.getBool('map_show_debug_info') ?? false;
|
||||
_openMapInFullscreen = prefs.getBool('map_fullscreen') ?? false;
|
||||
_rotateMapWithHeading =
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('map_rotate_with_heading'),
|
||||
) ??
|
||||
false;
|
||||
_showMapDebugInfo =
|
||||
prefs.getBool(ProfileStorageScope.scopedKey('map_show_debug_info')) ??
|
||||
false;
|
||||
_openMapInFullscreen =
|
||||
prefs.getBool(ProfileStorageScope.scopedKey('map_fullscreen')) ??
|
||||
false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _saveMapPreference(String key, bool value) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setBool(key, value);
|
||||
await prefs.setBool(ProfileStorageScope.scopedKey(key), value);
|
||||
}
|
||||
|
||||
Future<void> _loadVoicePreferences() async {
|
||||
@@ -487,7 +517,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
// Load settings and restore tracking state
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final wasTracking =
|
||||
prefs.getBool('background_tracking_enabled') ?? false;
|
||||
prefs.getBool(
|
||||
ProfileStorageScope.scopedKey('background_tracking_enabled'),
|
||||
) ??
|
||||
false;
|
||||
|
||||
if (wasTracking) {
|
||||
await _startBackgroundTracking();
|
||||
@@ -1518,6 +1551,46 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
builder: (context, connectionProvider, child) =>
|
||||
_buildImageModePreviewCard(connectionProvider),
|
||||
),
|
||||
_buildSectionHeader('Profiles'),
|
||||
_buildSettingsCard([
|
||||
SwitchListTile(
|
||||
secondary: const Icon(Icons.layers_outlined),
|
||||
title: const Text('Enable Profiles'),
|
||||
subtitle: const Text(
|
||||
'Show profile management UI while keeping the hidden Default profile as the current workspace.',
|
||||
),
|
||||
value: _profilesEnabled,
|
||||
onChanged: (value) async {
|
||||
await context
|
||||
.read<ProfileWorkspaceCoordinator>()
|
||||
.setProfilesEnabled(value);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_profilesEnabled = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (_profilesEnabled)
|
||||
ListTile(
|
||||
leading: const Icon(Icons.folder_copy_outlined),
|
||||
title: const Text('Manage profiles'),
|
||||
subtitle: Text(
|
||||
context.watch<ProfileManager>().activeProfileId ==
|
||||
ConfigProfile.defaultProfileId
|
||||
? 'Default is active'
|
||||
: 'Custom profile active',
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfilesScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
]),
|
||||
_buildSectionHeader('Templates & Help'),
|
||||
_buildSettingsCard([
|
||||
ListTile(
|
||||
|
||||
Reference in New Issue
Block a user