From 7a80688579cd2927a69d8eaf1cd43058a743a0b1 Mon Sep 17 00:00:00 2001 From: Jonah Date: Fri, 28 Aug 2026 15:36:52 -0500 Subject: [PATCH] Add a full screen toggle button to the map screen Applies the saved preference at startup. New FullScreen utility owns the window state, exposing a ValueNotifier the button listens to, so the map screen holds no local state of its own. Constants.supportsWindowManagement gates it to linux/macos/windows, and AppSettings persists the choice under key-full-screen. Adds a dependency on window_manager, which declares linux/macos/windows only, so it is not registered on Android or iOS. USER_MANUAL.md updated in sections 2, 4.3 and 14. assets/docs/USER_MANUAL.pdf is not regenerated here. --- USER_MANUAL.md | 3 + lib/constants.dart | 1 + lib/data/app_settings.dart | 8 +++ lib/main.dart | 2 + lib/map_screen.dart | 12 ++++ lib/utils/full_screen.dart | 38 ++++++++++ linux/flutter/generated_plugin_registrant.cc | 8 +++ linux/flutter/generated_plugins.cmake | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 ++ pubspec.lock | 70 ++++++++++++++++--- pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 6 ++ windows/flutter/generated_plugins.cmake | 2 + 13 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 lib/utils/full_screen.dart diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 23d2409a..0a6b1563 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -62,6 +62,7 @@ You can reopen onboarding later from the drawer header icon. | Avidyne IFD ADS-B traffic & weather (Capstone GDL90 over Wi-Fi) | All supported platforms | | Pro Services (Flight Intelligence + Backup/Sync + Community + Aircraft Scheduler ) | **iOS and Android only** | | Airport Businesses & Reviews (free, sign-in required) | **iOS and Android only** | +| Full screen toggle | **Desktop only** (Linux, macOS, Windows) | | PDF viewing in Documents/Help | Not available on Linux | | File sharing from Documents/Logbook export | Not available on Linux | @@ -139,6 +140,7 @@ The **instrument tiles** float as a movable overlay over the map (GS, ALT, MT, P - **Notes icon**: opens handwriting Notes screen. - **Chart type popup**: selects chart source type. - **Layers popup**: per-layer on/off via opacity slider. +- **Full screen toggle** (desktop only): switches the app window between full screen and windowed. The choice is remembered for the next launch. Starts in full screen. #### Top-left - **Instrument tiles menu** (arrow dropdown): tile sizing, lock/reset layout, and show/hide individual instrument tiles (including the `ADSB` tile). @@ -1317,6 +1319,7 @@ This is the most common cause of "AvareX traffic doesn't work" on iPhone/iPad an | Landing performance | `MAP → Menu → Aircraft & Performance → Landing tab` (fixed-wing icon only; hidden for helicopter) | | Cruise performance | `MAP → Menu → Aircraft & Performance → Cruise tab` (fixed-wing icon only; hidden for helicopter) | | Runway crossing alert | Automatic when Mute is off; uses closest-airport runways | +| Toggle full screen (desktop) | `MAP → bottom-right control row → full screen icon` | | Fuel burn in plan | `PLAN tab` — set **GPH** manually or use the aircraft icon to load from performance data | | Logbook + dashboard | `MAP → Menu → Log Book` | | Logbook statistics | `MAP → Menu → Log Book → Details` | diff --git a/lib/constants.dart b/lib/constants.dart index 55886524..20af0764 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -99,6 +99,7 @@ class Constants { static final bool shouldShowBluetoothSpp = (Platform.isAndroid); static final bool shouldShouldReview = (Platform.isMacOS || Platform.isIOS | Platform.isAndroid || Platform.isWindows); static final bool shouldShowProServices = (Platform.isIOS || Platform.isAndroid); + static final bool supportsWindowManagement = (Platform.isLinux || Platform.isWindows || Platform.isMacOS); // Whether the Firebase-backed cloud features (e.g. Airport Businesses & // Reviews) are available. Firebase is only initialized on these platforms diff --git a/lib/data/app_settings.dart b/lib/data/app_settings.dart index cb212354..22dbf469 100644 --- a/lib/data/app_settings.dart +++ b/lib/data/app_settings.dart @@ -244,6 +244,14 @@ class AppSettings { provider.setBool("key-signed", value); } + bool getFullScreen() { + return provider.getValue("key-full-screen", defaultValue: true) as bool; + } + + void setFullScreen(bool value) { + provider.setBool("key-full-screen", value); + } + String getTrafficPuckSize() { return provider.getValue("key-traffic-puck-size", defaultValue: "S") as String; } diff --git a/lib/main.dart b/lib/main.dart index c9161d5c..1cff2781 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:avaremp/plan/plan_action_screen.dart'; import 'package:avaremp/services/login_screen.dart'; import 'package:avaremp/services/revenue_cat.dart'; import 'package:avaremp/storage.dart'; +import 'package:avaremp/utils/full_screen.dart'; import 'package:avaremp/writing_screen.dart'; import 'aircraft/aircraft_performance_screen.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -32,6 +33,7 @@ void main() { // this is to control cache. Nexrad needs it or image caching will make it impossible to animate weather CustomWidgetsBinding(); Storage().init().then((accentColor) async { + await FullScreen.applySaved(); if(Constants.shouldShowProServices) { try { await Firebase.initializeApp( diff --git a/lib/map_screen.dart b/lib/map_screen.dart index 761df35f..30163f8d 100644 --- a/lib/map_screen.dart +++ b/lib/map_screen.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'package:avaremp/utils/elevation_tile_provider.dart'; +import 'package:avaremp/utils/full_screen.dart'; import 'package:avaremp/utils/image_utils.dart'; import 'package:avaremp/utils/mbtiles_layer.dart'; import 'package:avaremp/utils/path_utils.dart'; @@ -1678,6 +1679,17 @@ class MapScreenState extends State { child: const Icon(Icons.layers)), onPressed: () => _showLayerSelector(context), ), + + if (FullScreen.supported) + ValueListenableBuilder( + valueListenable: FullScreen.state, + builder: (context, on, _) => IconButton( + tooltip: on ? "Leave full screen" : "Enter full screen", + icon: CircleAvatar(radius: iconRadius, backgroundColor: Theme.of(context).scaffoldBackgroundColor.withValues(alpha: 0.7), + child: Icon(on ? Icons.fullscreen_exit : Icons.fullscreen)), + onPressed: () => FullScreen.set(!on), + ), + ), ] ), ) diff --git a/lib/utils/full_screen.dart b/lib/utils/full_screen.dart new file mode 100644 index 00000000..06fb7805 --- /dev/null +++ b/lib/utils/full_screen.dart @@ -0,0 +1,38 @@ +import 'package:flutter/foundation.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../constants.dart'; +import '../storage.dart'; + +class FullScreen { + static final ValueNotifier state = ValueNotifier(false); + + static bool get supported => Constants.supportsWindowManagement; + + static Future applySaved() async { + if (!supported) { + return; + } + try { + await windowManager.ensureInitialized(); + await set(Storage().settings.getFullScreen()); + } + catch (e) { + Storage().setException("Full screen failed: $e"); + } + } + + static Future set(bool on) async { + if (!supported) { + return; + } + try { + await windowManager.setFullScreen(on); + Storage().settings.setFullScreen(on); + state.value = on; + } + catch (e) { + Storage().setException("Full screen failed: $e"); + } + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index c68b47ab..f1c33627 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = @@ -27,6 +29,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) gtk_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); gtk_plugin_register_with_registrar(gtk_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); + screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); @@ -36,4 +41,7 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 5594240e..3d7dd790 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -7,9 +7,11 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_webview_auth file_selector_linux gtk + screen_retriever_linux sqlite3_flutter_libs syncfusion_pdfviewer_linux url_launcher_linux + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 63736a90..76443105 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -21,12 +21,14 @@ import in_app_review import package_info_plus import path_provider_foundation import purchases_flutter +import screen_retriever_macos import share_plus import sqflite_darwin import sqlite3_flutter_libs import syncfusion_pdfviewer_macos import url_launcher_macos import wakelock_plus +import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) @@ -45,10 +47,12 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PurchasesFlutterPlugin.register(with: registry.registrar(forPlugin: "PurchasesFlutterPlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index cf137642..8fe66f0b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1160,10 +1160,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.20.3" introduction_screen: dependency: "direct main" description: @@ -1256,10 +1256,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -1280,10 +1280,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.19.0" mgrs_dart: dependency: transitive description: @@ -1574,6 +1574,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: ace919117a7520c13a50a6259e60c4a0d4cbe98809468792a91b5c5adada2aa6 + url: "https://pub.dev" + source: hosted + version: "0.2.2" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: "7b52006a5ceae1f3d5af7f77188c3290d6e7d8ded16d99809bea84967c65c257" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: a1489b99cce597c45a54b9aae1cd94c8d4705353b7e0bb2457a6e4de44e0ad8a + url: "https://pub.dev" + source: hosted + version: "0.2.2" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: "94a5535277510a63184ca178ce12a1449bc0b38618879aa1c18bf57369c5064a" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: dafc6922b0bfbf1d48cf3ccbf519b4fff47bdcb820da1728ea6db675fecc9324 + url: "https://pub.dev" + source: hosted + version: "0.2.2" scribble: dependency: "direct main" description: @@ -1847,10 +1887,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.12" timing: dependency: transitive description: @@ -2007,10 +2047,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vector_tile: dependency: "direct main" description: @@ -2123,6 +2163,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: "05c231fd7b23d2380f14c5cc10b7b93d60d4fa4a2fb4e0f032de27e44b5560e9" + url: "https://pub.dev" + source: hosted + version: "0.5.2" wkt_parser: dependency: transitive description: @@ -2156,5 +2204,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.0 <4.0.0" + dart: ">=3.11.0-0 <4.0.0" flutter: ">=3.38.0" diff --git a/pubspec.yaml b/pubspec.yaml index 74302fb8..8ba07dac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: geolocator: ^14.0.2 geolocator_linux: ^0.2.0+1 wakelock_plus: ^1.1.4 + window_manager: ^0.5.2 dio: ^5.3.4 archive: ^4.0.7 http: any diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 22693539..24fce018 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AppLinksPluginCApiRegisterWithRegistrar( @@ -42,6 +44,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("GeolocatorWindows")); MsvcredistPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("MsvcredistPluginCApi")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( @@ -50,4 +54,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 344f8617..e9f96d2f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -13,10 +13,12 @@ list(APPEND FLUTTER_PLUGIN_LIST firebase_storage geolocator_windows msvcredist + screen_retriever_windows share_plus sqlite3_flutter_libs syncfusion_pdfviewer_windows url_launcher_windows + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST