Flutter全局监听蓝牙状态并实现无Context页面跳转方案咨询
全局监听蓝牙状态并自动跳转的实现方案
核心思路
通过全局导航键(GlobalKey
步骤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
相关产品推荐
相关产品推荐

