杰理 SDK 文档中心
首页
首页
  • 概览与快速开始

    • 项目概述与能力总览
    • 快速开始与 SDK 集成
  • SDK 核心接口

    • 发送接口 BleMethod
    • 接收接口 BleEventStream
    • 数据模型与常量定义
  • 平台原生实现

    • Android 原生层
    • iOS 原生层架构
    • iOS 蓝牙管理与 SDK 运行
    • 辅助连接与广播音箱
  • OTA 升级功能

    • 升级流程与传输通道
    • 自动回连机制
    • 复用空间升级
    • 自定义命令
  • 示例应用

    • 页面结构与用户旅程
    • 设备扫描与连接管理
    • 固件文件管理
    • 升级执行与状态展示
    • 设置与调试
  • 文档与支持

    • 接口文档与收发说明
    • 调试与问题排查

升级执行与状态展示

本页介绍 JL_OTA Flutter 示例应用中固件升级(OTA)的执行流程与状态展示机制,涵盖升级页面(UpdatePage)、升级进度/结果对话框(OtaDialog)、全局连接状态管理(ConnectionStateManager)以及与原生插件、平台通道(MethodChannel)的交互方式。

Purpose and Scope

本页聚焦于示例应用(code/JL_OTA/example)中「升级执行与状态展示」这条完整能力链路:

  • 升级入口:UpdatePage 的初始化、状态装配与 UI 呈现;
  • 升级执行:通过 OtaConnectionManager、平台通道与 jl_ota 插件交互,触发固件升级;
  • 状态展示:OtaDialog 对升级进度、成功/失败结果的实时渲染,以及 DataNotifier/ConnectionStateManager 对状态的全局分发;
  • 辅助设施:OtaFileManager 文件列表管理、DialogManager 对话框装配、PopupMenuManager 菜单管理。

以下相关主题属于兄弟页面,不在本页展开:设备扫描/连接(设备页 devices_page)、固件文件下载与分享(file_share_page、download_file_dialog)、设置项(setting_page)等。本页只描述「升级执行与状态展示」本身。

Overview

UpdatePage 是示例应用的核心功能模块之一,其职责在源码注释中有明确界定(update_page.dart):

  1. 显示当前设备的连接状态与设备类型;
  2. 提供文件选择入口(本地添加、电脑传输、扫码下载);
  3. 管理 OTA 文件列表(查看、选择、删除);
  4. 执行固件升级操作;
  5. 与原生平台交互完成文件与通道操作。

整个机制的核心思路是 事件驱动 + 全局状态分发:jl_ota 插件通过 BLE 事件流(BleEventStream)上报设备连接与 OTA 状态,示例应用将这些事件写入 DataNotifier(Provider 全局状态),UpdatePage 与 OtaDialog 各自订阅并渲染。这样升级过程中的每个阶段(查文件 → 传输 → 校验 → 成功/失败)都能实时反映在 UI 上,且页面与弹窗解耦。

Architecture

flowchart TD
    subgraph sg_UI["UI Layer (example)"]
        UpdatePage["UpdatePage (升级页)"]
        OtaDialog["OtaDialog (升级状态弹窗)"]
        ConnectionStateManager["ConnectionStateManager<br/>单例 ChangeNotifier"]
    end

    subgraph sg_Managers["Manager Layer (example)"]
        OtaFileManager["OtaFileManager"]
        DialogManager["DialogManager"]
        PopupMenuManager["PopupMenuManager"]
        OtaConnectionManager["OtaConnectionManager"]
    end

    subgraph sg_Plugin["jl_ota 插件层"]
        BleEventStream["BleEventStream"]
        BleMethod["BleMethod"]
        MethodChannel["MethodChannel<br/>com.jieli.ble_plugin/methods"]
    end

    subgraph sg_Native["原生平台"]
        Native["Android/iOS BLE 与 OTA 实现"]
    end

    subgraph sg_State["全局状态"]
        DataNotifier["DataNotifier (Provider)"]
    end

    UpdatePage --> OtaFileManager
    UpdatePage --> DialogManager
    UpdatePage --> PopupMenuManager
    UpdatePage --> OtaConnectionManager
    UpdatePage -->|"otaData"| DataNotifier
    DataNotifier -->|"状态事件"| UpdatePage
    DataNotifier -->|"状态事件"| OtaDialog
    OtaDialog -->|"订阅 OTA 状态流"| BleEventStream
    OtaConnectionManager --> BleMethod
    UpdatePage -->|"setMethodCallHandler"| MethodChannel
    BleMethod --> MethodChannel
    MethodChannel --> Native
    Native -->|"BLE/OTA 事件"| BleEventStream
    ConnectionStateManager -->|"connectState"| UpdatePage

各组件职责说明:

  • UpdatePage:升级功能的入口页面(StatefulWidget),负责装配各管理器、读取全局 otaData 渲染设备状态、提供文件添加入口与升级操作入口;
  • OtaDialog:升级过程中的底部弹窗,展示进度条、当前阶段消息与成功/失败结果,并根据 OTA 状态决定是否允许用户关闭;
  • OtaConnectionManager:连接管理封装,通过 BleMethod 与插件交互,并在 OTA 数据被清理时回调 _cleanOtaData;
  • OtaFileManager:管理 OTA 文件列表与选中文件,通过回调驱动页面 setState;
  • DialogManager:统一构建各类对话框(OTA 弹窗、下载弹窗、权限确认等);
  • DataNotifier:Provider 全局状态,承载 otaData(含 KEY_STATE、KEY_DEVICE_TYPE 等键),是升级状态展示的数据中枢;
  • ConnectionStateManager:单例 ChangeNotifier,维护设备连接状态并通知监听者;
  • BleEventStream / BleMethod:jl_ota 插件暴露给 Dart 层的事件流与方法调用接口;
  • MethodChannel:名为 com.jieli.ble_plugin/methods 的平台通道,UpdatePage 通过它接收原生侧回调。

升级页面的状态模型与 UI 呈现

页面状态字段

_UpdatePageState 维护了升级页的全部运行状态(update_page.dart):

class _UpdatePageState extends State<UpdatePage> {
  late int _state = AppConstants.connectionDisconnect;
  late String _deviceType = '';
  List<Map<String, String>> _otaFileList = []; // 用于存储文件列表
  String? _selectedFilePath; // 用于存储当前选中的文件路径
  final GlobalKey _addFileButtonKey = GlobalKey();
  Map<String, dynamic>? _otaData;

  bool _isStorageEnvironmentChecked = false;
  bool _isOtaStarted = false;
  bool _isInitialDataLoaded = false;

  static const MethodChannel _methodChannel = MethodChannel(
    'com.jieli.ble_plugin/methods',
  );
  StreamSubscription<List<Map<String, String>>>? _otaFileListSubscription;
  ...
}

关键字段的设计意图:

  • _state 保存设备连接状态,默认 AppConstants.connectionDisconnect(未连接),在 build 阶段从 otaData[BleEventConstants.KEY_STATE] 同步;
  • _deviceType 保存设备类型字符串,来自 otaData[BleEventConstants.KEY_DEVICE_TYPE];当连接断开时主动清空,避免展示过期的设备类型;
  • _otaFileList 与 _selectedFilePath 分别对应 OTA 文件列表与当前选中文件,供升级时使用;
  • _isOtaStarted 用于区分「用户主动开始升级」与「页面重建读取历史状态」:只有未开始升级时,才用 otaData 回填连接状态,防止升级进行中被旧数据覆盖。

状态同步的构建期逻辑

状态同步发生在 build 方法内(update_page.dart):

@override
Widget build(BuildContext context) {
  final loc = AppLocalizations.of(context)!;
  _otaData = Provider.of<DataNotifier>(context).otaData;
  final isAndroid = AppUtil.isAndroid;

  if (!_isOtaStarted && _otaData != null && _otaData!.isNotEmpty) {
    _state = _otaData?[BleEventConstants.KEY_STATE] ?? 0;
    _deviceType = _otaData?[BleEventConstants.KEY_DEVICE_TYPE] ?? '';
    if (_state == AppConstants.connectionDisconnect) {
      _deviceType = '';
    }
  }
  ...
}

这里采用「读取 Provider 全局状态 → 回填本地字段」的模式:DataNotifier 是全局唯一的 OTA 数据源(通过 Provider.of 获取),页面每次重建都会拿到最新事件数据。之所以限定 !_isOtaStarted,是为了保证升级一旦开始,页面不再用历史事件覆盖运行中的升级状态,而是由 OtaDialog 独立订阅事件流来展示进度。

设备状态栏渲染

页面顶部展示「设备状态:已连接/未连接」与「设备类型」,样式用 HexColor 与 loc(国际化文案)驱动(update_page.dart):

Text.rich(
  TextSpan(
    children: [
      TextSpan(
        text: '${loc.deviceStatus}:',
        style: TextStyle(
          fontSize: 15,
          color: HexColor.hexColor("#242424"),
          fontWeight: FontWeight.bold,
        ),
      ),
      TextSpan(
        text: _state == AppConstants.connectionOK
            ? loc.connected
            : loc.disconnected,
        style: TextStyle(
          fontSize: 15,
          color: HexColor.hexColor("#628DFF"),
          fontWeight: FontWeight.bold,
        ),
      ),
    ],
  ),
),
SizedBox(height: 10),
Text(
  '${loc.deviceType}:$_deviceType',
  ...
),

设计意图:状态与设备类型直接取自 jl_ota 插件的事件常量(BleEventConstants.KEY_STATE / KEY_DEVICE_TYPE),保证 UI 与底层 BLE 状态始终一致;文案走 AppLocalizations 国际化,便于多语言发布。

管理器装配与平台通道

初始化流程

initState 中依次完成:装配管理器 → 初始化 → 注册平台通道回调 → 恢复上次选择的 OTA 文件路径(update_page.dart):

@override
void initState() {
  super.initState();
  _initializeManagers();
  _initialize();
  _methodChannel.setMethodCallHandler(_handleMethodCall);
  FilePreferenceManager.loadOtaPath().then((path) {
    setState(() {
      _selectedFilePath = path;
    });
  });
}
  • _initializeManagers() 创建 OtaFileManager、DialogManager、PopupMenuManager、OtaConnectionManager 四个管理器;
  • _methodChannel.setMethodCallHandler(_handleMethodCall) 让 Dart 侧可以接收原生平台主动发来的方法调用(例如文件扫描结果、设备回调);
  • FilePreferenceManager.loadOtaPath() 异步恢复上次选中的文件路径,提升用户体验(断点续选)。

管理器装配细节

void _initializeManagers() {
  _otaFileManager = OtaFileManager(
    otaFileList: _otaFileList,
    onFileListUpdated: (newList) {
      setState(() {
        _otaFileList = newList;
      });
    },
    onSelectedFileChanged: (filePath) {
      setState(() {
        if (_selectedFilePath == filePath) {
          _selectedFilePath = null;
        }
      });
    },
  );

  _dialogManager = DialogManager(
    context: context,
    methodChannel: _methodChannel,
  );

  _popupMenuManager = PopupMenuManager();

  _otaConnectionManager = OtaConnectionManager(
    onOtaDataCleaned: _cleanOtaData,
  );
}

来源:update_page.dart

装配方式体现了解耦思想:

  • 回调式文件管理:OtaFileManager 不持有 BuildContext,通过 onFileListUpdated / onSelectedFileChanged 回调让页面自行 setState,管理器可独立测试;
  • DialogManager 持有 context 与方法通道:对话框经常需要调用原生能力(如文件保存、扫码),因此把 methodChannel 注入其中;
  • OtaConnectionManager 反向回调:当原生侧清理 OTA 数据时,通过 onOtaDataCleaned: _cleanOtaData 通知页面重置状态。

升级状态对话框(OtaDialog)

状态与进度模型

OtaDialog 是升级执行期间的状态展示载体,其内部状态包括进度、OTA 阶段、当前消息与成功标志(ota_dialog.dart):

class _OtaDialogState extends State<OtaDialog> {
  int _progress = 0;
  String _otaState = BleEventConstants.KEY_STATE_START;
  final String _failureReason = '';
  String _otaType = BleEventConstants.KEY_CHECK_FILE;
  String _currentMessage = '';
  bool _isSuccess = false;
  StreamSubscription? _otaStateSubscription;
  bool _isLoadingDialogShowing = false;

  @override
  void initState() {
    super.initState();
    _startListeningToOtaState();
  }
}
  • _otaState 初始为 KEY_STATE_START(升级开始),_otaType 初始为 KEY_CHECK_FILE(查文件阶段);
  • _startListeningToOtaState() 在 initState 中订阅 BleEventStream 的 OTA 状态事件,弹窗因此能在不依赖页面 setState 的情况下实时更新;
  • 成功/失败结果分别通过 _isSuccess 与内置的 ic_success_big.png / ic_fail_big.png 图标呈现。

关闭控制(PopScope)

弹窗是否允许被关闭,取决于当前 OTA 状态(ota_dialog.dart):

// 根据状态决定是否允许关闭(成功或失败状态可以关闭)
final bool canPop =
    _otaState == BleEventConstants.KEY_STATE_IDLE ||
    _otaState == BleEventConstants.ERROR;

return PopScope(
  canPop: canPop,
  onPopInvokedWithResult: (bool didPop, Object? result) {
    if (!canPop) {
      return;
    }
  },
  ...
);

设计意图:升级过程是不可中断的关键操作。只有当 OTA 进入 KEY_STATE_IDLE(空闲/完成)或 ERROR(出错)终态时,才允许用户关闭弹窗;升级进行中按返回键会被 PopScope 拦截,避免误触导致升级中断。

视觉与布局常量

弹窗样式全部抽离到 _OtaDialogConstants 常量类(ota_dialog.dart),包括边距、圆角、进度条高度(3px)、字号、主题色(进度蓝 0xFF398BFF、文字主色 0xFF242424)等。抽常量的好处是保证各阶段(升级中/重连中/完成态)视觉一致,且后续主题调整只改一处。

全局连接状态管理(ConnectionStateManager)

ConnectionStateManager 是全局唯一的连接状态单例,基于 ChangeNotifier 实现(connection_state_manager.dart):

class ConnectionStateManager extends ChangeNotifier {
  static ConnectionStateManager? _instance;

  ConnectionStateManager._internal();

  factory ConnectionStateManager() {
    _instance ??= ConnectionStateManager._internal();
    return _instance!;
  }

  int _connectState = AppConstants.connectionFailed;

  int get connectState => _connectState;

  void updateConnectState(int newState) {
    if (_connectState != newState) {
      _connectState = newState;
      notifyListeners();
    }
  }

  static int get currentState => _instance?._connectState ?? AppConstants.connectionFailed;
}

要点:

  • 私有构造函数 + 工厂单例,保证全应用只有一份连接状态,避免多页面状态不同步;
  • updateConnectState 只在状态变化时 notifyListeners(),避免无意义的重建通知;
  • static currentState 提供静态读取入口,便于非 Widget 场景(如管理器)快速获取当前状态;
  • 默认值 AppConstants.connectionFailed,与 UpdatePage 默认的 connectionDisconnect 互补,覆盖「尚未连接」与「连接失败」两种初始场景。

Core Flow:升级执行与状态回传的完整链路

从用户选择固件文件、触发升级,到状态实时展示、最终成功/失败落地的全过程如下:

sequenceDiagram
    participant U as 用户
    participant P as UpdatePage
    participant FM as OtaFileManager
    participant OC as OtaConnectionManager
    participant PL as jl_ota 插件 (BleMethod)
    participant N as 原生 BLE/OTA
    participant ES as BleEventStream
    participant DN as DataNotifier
    participant OD as OtaDialog

    U->>P: 添加/选择固件文件
    P->>FM: 更新文件列表与选中路径
    U->>P: 点击「升级」按钮
    P->>OC: 发起 OTA 升级指令
    OC->>PL: 调用升级方法
    PL->>N: 平台通道下发指令
    N-->>ES: 上报 OTA 状态事件 (查文件/传输/校验)
    ES-->>DN: 写入全局 otaData
    DN-->>P: 页面读取 otaData (仅未开始时回填)
    DN-->>OD: 弹窗订阅状态流
    OD-->>U: 渲染进度条与阶段消息
    N-->>ES: 上报成功/失败终态
    ES-->>OD: KEY_STATE_IDLE / ERROR
    OD-->>U: 展示成功/失败结果,允许关闭

各步骤说明:

  1. 文件准备:用户在 UpdatePage 通过「本地添加 / 电脑传输 / 扫码下载」三个入口之一获得固件文件,OtaFileManager 维护列表与选中项;
  2. 触发升级:点击升级按钮后,OtaConnectionManager 通过 BleMethod(jl_ota 插件的方法封装)向原生层下发升级指令;
  3. 原生执行与事件上报:原生 BLE/OTA 实现执行查文件(KEY_CHECK_FILE)、传输、校验等阶段,并通过 BleEventStream 持续上报事件;
  4. 全局状态分发:事件写入 DataNotifier.otaData;UpdatePage 在未开始升级时据此回填连接状态与设备类型,OtaDialog 则直接订阅事件流渲染进度;
  5. 终态收敛:升级进入 KEY_STATE_IDLE(成功)或 ERROR(失败)后,弹窗展示结果图标与文案,PopScope.canPop 变为 true,用户可关闭弹窗。

Usage Examples

示例一:初始化平台通道回调并恢复上次文件路径

UpdatePage.initState 中注册 MethodChannel('com.jieli.ble_plugin/methods') 的方法处理器,并异步恢复上次选中的 OTA 文件:

@override
void initState() {
  super.initState();
  _initializeManagers();
  _initialize();
  _methodChannel.setMethodCallHandler(_handleMethodCall);
  FilePreferenceManager.loadOtaPath().then((path) {
    setState(() {
      _selectedFilePath = path;
    });
  });
}

来源:update_page.dart

示例二:根据 OTA 状态控制弹窗可关闭性

OtaDialog 用 PopScope 将「能否关闭」与「OTA 状态」绑定,只有终态(空闲或出错)才允许关闭:

// 根据状态决定是否允许关闭(成功或失败状态可以关闭)
final bool canPop =
    _otaState == BleEventConstants.KEY_STATE_IDLE ||
    _otaState == BleEventConstants.ERROR;

return PopScope(
  canPop: canPop,
  onPopInvokedWithResult: (bool didPop, Object? result) {
    if (!canPop) {
      return;
    }
  },
  ...
);

来源:ota_dialog.dart

示例三:单例连接状态更新与通知

ConnectionStateManager 提供全局唯一的连接状态,并在状态变化时通知所有监听者:

int _connectState = AppConstants.connectionFailed;

int get connectState => _connectState;

void updateConnectState(int newState) {
  if (_connectState != newState) {
    _connectState = newState;
    notifyListeners();
  }
}

static int get currentState => _instance?._connectState ?? AppConstants.connectionFailed;

来源:connection_state_manager.dart

Configuration Options

升级执行与状态展示相关的可配置项分为三类:平台通道、事件常量、UI 常量。

平台通道

选项类型默认值说明
方法通道名称const Stringcom.jieli.ble_plugin/methodsUpdatePage 与原生平台通信的 MethodChannel 名称,用于接收原生回调(见 update_page.dart)

状态常量(来自 jl_ota 插件包)

常量用途出处
BleEventConstants.KEY_STATE从 otaData 中读取连接/OTA 状态update_page.dart
BleEventConstants.KEY_DEVICE_TYPE从 otaData 中读取设备类型update_page.dart
BleEventConstants.KEY_STATE_STARTOTA 状态初始值(开始)ota_dialog.dart
BleEventConstants.KEY_CHECK_FILEOTA 类型初始值(查文件)ota_dialog.dart
BleEventConstants.KEY_STATE_IDLE升级完成(空闲)终态,允许关闭弹窗ota_dialog.dart
BleEventConstants.ERROR升级出错终态,允许关闭弹窗ota_dialog.dart
AppConstants.connectionOK设备已连接,展示「已连接」update_page.dart
AppConstants.connectionDisconnect设备未连接,展示「未连接」并清空设备类型update_page.dart
AppConstants.connectionFailedConnectionStateManager 的默认连接状态connection_state_manager.dart

OtaDialog UI 常量(_OtaDialogConstants)

常量类型默认值说明
dialogHorizontalMargindouble12.0弹窗水平外边距
dialogBottomMargindouble34.0弹窗底部外边距
borderRadiusdouble15.0弹窗圆角
progressBarHeightdouble3.0进度条高度
iconSizedouble64.0成功/失败图标尺寸
progressValueColorColor0xFF398BFF进度条填充色
successImagePathStringassets/images/ic_success_big.png成功状态图标
failImagePathStringassets/images/ic_fail_big.png失败状态图标

完整列表见 ota_dialog.dart。

API Reference

UpdatePage(StatefulWidget)

升级功能入口页面。

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

  @override
  State<UpdatePage> createState() => _UpdatePageState();
}

来源:update_page.dart

_UpdatePageState 关键方法

方法说明
initState()装配管理器、注册 MethodChannel 回调、恢复上次选中文件路径
_initializeManagers()创建 OtaFileManager、DialogManager、PopupMenuManager、OtaConnectionManager
build(context)从 Provider.of<DataNotifier> 读取 otaData,未开始升级时回填 _state/_deviceType,渲染状态栏、文件区与操作区
_handleMethodCall平台通道方法处理器(原生回调入口)
_cleanOtaDataOtaConnectionManager 清理 OTA 数据后的回调,用于重置页面状态
_checkStorageEnvironmentAndroid 下首次添加文件前检查存储环境(Android 专用)
_handleLocalAdd / _handleComputerTransfer / _handleScanDownload三种文件添加方式的处理器,由 PopupMenuManager 弹出的菜单触发

OtaDialog(StatefulWidget)

升级进度/结果弹窗。内部 _OtaDialogState 在 initState 调用 _startListeningToOtaState() 订阅 BleEventStream,通过 PopScope.canPop 控制关闭时机,按 _otaState 分别渲染「升级中」「重新连接」「完成(成功/失败)」三种内容。

ConnectionStateManager(单例 ChangeNotifier)

成员签名说明
构造factory ConnectionStateManager()返回全局唯一实例(懒加载单例)
属性int get connectState当前连接状态值
方法void updateConnectState(int newState)更新状态;仅在变化时 notifyListeners()
静态static int get currentState静态读取当前状态;实例为空时返回 AppConstants.connectionFailed

OtaFileManager 构造回调

参数类型说明
otaFileListList<Map<String, String>>文件列表引用(页面持有)
onFileListUpdatedValueChanged<List<Map<String, String>>>列表更新回调,页面内 setState
onSelectedFileChangedValueChanged<String>选中文件变化回调;若重复点击同一文件则取消选中(置 null)

见 update_page.dart。

Failure Modes, Edge Cases & Concurrency

升级过程中误触返回键

升级进行中(非 KEY_STATE_IDLE / ERROR)用户按返回键,PopScope.canPop = false,弹窗不会关闭,防止升级被打断。这是「升级不可中断」设计意图的直接体现(ota_dialog.dart)。

连接断开时的状态回填

build 阶段若 _state == AppConstants.connectionDisconnect,会强制将 _deviceType 置空(update_page.dart)。这是有意的边界处理:设备断开后,残留的设备类型信息对用户是误导,UI 上应只保留「未连接」状态。

升级中避免历史事件覆盖

build 中的状态回填受 !_isOtaStarted 保护。升级一旦开始,页面不再用 otaData 历史数据覆盖 _state/_deviceType;升级进度完全由 OtaDialog 独立订阅 BleEventStream 驱动。这避免了「页面重建 → 回填旧状态 → 与弹窗进度冲突」的并发一致性问题(update_page.dart)。

连接状态变更的竞态

ConnectionStateManager.updateConnectState 在值不变时直接返回、不通知监听者,从源头消除了「重复状态导致的多余重建」。同时 static currentState 提供无实例时的兜底值 AppConstants.connectionFailed,保证任何时刻读取都不为空(connection_state_manager.dart)。

Android 存储环境与权限

Android 平台首次点击「添加文件」时,_isStorageEnvironmentChecked == false 会先执行 _checkStorageEnvironment() 并直接 return,待用户完成存储授权后才能弹出添加菜单(update_page.dart)。页面还维护 _hasCameraPermission / _hasGalleryPermission,配合 permission_handler 处理扫码与相册权限,避免在无权限时崩溃。

失败原因与结果展示

_OtaDialogState._failureReason 预留了失败原因字段(当前为空字符串,见 ota_dialog.dart),失败态通过 _isSuccess = false 与 ic_fail_big.png 图标呈现;成功态则展示 ic_success_big.png。终态均允许用户关闭弹窗并返回页面继续操作。

Performance & Operational Notes

订阅生命周期

  • OtaDialog 在 initState 中订阅 BleEventStream(StreamSubscription? _otaStateSubscription),弹窗关闭后需要确保取消订阅,避免事件泄漏与重复渲染;升级数据清理时由 OtaConnectionManager.onOtaDataCleaned 回调页面执行 _cleanOtaData 复位。
  • UpdatePage 持有 _otaFileListSubscription(StreamSubscription<List<Map<String, String>>>),文件列表流的订阅同样需要随页面销毁释放。

渲染性能

  • 状态通过 Provider(DataNotifier)与 ChangeNotifier(ConnectionStateManager)分发,只有数据变化才触发 notifyListeners(),避免整页高频重建;
  • 进度条使用固定高度(3px)与单一填充色(0xFF398BFF),进度更新仅作用于弹窗内部组件,不牵连整个页面树。

操作提示

  • 升级操作属于关键路径,建议在真机上验证完整链路(查文件 → 传输 → 校验 → 成功/失败),并确认 BLE 连接稳定后再发起升级;
  • 升级过程中 App 切后台或系统回收资源可能导致中断,生产环境应配合 BleEventConstants.ERROR 终态做断点恢复或失败引导。

Extension Points

  • 文件来源扩展:UpdatePage 的「本地添加 / 电脑传输 / 扫码下载」由 PopupMenuManager.showAddFilePopupMenu 统一弹出,新增来源只需扩展菜单项并实现对应的 _handleXxx 处理器;
  • 对话框扩展:DialogManager 集中了所有对话框的构建(OTA 弹窗、下载弹窗、权限确认等),新增对话框只需在 DialogManager 增加一个 buildXxxDialog() 方法;
  • 文件管理回调:OtaFileManager 通过 onFileListUpdated / onSelectedFileChanged 回调与 UI 解耦,可替换为其他列表管理实现(如数据库持久化)而不改页面;
  • 连接状态消费:ConnectionStateManager 是全局单例 ChangeNotifier,任意页面或管理器都可 addListener 或静态读取 currentState,适合扩展「连接状态变化 → 全局提示」等能力。

Related Links

  • UpdatePage 源码
  • OtaDialog 源码
  • ConnectionStateManager 源码
  • OtaConnectionManager 源码
  • DialogManager 源码
  • 设备连接与发现:参见设备页(devices_page)相关文档
  • 固件文件下载与分享:参见文件相关页面(file_share_page、download_file_dialog)文档
Prev
固件文件管理
Next
设置与调试