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

Flutter开发:解决LateInitializationError(device未初始化)问题

解决Flutter Blue中LateInitializationError: Field 'device' has not been initialized错误

问题原因

  1. 用late声明device和_heartRate变量,但在未完成初始化前就在build方法中访问(比如device == null的判断),而late变量要求首次访问前必须完成初始化,否则直接抛出LateError。
  2. late修饰的变量不能为null,所以device == null的判断逻辑本身不成立——未初始化的late变量会直接报错,而非返回null。
  3. scanResults.firstWhere的orElse回调直接抛出异常,会导致扫描不到设备时程序崩溃,无法正常处理未找到设备的场景。

修复方案

  • 将device和_heartRate改为可空类型(去掉late,声明为BluetoothDevice?和int?),或给_heartRate设置初始值。
  • 用where(...).firstOrNull替代firstWhere(Dart 2.12+支持),避免找不到设备时抛出异常。
  • 在build方法中处理变量未初始化的情况,比如显示加载状态或提示文本。
  • 优化错误处理,避免直接抛出异常,改为打印日志或提示用户。

修改后的完整代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';

class HeartRatePage extends StatefulWidget {
  @override
  _HeartRatePageState createState() => _HeartRatePageState();
}

class _HeartRatePageState extends State<HeartRatePage> {
  FlutterBlue flutterBlue = FlutterBlue.instance;
  BluetoothDevice? device; // 改为可空类型,初始为null
  BluetoothCharacteristic? _heartRateMeasurementCharacteristic; // 同样改为可空
  StreamSubscription<List<int>>? _streamSubscription;
  int _heartRate = 0; // 给心率设置初始值,避免未初始化问题

  @override
  void initState() {
    super.initState();
    startScan();
  }

  void startScan() {
    flutterBlue.startScan(timeout: const Duration(seconds: 4));
    flutterBlue.scanResults.listen((results) {
      // 使用firstOrNull替代firstWhere,避免抛出异常
      final foundDevice = results.firstWhereOrNull(
        (r) => r.device.name == 'Heart Rate',
      );
      if (foundDevice != null && device == null) { // 避免重复连接
        connectToDevice(foundDevice.device);
      }
    });

    // 扫描结束后如果未找到设备,更新状态
    flutterBlue.isScanning.listen((isScanning) {
      if (!isScanning && device == null) {
        setState(() {});
      }
    });
  }

  @override
  void dispose() {
    _streamSubscription?.cancel();
    device?.disconnect(); // 页面销毁时断开设备连接
    super.dispose();
  }

  void connectToDevice(BluetoothDevice _device) async {
    setState(() {
      device = _device;
    });

    try {
      await device?.connect();
      final services = await device?.discoverServices();
      if (services == null) return;

      // 查找心率服务
      final heartRateService = services.firstWhereOrNull(
        (s) => s.uuid.toString() == '0000180d-0000-1000-8000-00805f9b34fb',
      );
      if (heartRateService == null) {
        print('未找到心率服务');
        return;
      }

      // 查找心率测量特征
      _heartRateMeasurementCharacteristic = heartRateService.characteristics.firstWhereOrNull(
        (c) => c.uuid.toString() == '00002a37-0000-1000-8000-00805f9b34fb',
      );
      if (_heartRateMeasurementCharacteristic == null) {
        print('未找到心率测量特征');
        return;
      }

      // 监听心率数据
      _streamSubscription = _heartRateMeasurementCharacteristic?.value.listen((value) {
        // 解析心率数据(兼容8位和16位格式)
        int heartRate;
        if ((value[0] & 0x01) == 0) {
          // 心率是8位无符号整数
          heartRate = value[1];
        } else {
          // 心率是16位无符号整数
          heartRate = (value[1] << 8) | value[2];
        }
        setState(() {
          _heartRate = heartRate;
        });
        print('当前心率: $heartRate bpm');
      });
    } on Exception catch (e) {
      print('连接或获取数据失败: $e');
      setState(() {
        device = null;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color.fromARGB(255, 245, 245, 245),
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_ios_new, color: Color(0xFF61D2A4)),
          onPressed: () => Navigator.of(context).pop(),
        ),
        title: const Text(
          'Heart Rate',
          style: TextStyle(fontFamily: 'Roboto', fontWeight: FontWeight.bold),
        ),
        centerTitle: true,
        foregroundColor: Colors.black,
        backgroundColor: Colors.white,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          if (device == null)
            const Center(child: Text('正在搜索心率设备...'))
          else
            Center(
              child: Text(
                '心率: $_heartRate bpm',
                style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
        ],
      ),
    );
  }
}

额外说明

  • 使用firstWhereOrNull需要Dart版本2.12以上,若版本较低,可用where(...).toList().firstOrNull替代。
  • 心率数据解析做了格式兼容,适配更多支持标准蓝牙心率协议的设备。
  • 页面销毁时添加了设备断开逻辑,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:45:56