// lib/services/network_watcher.dart import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; /// 监听本机网络接口的地址变化。 /// /// 为什么不直接依赖 mpv 自己恢复:HTTP 流是长连接,Wi-Fi 切换 / 移动数据切换 /// 会换掉出口 IP,旧连接直接失效。mpv 不一定会重连,通常表现为 /// "进度不动、缓冲一直转"。所以在网络变化时由上层主动重开当前歌曲。 /// /// 实现方式:每 10s 枚举一次 IPv4 地址,签名变化即视为网络切换。 /// 相比平台 Channel 的方案,它不需要新增权限或插件, /// 且能覆盖"同一个 Wi-Fi 换了 AP / 换了出口"这类 transport 类型不变的情况。 class NetworkWatcher { static final NetworkWatcher _instance = NetworkWatcher._internal(); factory NetworkWatcher() => _instance; NetworkWatcher._internal(); Timer? _timer; String? _lastSignature; bool _ticking = false; /// 网络签名变化时回调(已在内部做过去抖) void Function()? onChanged; void start({Duration interval = const Duration(seconds: 3)}) { if (_timer != null) return; _timer = Timer.periodic(interval, (_) => _tick()); _tick(); debugPrint('🌐 [NetworkWatcher] 已启动(每 ${interval.inSeconds}s 检查一次)'); } void stop() { _timer?.cancel(); _timer = null; _lastSignature = null; debugPrint('🌐 [NetworkWatcher] 已停止'); } Future _tick() async { if (_ticking) return; _ticking = true; try { final interfaces = await NetworkInterface.list( type: InternetAddressType.IPv4, includeLoopback: false, ); final signature = interfaces .map((i) => '${i.name}:${i.addresses.map((a) => a.address).join(',')}') .join('|'); final previous = _lastSignature; _lastSignature = signature; // 首次只记录基线,不触发 if (previous != null && previous != signature) { debugPrint('🌐 [NetworkWatcher] 检测到网络变化'); onChanged?.call(); } } catch (e) { debugPrint('⚠️ [NetworkWatcher] 枚举网络接口失败: $e'); } finally { _ticking = false; } } }