
Выбор инструментов и архитектура
Для кросс-платформенной разработки взял Flutter. Для сканирования Wi-Fi пришлось использовать два разныx пакета: flutter_wifi_scan для Android и wifi_info_plugin для iOS. Android даёт полный доступ к списку точек доступа с BSSID, SSID, уровнем сигнала и частотой. iOS же с iOS 9 ограничивает фоновое сканирование — приложение может получить информацию только о текущей сети. Пришлось добавить отдельный поток для периодического опроса и агрегации данных.
Архитектура модульная: отдельный слой для сбора данных (платформенные адаптеры), слой обработки (фильтрация, усреднение RSSI) и слой визуализации (графики на CustomPainter). Для хранения истории использовал SQLite через пакет sqflite.
Важное замечание: на Android нужно добавить разрешения ACCESS_FINE_LOCATION и ACCESS_WIFI_STATE, а на iOS — Capability Access WiFi Information. Без них сканирование не работает.
Сбор данных о сети на Android
На Android самый простой путь — использовать WifiManager через MethodChannel. Вот пример кода для запуска сканирования и получения результатов:
import 'package:flutter/services.dart';
class AndroidWifiScanner {
static const platform = MethodChannel('wifi_scanner');
Future
На стороне Android (Kotlin) обрабатываем запрос:
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "startScan") {
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val scanResults = wifiManager.scanResults
val list = scanResults.map { scanResult ->
mapOf(
"ssid" to scanResult.SSID,
"bssid" to scanResult.BSSID,
"level" to scanResult.level,
"frequency" to scanResult.frequency
)
}
result.success(list)
context.unregisterReceiver(this)
}
}
context.registerReceiver(receiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
wifiManager.startScan()
}
}Сбор данных на iOS: ограничения и обход
iOS не даёт полного сканирования, но можно получить информацию о текущей точке доступа через NEHotspotNetwork. Для этого используем wifi_info_plugin:
import 'package:wifi_info_plugin/wifi_info_plugin.dart';
class IosWifiScanner {
Future<Map<String, dynamic>> getCurrentWifiInfo() async {
try {
var wifiInfo = await WifiInfoPlugin.wifiInfo;
return {
'ssid': wifiInfo.ssid ?? '',
'bssid': wifiInfo.bssid ?? '',
'level': wifiInfo.level ?? 0,
};
} catch (e) {
print('Ошибка получения информации о Wi-Fi: $e');
return {};
}
}
}Чтобы хоть как-то оценить обстановку, пришлось добавить механизм перемещения пользователя и логирования уровня сигнала в разных точках. По сути, iOS-версия стала «следопытом», а Android — полноценным сканером.
Сравнение возможностей Android и iOS
| Параметр | Android | iOS |
|---|---|---|
| Доступ к списку точек доступа | Да (полный) | Нет (только текущая) |
| BSSID | Да | Да (для текущей) |
| Уровень сигнала (RSSI) | Да | Да (для текущей) |
| Частота/канал | Да | Нет |
| Фоновое сканирование | Да | Только в foreground |
| Разрешения | ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE | Access WiFi Information + Location |
Визуализация и анализ
Данные выводились в виде линии уровня сигнала по времени и карты точек доступа с цветовой индикацией (зелёный — сильный сигнал, красный — слабый). Для отрисовки графиков использовал CustomPainter. Также реализовал фильтр по SSID и сортировку по уровню сигнала. На Android можно было построить тепловую карту каналов — это позволило выявить перегруженный канал 6 в офисе.
Совет: для Android используйте метод getScanResults только после того, как сканирование завершилось. Иначе получите пустой список. Я повесил BroadcastReceiver на SCAN_RESULTS_AVAILABLE_ACTION.
Практические советы и результаты
- Для минимизации нагрузки на батарею делайте паузу между сканированиями не менее 10 секунд.
- На iOS не пытайтесь получить список сетей — только текущую. Смиритесь.
- Используйте SQLite для хранения истории — это позволяет увидеть динамику изменения сигнала.
- Добавьте экспорт данных в CSV, чтобы передать сисадмину.
Личное наблюдение автора: когда я запустил сканер на Android в офисе, сразу увидел, что на канале 6 работают 8 точек доступа — очевидная причина деградации. Перенос сети на канал 11 решил проблему. iOS-версия оказалась менее полезной, но её можно использовать для приёмочного тестирования зоны покрытия.
Заключение
Создать Wi-Fi сканер под обе платформы реально, но придётся учитывать ограничения iOS. Flutter позволил переиспользовать 80% кода, а платформенные адаптеры сделали остальное. Если нужно простое приложение для диагностики — берите готовые библиотеки, но если хотите глубже разобраться в сетевом стеке — пишите сами.