You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成flutter_blue_plus扫描蓝牙设备ListView无结果求助

Flutter Blue Plus扫描蓝牙设备返回空列表问题

我正尝试在Flutter应用中集成flutter_blue_plus插件,但扫描可用蓝牙设备时始终得到空列表,无法显示可配对设备。设备为运行Android 13、One UI 5.1的安卓手机,已在设置中为应用授予所有权限(含位置权限),但ListView仍无法显示扫描到的设备。

控制器类代码

import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';

class BluetoothController extends GetxController {

  FlutterBluePlus flutterBlue = FlutterBluePlus.instance;

  Future scanDevices() async {
    flutterBlue.startScan(timeout: const Duration(seconds: 5));

    print("_________________________________________________________");
    print(flutterBlue.scanResults);
    print("_________________________________________________________");

    flutterBlue.stopScan();
  }

  Stream<List<ScanResult>> get scanResults => flutterBlue.scanResults;

}

扫描设备的Widget代码

Widget _buildPopupDialog(BuildContext context) {
    return new AlertDialog(
      title: const Text('List of available devices'),
      content: GetBuilder<BluetoothController>(
          init: BluetoothController(),
          builder: (controller) {
            return Container(
              height: MediaQuery.of(context).size.height/1.3,
              width: MediaQuery.of(context).size.width,
              child: SingleChildScrollView(
                child: Column(
                  mainAxisSize: MainAxisSize.max,
                  children: [
                    GestureDetector(
                      onTap: () {
                        controller.scanDevices();
                      },
                      child: Container(
                        height: MediaQuery.of(context).size.height / 20,
                        width: MediaQuery.of(context).size.width / 3,
                        decoration: BoxDecoration(
                          color: Colors.blueAccent,
                          border: Border.all(
                            color: Colors.black,
                            width: 1,
                          ),
                          borderRadius: BorderRadius.circular(10.0),
//gradient: LinearGradient(
//colors: [Colors.indigo, Colors.blueAccent]),
                          boxShadow: const [
                            BoxShadow(
                                color: Colors.white,
                                blurRadius: 1.0,
                                offset: Offset(1.0, 1.0))
                          ],
                        ),
                        child: Center(
                          child: Text(
                            "Scan Devices!!!",
                            style: TextStyle(
                                fontSize: MediaQuery.of(context).size.width/40,
                                letterSpacing: 1,
                                fontWeight: FontWeight.w400,
                                color: Colors.black),
                          ),
                        ),
                      ),
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    StreamBuilder<List<ScanResult>>(
                      stream: controller.scanResults,
                        builder: (context, snapshot){
                        if(snapshot.hasData)
                          {
                            return ListView.builder(
                                shrinkWrap: true,
                                itemCount: snapshot.data!.length,
                                itemBuilder: (context,index){
                                  final data = snapshot.data![index];
                                  return Card(
                                    elevation: 2,
                                      child: ListTile(
                                        title: Text(data.device.name,
                                        selectionColor: Colors.black,),
                                        subtitle: Text(data.device.id.id),
                                        trailing: Text(data.rssi.toString()),
                                      ),
                                  );
                                });
                          }
                        else
                        {
                          return const Center(
                            child: Text("No devices found!!!"),
                          );
                        }
                        }
                    )
                  ],
                ),
              ),
            );
          }),
      actions: <Widget>[
        new GestureDetector(
          onTap: () {
            Navigator.of(context).pop();
          },
          child: const Text('Close'),
        ),
      ],
    );
  }

扫描时的日志输出

/ViewRootImpl@7b87c05[MainActivity](13581): ViewPostIme pointer 0
I/ViewRootImpl@7b87c05[MainActivity](13581): ViewPostIme pointer 1
I/flutter (13581): _________________________________________________________
I/flutter (13581): Instance of '_BroadcastStream<List<ScanResult>>'
I/flutter (13581): _________________________________________________________
I/BluetoothAdapter(13581): STATE_ON
I/BluetoothAdapter(13581): STATE_ON
D/BluetoothLeScanner(13581): could not find callback wrapper
I/BluetoothAdapter(13581): STATE_ON
I/BluetoothAdapter(13581): STATE_ON
I/BluetoothAdapter(13581): STATE_ON
I/BluetoothAdapter(13581): STATE_ON
D/BluetoothLeScanner(13581): Start Scan with callback
D/BluetoothLeScanner(13581): onScannerRegistered() - status=0 scannerId=11 mScannerId=0
I/BluetoothAdapter(13581): STATE_ON
I/BluetoothAdapter(13581): STATE_ON
D/BluetoothLeScanner(13581): Stop Scan with callback

Android清单文件权限配置

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

问题原因及解决办法

1. 扫描逻辑错误:立即终止扫描

scanDevices方法中调用startScan后立刻执行stopScan,扫描还未完成就被终止,根本来不及收集设备数据。startScan是异步操作,带timeout参数时会自动在超时后停止扫描,无需手动提前终止。

修复后的控制器代码:

import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';

class BluetoothController extends GetxController {

  FlutterBluePlus flutterBlue = FlutterBluePlus.instance;

  Future scanDevices() async {
    // 先停止可能存在的旧扫描
    await flutterBlue.stopScan();
    // 启动扫描,等待超时自动结束
    await flutterBlue.startScan(timeout: const Duration(seconds: 5));
  }

  Stream<List<ScanResult>> get scanResults => flutterBlue.scanResults;
}

2. Android 13权限配置不完整

Android 13要求蓝牙扫描权限声明用途,需给BLUETOOTH_SCAN添加权限标志,同时确保权限申请逻辑正确。

修改后的权限配置:

<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 若无需通过BLE获取位置,可移除以下两个权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

3. GetBuilder控制器实例不统一

每次GetBuilder构建时都会重新初始化BluetoothController,导致监听的流不是同一个实例的流。需全局初始化控制器:

修改后的Widget代码片段:

content: GetBuilder<BluetoothController>(
    init: Get.put(BluetoothController()), // 全局初始化一次
    builder: (controller) {
      // 原有代码不变
    }),

或者在页面初始化时提前注入控制器:

@override
void initState() {
  super.initState();
  Get.put(BluetoothController());
}

之后Widget中直接使用:

content: GetBuilder<BluetoothController>(
    builder: (controller) {
      // 原有代码不变
    }),

4. 设备状态检查

确保手机蓝牙已开启,且待扫描的蓝牙设备处于可被发现模式(部分设备需手动开启该模式)。


内容的提问来源于stack exchange,提问作者shashank reddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 18:42:06