Flutter无线网络扫描插件wifi_scan的使用
Flutter无线网络扫描插件wifi_scan的使用
简介
wifi_scan
是一个允许Flutter应用程序扫描附近可见WiFi接入点的插件。该插件是 WiFiFlutter 套件的一部分,为Flutter提供各种WiFi服务。
平台支持
Platform | Status | Min. Version | API | Notes |
---|---|---|---|---|
Android | ✔️ | 16 (J) | Scan related APIs in WifiManager |
For SDK >= 26(O) scans are throttled. |
iOS | ✔️ | 9.0 | No public API, requires special entitlements from Apple | Stub implementation with sane returns. |
使用方法
入口点
插件的入口点是单例实例 WiFiScan.instance
。
开始扫描
你可以通过 WiFiScan.startScan
API 触发完整的WiFi扫描:
void _startScan() async {
// 检查平台支持和必要的要求
final can = await WiFiScan.instance.canStartScan(askPermissions: true);
switch(can) {
case CanStartScan.yes:
// 异步启动完整扫描
final isScanning = await WiFiScan.instance.startScan();
//...
break;
// ... 处理其他 `CanStartScan` 值的情况
}
}
更多详情请参阅 WiFiScan.startScan,WiFiScan.canStartScan 和 CanStartScan 的文档。
获取扫描结果
你可以通过 WiFiScan.getScannedResults
API 获取扫描结果:
void _getScannedResults() async {
// 检查平台支持和必要的要求
final can = await WiFiScan.instance.canGetScannedResults(askPermissions: true);
switch(can) {
case CanGetScannedResults.yes:
// 获取扫描结果
final accessPoints = await WiFiScan.instance.getScannedResults();
// ...
break;
// ... 处理其他 `CanGetScannedResults` 值的情况
}
}
注意: getScannedResults
API 可以独立于 startScan
API 使用。这会返回最新的可用扫描结果。
更多详情请参阅 WiFiScan.getScannedResults,WiFiAccessPoint,WiFiScan.canGetScannedResults 和 CanGetScannedResults 的文档。
获取扫描结果的通知
你可以通过 WiFiScan.onScannedResultsAvailable
API 在有新的扫描结果时获取通知:
// 初始化 accessPoints 和 subscription
List<WiFiAccessPoint> accessPoints = [];
StreamSubscription<List<WiFiAccessPoint>>? subscription;
void _startListeningToScannedResults() async {
// 检查平台支持和必要的要求
final can = await WiFiScan.instance.canGetScannedResults(askPermissions: true);
switch(can) {
case CanGetScannedResults.yes:
// 监听 onScannedResultsAvailable 流
subscription = WiFiScan.instance.onScannedResultsAvailable.listen((results) {
// 更新 accessPoints
setState(() => accessPoints = results);
});
// ...
break;
// ... 处理其他 `CanGetScannedResults` 值的情况
}
}
// 确保在完成后取消订阅
@override
dispose() {
super.dispose();
subscription?.cancel();
}
此外,WiFiScan.onScannedResultsAvailable
API 还可以与 Flutter 的 StreamBuilder 小部件一起使用。
注意: onScannedResultsAvailable
API 可以独立于 startScan
API 使用。通知也可以是平台或其它应用执行完整扫描的结果。
更多详情请参阅 WiFiScan.onScannedResultsAvailable,WiFiAccessPoint,WiFiScan.canGetScannedResults 和 CanGetScannedResults 的文档。
示例代码
以下是一个完整的示例代码,展示了如何使用 wifi_scan
插件进行WiFi扫描:
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:wifi_scan/wifi_scan.dart';
void main() {
runApp(const MyApp());
}
/// 示例应用程序用于 wifi_scan 插件。
class MyApp extends StatefulWidget {
/// 默认构造函数。
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<WiFiAccessPoint> accessPoints = <WiFiAccessPoint>[];
StreamSubscription<List<WiFiAccessPoint>>? subscription;
bool shouldCheckCan = true;
bool get isStreaming => subscription != null;
Future<void> _startScan(BuildContext context) async {
// 检查是否可以开始扫描
if (shouldCheckCan) {
// 检查是否可以开始扫描
final can = await WiFiScan.instance.canStartScan();
// 如果不可以,则显示错误信息
if (can != CanStartScan.yes) {
if (context.mounted) kShowSnackBar(context, "Cannot start scan: $can");
return;
}
}
// 调用 startScan API
final result = await WiFiScan.instance.startScan();
if (context.mounted) kShowSnackBar(context, "startScan: $result");
// 重置访问点。
setState(() => accessPoints = <WiFiAccessPoint>[]);
}
Future<bool> _canGetScannedResults(BuildContext context) async {
if (shouldCheckCan) {
// 检查是否可以获取扫描结果
final can = await WiFiScan.instance.canGetScannedResults();
// 如果不可以,则显示错误信息
if (can != CanGetScannedResults.yes) {
if (context.mounted) {
kShowSnackBar(context, "Cannot get scanned results: $can");
}
accessPoints = <WiFiAccessPoint>[];
return false;
}
}
return true;
}
Future<void> _getScannedResults(BuildContext context) async {
if (await _canGetScannedResults(context)) {
// 获取扫描结果
final results = await WiFiScan.instance.getScannedResults();
setState(() => accessPoints = results);
}
}
Future<void> _startListeningToScanResults(BuildContext context) async {
if (await _canGetScannedResults(context)) {
subscription = WiFiScan.instance.onScannedResultsAvailable
.listen((result) => setState(() => accessPoints = result));
}
}
void _stopListeningToScanResults() {
subscription?.cancel();
setState(() => subscription = null);
}
@override
void dispose() {
super.dispose();
// 停止扫描结果的订阅
_stopListeningToScanResults();
}
// 构建带标签的切换按钮
Widget _buildToggle({
String? label,
bool value = false,
ValueChanged<bool>? onChanged,
Color? activeColor,
}) =>
Row(
children: [
if (label != null) Text(label),
Switch(value: value, onChanged: onChanged, activeColor: activeColor),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
actions: [
_buildToggle(
label: "Check can?",
value: shouldCheckCan,
onChanged: (v) => setState(() => shouldCheckCan = v),
activeColor: Colors.purple)
],
),
body: Builder(
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
icon: const Icon(Icons.perm_scan_wifi),
label: const Text('SCAN'),
onPressed: () async => _startScan(context),
),
ElevatedButton.icon(
icon: const Icon(Icons.refresh),
label: const Text('GET'),
onPressed: () async => _getScannedResults(context),
),
_buildToggle(
label: "STREAM",
value: isStreaming,
onChanged: (shouldStream) async => shouldStream
? await _startListeningToScanResults(context)
: _stopListeningToScanResults(),
),
],
),
const Divider(),
Flexible(
child: Center(
child: accessPoints.isEmpty
? const Text("NO SCANNED RESULTS")
: ListView.builder(
itemCount: accessPoints.length,
itemBuilder: (context, i) =>
_AccessPointTile(accessPoint: accessPoints[i])),
),
),
],
),
),
),
),
);
}
}
/// 显示接入点的瓷砖。
///
/// 点击时可以查看详细信息。
class _AccessPointTile extends StatelessWidget {
final WiFiAccessPoint accessPoint;
const _AccessPointTile({Key? key, required this.accessPoint})
: super(key: key);
// 构建可以显示信息的行,基于标签:值对。
Widget _buildInfo(String label, dynamic value) => Container(
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.grey)),
),
child: Row(
children: [
Text(
"$label: ",
style: const TextStyle(fontWeight: FontWeight.bold),
),
Expanded(child: Text(value.toString()))
],
),
);
@override
Widget build(BuildContext context) {
final title = accessPoint.ssid.isNotEmpty ? accessPoint.ssid : "**EMPTY**";
final signalIcon = accessPoint.level >= -80
? Icons.signal_wifi_4_bar
: Icons.signal_wifi_0_bar;
return ListTile(
visualDensity: VisualDensity.compact,
leading: Icon(signalIcon),
title: Text(title),
subtitle: Text(accessPoint.capabilities),
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildInfo("BSSID", accessPoint.bssid),
_buildInfo("Capability", accessPoint.capabilities),
_buildInfo("frequency", "${accessPoint.frequency}MHz"),
_buildInfo("level", accessPoint.level),
_buildInfo("standard", accessPoint.standard),
_buildInfo(
"centerFrequency0", "${accessPoint.centerFrequency0}MHz"),
_buildInfo(
"centerFrequency1", "${accessPoint.centerFrequency1}MHz"),
_buildInfo("channelWidth", accessPoint.channelWidth),
_buildInfo("isPasspoint", accessPoint.isPasspoint),
_buildInfo(
"operatorFriendlyName", accessPoint.operatorFriendlyName),
_buildInfo("venueName", accessPoint.venueName),
_buildInfo("is80211mcResponder", accessPoint.is80211mcResponder),
],
),
),
),
);
}
}
/// 显示Snackbar。
void kShowSnackBar(BuildContext context, String message) {
if (kDebugMode) print(message);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
资源
问题和反馈
请在我们的 issue tracker 中提交 WiFiFlutter 特定的问题、错误或功能请求。
要贡献更改,请查看插件的 checklist for 1.0,我们的 contribution guide 并打开一个 pull request。
贡献者 ✨
感谢这些 💖 人 的贡献。
该项目遵循 all-contributors 规范。欢迎任何形式的贡献!
更多关于Flutter无线网络扫描插件wifi_scan的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter无线网络扫描插件wifi_scan的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用wifi_scan
插件来进行无线网络扫描的示例代码。这个插件允许你获取附近可用的Wi-Fi网络列表。
步骤 1: 添加依赖
首先,你需要在pubspec.yaml
文件中添加wifi_scan
插件的依赖:
dependencies:
flutter:
sdk: flutter
wifi_scan: ^0.4.0 # 请确保使用最新版本
然后,运行以下命令来安装依赖:
flutter pub get
步骤 2: 请求权限
在Android平台上,你需要在AndroidManifest.xml
文件中添加必要的权限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 其他配置 -->
</manifest>
对于iOS平台,你需要在Info.plist
文件中添加NSLocationWhenInUseUsageDescription
和NSLocationAlwaysUsageDescription
权限描述。
步骤 3: 编写Flutter代码
接下来,在你的Flutter项目中编写代码以使用wifi_scan
插件。
import 'package:flutter/material.dart';
import 'package:wifi_scan/wifi_scan.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Wi-Fi Scanner'),
),
body: WiFiScanner(),
),
);
}
}
class WiFiScanner extends StatefulWidget {
@override
_WiFiScannerState createState() => _WiFiScannerState();
}
class _WiFiScannerState extends State<WiFiScanner> {
List<WifiScanResult> wifiList = [];
@override
void initState() {
super.initState();
_requestPermissions();
}
Future<void> _requestPermissions() async {
var status = await Permission.locationWhenInUse.status;
if (!status.isGranted) {
Map<Permission, PermissionStatus> statuses = await Permission.requestMultiple([
Permission.locationWhenInUse,
Permission.accessWifiState,
]);
if (statuses[Permission.locationWhenInUse]?.isGranted == true &&
statuses[Permission.accessWifiState]?.isGranted == true) {
_scanWifi();
} else {
// 处理权限被拒绝的情况
}
} else {
_scanWifi();
}
}
Future<void> _scanWifi() async {
try {
bool isLocationEnabled = await WifiScan.isLocationEnabled();
if (!isLocationEnabled) {
// 提示用户启用位置服务
return;
}
List<WifiScanResult> results = await WifiScan.scanResults();
setState(() {
wifiList = results;
});
} catch (e) {
print("Error scanning for Wi-Fi: $e");
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Available Wi-Fi Networks',
style: TextStyle(fontSize: 24),
),
SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: wifiList.length,
itemBuilder: (context, index) {
WifiScanResult result = wifiList[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
'SSID: ${result.ssid} | BSSID: ${result.bssid} | Level: ${result.level}',
style: TextStyle(fontSize: 18),
),
);
},
),
),
],
),
);
}
}
解释
-
依赖管理:首先,我们添加了
wifi_scan
和permission_handler
依赖,以便在Flutter应用中处理Wi-Fi扫描和权限请求。 -
权限请求:在
_requestPermissions
方法中,我们请求位置权限和Wi-Fi访问权限。这些权限是扫描Wi-Fi网络所必需的。 -
Wi-Fi扫描:在
_scanWifi
方法中,我们检查位置服务是否已启用,然后调用WifiScan.scanResults()
方法来获取附近的Wi-Fi网络列表。 -
UI展示:在UI部分,我们使用
ListView.builder
来展示扫描到的Wi-Fi网络列表。
这样,你就可以在Flutter应用中实现无线网络扫描功能了。确保在实际部署中处理各种异常和权限被拒绝的情况,以提供更好的用户体验。