ios/Podfile.lock
.. .. @@ -5,6 +5,8 @@ 5 5 - bonsoir_darwin (0.0.1): 6 6 - Flutter 7 7 - FlutterMacOS 8 + - connectivity_plus (0.0.1):9 + - Flutter8 10 - device_info_plus (0.0.1): 9 11 - Flutter 10 12 - DKImagePickerController/Core (4.3.9): .. .. @@ -70,6 +72,7 @@ 70 72 DEPENDENCIES: 71 73 - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`) 72 74 - bonsoir_darwin (from `.symlinks/plugins/bonsoir_darwin/darwin`) 75 + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)73 76 - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) 74 77 - file_picker (from `.symlinks/plugins/file_picker/ios`) 75 78 - Flutter (from `Flutter`) .. .. @@ -95,6 +98,8 @@ 95 98 :path: ".symlinks/plugins/audioplayers_darwin/darwin" 96 99 bonsoir_darwin: 97 100 :path: ".symlinks/plugins/bonsoir_darwin/darwin" 101 + connectivity_plus:102 + :path: ".symlinks/plugins/connectivity_plus/ios"98 103 device_info_plus: 99 104 :path: ".symlinks/plugins/device_info_plus/ios" 100 105 file_picker: .. .. @@ -123,6 +128,7 @@ 123 128 SPEC CHECKSUMS: 124 129 audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5 125 130 bonsoir_darwin: 29c7ccf356646118844721f36e1de4b61f6cbd0e 131 + connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd126 132 device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe 127 133 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c 128 134 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 lib/services/mqtt_service.dart
.. .. @@ -5,6 +5,7 @@ 5 5 import 'package:crypto/crypto.dart'; 6 6 7 7 import 'package:bonsoir/bonsoir.dart'; 8 +import 'package:connectivity_plus/connectivity_plus.dart';8 9 import 'package:flutter/foundation.dart'; 9 10 import 'package:flutter/widgets.dart'; 10 11 import 'package:path_provider/path_provider.dart' as pp; .. .. @@ -50,6 +51,8 @@ 50 51 bool _intentionalClose = false; 51 52 String? _clientId; 52 53 String? _lastDiscoveredHost; 54 + StreamSubscription? _connectivitySub;55 + List<ConnectivityResult>? _lastConnectivity;53 56 StreamSubscription? _updatesSub; 54 57 55 58 // Message deduplication .. .. @@ -103,6 +106,36 @@ 103 106 104 107 _intentionalClose = false; 105 108 _setStatus(ConnectionStatus.connecting); 109 +110 + // Start listening for network changes (WiFi↔cellular, VPN connect/disconnect)111 + _connectivitySub ??= Connectivity().onConnectivityChanged.listen((results) {112 + if (_lastConnectivity != null && !_intentionalClose) {113 + final changed = results.length != _lastConnectivity!.length ||114 + !results.every((r) => _lastConnectivity!.contains(r));115 + if (changed) {116 + _mqttLog('MQTT: network changed: ${results.map((r) => r.name).join(",")} — forcing reconnect');117 + // Force disconnect and reconnect on new network118 + final client = _client;119 + if (client != null) {120 + _intentionalClose = true;121 + client.autoReconnect = false;122 + try { client.disconnect(); } catch (_) {}123 + _client = null;124 + _updatesSub?.cancel();125 + _updatesSub = null;126 + _intentionalClose = false;127 + }128 + _lastDiscoveredHost = null; // Clear cached discovery — subnet may have changed129 + connectedHost = null;130 + connectedVia = null;131 + _setStatus(ConnectionStatus.reconnecting);132 + Future.delayed(const Duration(milliseconds: 500), () {133 + if (!_intentionalClose) connect();134 + });135 + }136 + }137 + _lastConnectivity = results;138 + });106 139 107 140 // Load trusted cert fingerprint for TOFU verification 108 141 if (_trustedFingerprint == null) await _loadTrustedFingerprint(); .. .. @@ -724,6 +757,8 @@ 724 757 _intentionalClose = true; 725 758 _updatesSub?.cancel(); 726 759 _updatesSub = null; 760 + _connectivitySub?.cancel();761 + _connectivitySub = null;727 762 728 763 try { 729 764 _client?.disconnect(); macos/Flutter/GeneratedPluginRegistrant.swift
.. .. @@ -7,6 +7,7 @@ 7 7 8 8 import audioplayers_darwin 9 9 import bonsoir_darwin 10 +import connectivity_plus10 11 import device_info_plus 11 12 import file_picker 12 13 import file_selector_macos .. .. @@ -20,6 +21,7 @@ 20 21 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 21 22 AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) 22 23 SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin")) 24 + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))23 25 DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) 24 26 FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) 25 27 FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) pubspec.lock
.. .. @@ -161,6 +161,22 @@ 161 161 url: "https://pub.dev" 162 162 source: hosted 163 163 version: "1.19.1" 164 + connectivity_plus:165 + dependency: "direct main"166 + description:167 + name: connectivity_plus168 + sha256: b8fe52979ff12432ecf8f0abf6ff70410b1bb734be1c9e4f2f86807ad7166c79169 + url: "https://pub.dev"170 + source: hosted171 + version: "7.1.0"172 + connectivity_plus_platform_interface:173 + dependency: transitive174 + description:175 + name: connectivity_plus_platform_interface176 + sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"177 + url: "https://pub.dev"178 + source: hosted179 + version: "2.1.0"164 180 cross_file: 165 181 dependency: transitive 166 182 description: .. .. @@ -608,6 +624,14 @@ 608 624 url: "https://pub.dev" 609 625 source: hosted 610 626 version: "0.17.6" 627 + nm:628 + dependency: transitive629 + description:630 + name: nm631 + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"632 + url: "https://pub.dev"633 + source: hosted634 + version: "0.5.0"611 635 objective_c: 612 636 dependency: transitive 613 637 description: pubspec.yaml
.. .. @@ -33,6 +33,7 @@ 33 33 crypto: ^3.0.7 34 34 push: ^3.3.3 35 35 flutter_app_badger: ^1.5.0 36 + connectivity_plus: ^7.1.036 37 37 38 dev_dependencies: 38 39 flutter_test: windows/flutter/generated_plugin_registrant.cc
.. .. @@ -8,6 +8,7 @@ 8 8 9 9 #include <audioplayers_windows/audioplayers_windows_plugin.h> 10 10 #include <bonsoir_windows/bonsoir_windows_plugin_c_api.h> 11 +#include <connectivity_plus/connectivity_plus_windows_plugin.h>11 12 #include <file_selector_windows/file_selector_windows.h> 12 13 #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> 13 14 #include <permission_handler_windows/permission_handler_windows_plugin.h> .. .. @@ -20,6 +21,8 @@ 20 21 registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); 21 22 BonsoirWindowsPluginCApiRegisterWithRegistrar( 22 23 registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi")); 24 + ConnectivityPlusWindowsPluginRegisterWithRegistrar(25 + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));23 26 FileSelectorWindowsRegisterWithRegistrar( 24 27 registry->GetRegistrarForPlugin("FileSelectorWindows")); 25 28 FlutterSecureStorageWindowsPluginRegisterWithRegistrar( windows/flutter/generated_plugins.cmake
.. .. @@ -5,6 +5,7 @@ 5 5 list(APPEND FLUTTER_PLUGIN_LIST 6 6 audioplayers_windows 7 7 bonsoir_windows 8 + connectivity_plus8 9 file_selector_windows 9 10 flutter_secure_storage_windows 10 11 permission_handler_windows