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

Flutter全局监听蓝牙状态并实现无Context页面跳转方案咨询

全局监听蓝牙状态并自动跳转的实现方案

核心思路

通过全局导航键(GlobalKey) 摆脱对Widget Context的依赖,结合原生平台的蓝牙状态监听(通过EventChannel传递到Flutter层),实现全局范围内的蓝牙状态监控与自动路由跳转。

步骤1:定义全局导航键

创建一个全局的GlobalKey<NavigatorState>,并绑定到MaterialApp上,无需Widget Context就能直接操作导航栈:

// 全局导航键,可放在main.dart或单独的全局文件中
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

在MaterialApp中配置该键:

MaterialApp(
  navigatorKey: navigatorKey,
  home: const HomePage(), // 你的首页
  routes: {
    '/bluetooth-off': (context) => const BluetoothOffPage(), // 蓝牙关闭页面路由
  },
  // 即使不使用onGeneratedRoute,navigatorKey依然有效
);

步骤2:实现蓝牙状态监听服务

通过EventChannel对接原生平台的蓝牙状态监听,将原生的状态变化转为Flutter可订阅的Stream:

class BluetoothStateManager {
  // 定义EventChannel,与原生端保持一致的通道名
  final EventChannel _eventChannel = const EventChannel('com.yourapp/bluetooth_state');
  Stream<bool>? _bluetoothStateStream;

  // 获取蓝牙状态流:true为开启,false为关闭
  Stream<bool> get bluetoothStateStream {
    _bluetoothStateStream ??= _eventChannel.receiveBroadcastStream()
        .map((event) => event as bool);
    return _bluetoothStateStream!;
  }

  // 启动全局监听
  void startGlobalListening() {
    bluetoothStateStream.listen((isBluetoothEnabled) {
      if (!isBluetoothEnabled) {
        // 避免重复跳转:检查当前页面是否已是蓝牙关闭页面
        final currentRouteName = navigatorKey.currentState?.currentRouteName;
        if (currentRouteName != '/bluetooth-off') {
          navigatorKey.currentState?.pushNamed('/bluetooth-off');
        }
      }
    });
  }
}

步骤3:启动全局监听

在App初始化时启动蓝牙状态监听,推荐在MyApp的initState中执行(确保MaterialApp已初始化,导航键可用):

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final BluetoothStateManager _bluetoothManager = BluetoothStateManager();

  @override
  void initState() {
    super.initState();
    // 启动全局蓝牙监听
    _bluetoothManager.startGlobalListening();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      home: const HomePage(),
      routes: {
        '/bluetooth-off': (context) => const BluetoothOffPage(),
      },
    );
  }
}

步骤4:原生平台实现蓝牙状态监听

Android端(Kotlin)

在MainActivity中注册BroadcastReceiver监听蓝牙状态变化,并通过EventChannel传递给Flutter:

class MainActivity : FlutterActivity() {
    private val BLUETOOTH_CHANNEL = "com.yourapp/bluetooth_state"
    private lateinit var bluetoothAdapter: BluetoothAdapter
    private lateinit var stateReceiver: BroadcastReceiver

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        EventChannel(flutterEngine.dartExecutor.binaryMessenger, BLUETOOTH_CHANNEL).setStreamHandler(
            object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
                    // 发送初始蓝牙状态
                    events?.success(bluetoothAdapter.isEnabled)

                    // 注册广播接收器监听状态变化
                    stateReceiver = object : BroadcastReceiver() {
                        override fun onReceive(context: Context?, intent: Intent?) {
                            val state = intent?.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
                            events?.success(state == BluetoothAdapter.STATE_ON)
                        }
                    }
                    registerReceiver(stateReceiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
                }

                override fun onCancel(arguments: Any?) {
                    unregisterReceiver(stateReceiver)
                }
            }
        )
    }
}

iOS端(Swift)

在AppDelegate中使用CBCentralManager监听蓝牙状态,并通过EventChannel传递给Flutter:

import UIKit
import Flutter
import CoreBluetooth

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    private let BLUETOOTH_CHANNEL = "com.yourapp/bluetooth_state"
    private var centralManager: CBCentralManager!
    private var eventSink: FlutterEventSink?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window?.rootViewController as! FlutterViewController
        let eventChannel = FlutterEventChannel(name: BLUETOOTH_CHANNEL, binaryMessenger: controller.binaryMessenger)
        eventChannel.setStreamHandler(self)

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

extension AppDelegate: FlutterStreamHandler {
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        eventSink = events
        centralManager = CBCentralManager(delegate: self, queue: nil)
        // 发送初始状态
        events(centralManager.state == .poweredOn)
        return nil
    }

    func onCancel(withArguments arguments: Any?) -> FlutterError? {
        eventSink = nil
        centralManager = nil
        return nil
    }
}

extension AppDelegate: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        eventSink?(central.state == .poweredOn)
    }
}

关键注意点

  • 避免重复跳转:每次检测到蓝牙关闭时,先通过navigatorKey.currentState?.currentRouteName判断当前页面是否已是目标页面,防止路由栈重复入栈。
  • 权限处理:Android需要添加BLUETOOTH、BLUETOOTH_ADMIN权限(Android 12+还需BLUETOOTH_CONNECT);iOS需要在Info.plist中添加NSBluetoothAlwaysUsageDescription或NSBluetoothPeripheralUsageDescription权限。
  • 导航键有效性:确保navigatorKey已绑定到MaterialApp,且在启动监听前MaterialApp已完成初始化(所以推荐在MyApp的initState中启动监听)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 04:28:14