跨页面调用startDataCollection报Null check空值错误的解决咨询
问题:跨页面调用蓝牙连接相关函数触发空指针错误
我有一个DataCollectionPage类,在其initState方法中实现了establishConnection函数用于建立蓝牙连接:
void establishConnection (){ BluetoothConnection.toAddress(widget.server!.address).then((connection) { Utils.showSnackBar2("Successfully connected to your device"); this.connection = connection; setState(() { isConnecting = false; isDisconnecting = false; }); this.connection!.input!.listen(_onDataReceived).onDone(() { if (isDisconnecting) { Utils.showSnackBar2("Device Disconnected!!"); } else { Utils.showSnackBar2("Device Disconnected!!"); } if (mounted) { setState(() {}); } }); }).catchError((error) { Utils.showSnackBar("Connection failed, please try again later"); Navigator.push( context, MaterialPageRoute(builder: (context) => const BluetoothConnectionTask()), ); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Error occurred while connecting'), content: const Text( "Connection failed. Please Try connecting to your device again"), actions: <Widget>[ TextButton( child: const Text("Close",style: TextStyle(color: Colors.black),), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }); }
同一类中还有startDataCollection函数,用于发送采集指令:
void startDataCollection() async { startFlag = startFlag.trim(); try { List<int> list = startFlag.codeUnits; Uint8List bytes = Uint8List.fromList(list); connection!.output.add(bytes); await connection!.output.allSent; } catch (e) { print(e); } }
在DataCollectionPage内部调用该函数正常,但从另一个页面DataCollectionTimer调用时,出现错误:
Null check operator used on a null value
错误指向startDataCollection函数中的connection变量。实际蓝牙连接已建立且处于有效状态,但跨页面调用时connection为空。请问如何正确从其他页面调用startDataCollection函数?
解决方案
1. 全局单例管理蓝牙连接
创建独立的蓝牙连接管理类,用单例模式保存连接实例,确保所有页面共享同一个连接对象,这是最直接的解决方式。
示例代码:
class BluetoothConnectionManager { // 单例实例 static final BluetoothConnectionManager _instance = BluetoothConnectionManager._internal(); BluetoothConnection? _connection; factory BluetoothConnectionManager() => _instance; BluetoothConnectionManager._internal(); // 建立连接的方法 Future<void> establishConnection(String address) async { try { _connection = await BluetoothConnection.toAddress(address); Utils.showSnackBar2("成功连接设备"); // 监听数据接收与断开事件 _connection?.input?.listen(_onDataReceived).onDone(() { Utils.showSnackBar2("设备已断开连接"); _connection = null; }); } catch (error) { Utils.showSnackBar("连接失败,请稍后重试"); // 原错误处理逻辑(跳转页面、弹窗)可移到这里 } } void _onDataReceived(Uint8List data) { // 原有的数据接收处理逻辑 } // 封装发送采集指令的方法 Future<void> sendStartCommand(String startFlag) async { if (_connection == null) { Utils.showSnackBar("未建立蓝牙连接"); return; } try { startFlag = startFlag.trim(); final bytes = Uint8List.fromList(startFlag.codeUnits); _connection!.output.add(bytes); await _connection!.output.allSent; } catch (e) { print("发送指令失败:$e"); } } }
修改原页面代码:
- 在
DataCollectionPage的initState中,调用单例的establishConnection建立连接,不再自行保存connection变量。 - 原
startDataCollection方法改为调用单例的sendStartCommand。 - 在
DataCollectionTimer页面,直接调用单例方法即可:
// DataCollectionTimer中需要触发采集的位置 BluetoothConnectionManager().sendStartCommand("你的启动指令");
2. 通过回调函数传递方法
如果不需要全局管理,可在跳转DataCollectionTimer时,把startDataCollection作为回调参数传递。
修改DataCollectionPage中的跳转逻辑:
Navigator.push( context, MaterialPageRoute( builder: (context) => DataCollectionTimer( onStartCollection: startDataCollection, ), ), );
修改DataCollectionTimer类定义:
class DataCollectionTimer extends StatelessWidget { final VoidCallback onStartCollection; const DataCollectionTimer({super.key, required this.onStartCollection}); void _triggerStart() { onStartCollection(); } @override Widget build(BuildContext context) { return ElevatedButton( onPressed: _triggerStart, child: const Text("开始数据采集"), ); } }
⚠️ 注意:这种方式要求DataCollectionPage在内存中未被销毁(不要用pushReplacement跳转),否则会丢失连接状态。
3. 使用状态管理框架(如Provider、GetX)
如果项目已使用状态管理框架,可将蓝牙连接实例存入全局状态,便于跨页面访问。以Provider为例:
- 创建
ChangeNotifier类管理蓝牙状态:
class BluetoothProvider extends ChangeNotifier { BluetoothConnection? connection; Future<void> establishConnection(String address) async { try { connection = await BluetoothConnection.toAddress(address); Utils.showSnackBar2("成功连接设备"); connection?.input?.listen(_onDataReceived).onDone(() { Utils.showSnackBar2("设备已断开连接"); connection = null; notifyListeners(); }); notifyListeners(); } catch (error) { Utils.showSnackBar("连接失败,请稍后重试"); } } void _onDataReceived(Uint8List data) { // 数据处理逻辑 } Future<void> startDataCollection(String startFlag) async { if (connection == null) { Utils.showSnackBar("未建立蓝牙连接"); return; } try { startFlag = startFlag.trim(); final bytes = Uint8List.fromList(startFlag.codeUnits); connection!.output.add(bytes); await connection!.output.allSent; } catch (e) { print("发送指令失败:$e"); } } }
- 在
main.dart中注册Provider:
void main() { runApp( ChangeNotifierProvider( create: (context) => BluetoothProvider(), child: const MyApp(), ), ); }
- 在
DataCollectionPage中建立连接:
final bluetoothProvider = Provider.of<BluetoothProvider>(context, listen: false); @override void initState() { super.initState(); bluetoothProvider.establishConnection(widget.server!.address); }
- 在
DataCollectionTimer页面调用方法:
final bluetoothProvider = Provider.of<BluetoothProvider>(context, listen: false); // 触发采集的位置 bluetoothProvider.startDataCollection("你的启动指令");
内容的提问来源于stack exchange,提问作者taha khamis
相关产品推荐
相关产品推荐

