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

跨页面调用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为例:

  1. 创建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");
    }
  }
}
  1. 在main.dart中注册Provider:
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => BluetoothProvider(),
      child: const MyApp(),
    ),
  );
}
  1. 在DataCollectionPage中建立连接:
final bluetoothProvider = Provider.of<BluetoothProvider>(context, listen: false);

@override
void initState() {
  super.initState();
  bluetoothProvider.establishConnection(widget.server!.address);
}
  1. 在DataCollectionTimer页面调用方法:
final bluetoothProvider = Provider.of<BluetoothProvider>(context, listen: false);

// 触发采集的位置
bluetoothProvider.startDataCollection("你的启动指令");

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 03:36:31