mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-13 09:20:29 +00:00
feat: Add setting to disable map tab for battery savings
Add a new "Disable Map" toggle in settings that allows users to hide the map tab entirely. This is useful for scenarios where the map is not needed and battery conservation is a priority. Changes: - Add disableMap and disableMapDescription localized strings (all languages) - Add isMapEnabled state to AppProvider with persistence - Update HomeScreen to dynamically adjust tab count based on setting - Make onNavigateToMap optional in MessagesTab and ContactsTab - Add toggle switch in Settings screen under Interface section
This commit is contained in:
@@ -47,20 +47,12 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
int _currentIndex = 0;
|
||||
bool _isMapFullscreen = false;
|
||||
bool _showRxTxIndicators = true;
|
||||
bool _isMapEnabled = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 3, vsync: this);
|
||||
_tabController.addListener(() {
|
||||
setState(() {
|
||||
_currentIndex = _tabController.index;
|
||||
// Exit fullscreen when switching away from map tab
|
||||
if (_currentIndex != 2) {
|
||||
_isMapFullscreen = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
_loadMapEnabledAndInitTabs();
|
||||
_loadRxTxPreference();
|
||||
|
||||
// Show permission dialog after the first frame if needed
|
||||
@@ -71,6 +63,63 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadMapEnabledAndInitTabs() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final mapEnabled = prefs.getBool('map_enabled') ?? true;
|
||||
if (mapEnabled != _isMapEnabled) {
|
||||
_isMapEnabled = mapEnabled;
|
||||
}
|
||||
_initTabController();
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _initTabController() {
|
||||
final tabCount = _isMapEnabled ? 3 : 2;
|
||||
_tabController = TabController(length: tabCount, vsync: this);
|
||||
_tabController.addListener(_onTabChanged);
|
||||
}
|
||||
|
||||
void _onTabChanged() {
|
||||
setState(() {
|
||||
_currentIndex = _tabController.index;
|
||||
// Exit fullscreen when switching away from map tab (only if map is enabled and is tab 2)
|
||||
if (_isMapEnabled && _currentIndex != 2) {
|
||||
_isMapFullscreen = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _updateTabController(bool mapEnabled) {
|
||||
if (_isMapEnabled == mapEnabled) return;
|
||||
|
||||
// Save current index before rebuilding
|
||||
final oldIndex = _tabController.index;
|
||||
|
||||
// Remove old listener and dispose
|
||||
_tabController.removeListener(_onTabChanged);
|
||||
_tabController.dispose();
|
||||
|
||||
// Update state
|
||||
_isMapEnabled = mapEnabled;
|
||||
|
||||
// Create new controller
|
||||
final tabCount = mapEnabled ? 3 : 2;
|
||||
_tabController = TabController(length: tabCount, vsync: this);
|
||||
_tabController.addListener(_onTabChanged);
|
||||
|
||||
// Restore index (clamp to valid range)
|
||||
if (oldIndex < tabCount) {
|
||||
_tabController.index = oldIndex;
|
||||
_currentIndex = oldIndex;
|
||||
} else {
|
||||
_currentIndex = tabCount - 1;
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _loadRxTxPreference() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (mounted) {
|
||||
@@ -82,6 +131,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.removeListener(_onTabChanged);
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
@@ -225,8 +275,16 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
messagesProvider.setLocalizations(localizations);
|
||||
}
|
||||
|
||||
// Check if map enabled setting changed and update tab controller
|
||||
final appProvider = context.watch<AppProvider>();
|
||||
if (_isMapEnabled != appProvider.isMapEnabled) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_updateTabController(appProvider.isMapEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
// Determine if we should hide the UI (only in fullscreen on map tab)
|
||||
final shouldHideUI = _isMapFullscreen && _currentIndex == 2;
|
||||
final shouldHideUI = _isMapEnabled && _isMapFullscreen && _currentIndex == 2;
|
||||
|
||||
return Scaffold(
|
||||
appBar: shouldHideUI
|
||||
@@ -308,16 +366,25 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
MessagesTab(onNavigateToMap: () => _tabController.animateTo(2)),
|
||||
ContactsTab(onNavigateToMap: () => _tabController.animateTo(2)),
|
||||
MapTab(
|
||||
onFullscreenChanged: (isFullscreen) {
|
||||
setState(() {
|
||||
_isMapFullscreen = isFullscreen;
|
||||
});
|
||||
},
|
||||
onNavigateToMessages: () => _tabController.animateTo(0),
|
||||
MessagesTab(
|
||||
onNavigateToMap: _isMapEnabled
|
||||
? () => _tabController.animateTo(2)
|
||||
: null,
|
||||
),
|
||||
ContactsTab(
|
||||
onNavigateToMap: _isMapEnabled
|
||||
? () => _tabController.animateTo(2)
|
||||
: null,
|
||||
),
|
||||
if (_isMapEnabled)
|
||||
MapTab(
|
||||
onFullscreenChanged: (isFullscreen) {
|
||||
setState(() {
|
||||
_isMapFullscreen = isFullscreen;
|
||||
});
|
||||
},
|
||||
onNavigateToMessages: () => _tabController.animateTo(0),
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: shouldHideUI
|
||||
@@ -354,10 +421,11 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
),
|
||||
text: AppLocalizations.of(context)!.contacts,
|
||||
),
|
||||
Tab(
|
||||
icon: const Icon(Icons.map),
|
||||
text: AppLocalizations.of(context)!.map,
|
||||
),
|
||||
if (_isMapEnabled)
|
||||
Tab(
|
||||
icon: const Icon(Icons.map),
|
||||
text: AppLocalizations.of(context)!.map,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -18,9 +18,9 @@ import '../utils/toast_logger.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
|
||||
class MessagesTab extends StatefulWidget {
|
||||
final VoidCallback onNavigateToMap;
|
||||
final VoidCallback? onNavigateToMap;
|
||||
|
||||
const MessagesTab({super.key, required this.onNavigateToMap});
|
||||
const MessagesTab({super.key, this.onNavigateToMap});
|
||||
|
||||
@override
|
||||
State<MessagesTab> createState() => _MessagesTabState();
|
||||
@@ -780,7 +780,8 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
isHighlighted: isHighlighted,
|
||||
onNavigateToMap: widget.onNavigateToMap,
|
||||
onTap:
|
||||
message.isSarMarker &&
|
||||
widget.onNavigateToMap != null &&
|
||||
message.isSarMarker &&
|
||||
message.sarGpsCoordinates != null
|
||||
? () {
|
||||
final mapProvider = context
|
||||
@@ -789,9 +790,10 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
location: message.sarGpsCoordinates!,
|
||||
zoom: 15.0,
|
||||
);
|
||||
widget.onNavigateToMap();
|
||||
widget.onNavigateToMap?.call();
|
||||
}
|
||||
: message.isDrawing && message.drawingId != null
|
||||
: widget.onNavigateToMap != null &&
|
||||
message.isDrawing && message.drawingId != null
|
||||
? () {
|
||||
debugPrint('🗺️ [MessagesTab] Drawing tapped! ID: ${message.drawingId}');
|
||||
final mapProvider = context
|
||||
@@ -802,7 +804,7 @@ class _MessagesTabState extends State<MessagesTab> {
|
||||
message.drawingId!,
|
||||
drawingProvider,
|
||||
);
|
||||
widget.onNavigateToMap();
|
||||
widget.onNavigateToMap?.call();
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
@@ -528,6 +528,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
},
|
||||
),
|
||||
),
|
||||
Consumer<AppProvider>(
|
||||
builder: (context, appProvider, child) => SwitchListTile(
|
||||
secondary: const Icon(Icons.map_outlined),
|
||||
title: Text(AppLocalizations.of(context)!.disableMap),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.disableMapDescription,
|
||||
),
|
||||
value: !appProvider.isMapEnabled,
|
||||
onChanged: (value) async {
|
||||
await appProvider.toggleMapEnabled(!value);
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.language),
|
||||
title: Text(AppLocalizations.of(context)!.language),
|
||||
|
||||
Reference in New Issue
Block a user