flet-camera `CameraStateEvent` 详解:相机状态事件字段、触发机制与实战用法
2026/9/22 11:05:57 网站建设 项目流程

flet-cameraCameraStateEvent详解:相机状态事件字段、触发机制与实战用法

【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet

CameraStateEvent是 flet-camera 扩展包中用于承载相机控制器状态快照的核心事件类型。它通过Camera控件的on_state_change回调触发,把底层相机「是否已初始化、是否在录制、是否在拍照、闪光灯/曝光/对焦模式、预览尺寸、错误状态」等全量运行状态一次性同步给 Python 侧应用。读完本文,你将掌握该事件的全部字段语义、事件在 Python 与 Flutter 两端之间的触发链路,并能在自己的 Flet 相机应用中基于它构建状态驱动的 UI。

本文以 camerastateevent.md 这一 API 参考页为骨架,并结合 flet-camera 包源码与官方示例展开。该参考页由 Crocodocs 工具从 types.py 中CameraStateEvent的 docstring 自动生成,因此字段定义以源码为准。

一、事件概览:CameraStateEvent 是什么

在 flet-camera 中,Camera控件(见 Camera 控件文档)负责相机预览、拍照、录像与图像流。相机控制器的状态是不断变化的:初始化完成、开始录像、暂停预览、切换对焦模式……这些变化需要一个统一的事件通道通知到 Python 应用,CameraStateEvent就是这条通道上的「状态快照信封」。

从源码看,CameraStateEvent是一个继承自ft.Event["Camera"]的 dataclass,事件源类型被标注为Camera

@dataclass class CameraStateEvent(ft.Event["Camera"]): """Snapshot of the camera controller state.""" ...

出处:types.py。

Camera控件上通过on_state_change属性订阅该事件(camera.py):

on_state_change: Optional[ft.EventHandler[CameraStateEvent]] = None """Fires when the camera controller state changes."""

事件本身不携带新旧状态对比,只携带「当前这一刻」的完整状态快照。应用侧需要自行保存上次状态并做差异判断(官方示例正是这么做的)。

二、字段全解析:21 个状态字段的语义与取值

CameraStateEvent包含 21 个字段,覆盖了相机运行时的全部关键状态。下表按「状态标志位 / 方向信息 / 模式与能力 / 预览与元数据 / 错误与设备信息」五组归类(字段定义与注释源自 types.py):

分组字段类型默认值含义
状态标志位is_initializedbool必填控制器是否已完成初始化
状态标志位is_recording_videobool必填是否正在进行视频录制
状态标志位is_recording_pausedbool必填当前活动录制是否处于暂停状态
状态标志位is_taking_picturebool必填是否正在执行拍照(静态采集进行中)
状态标志位is_streaming_imagesbool必填图像流式传输是否正在运行
状态标志位is_preview_pausedbool必填预览是否已被手动暂停
状态标志位is_capture_orientation_lockedbool必填采集方向是否已锁定
方向信息device_orientationOptional[ft.DeviceOrientation]None当前设备 UI 方向
方向信息locked_capture_orientationOptional[ft.DeviceOrientation]None锁定采集方向时使用的方向
方向信息recording_orientationOptional[ft.DeviceOrientation]None当前录制使用的方向
方向信息preview_pause_orientationOptional[ft.DeviceOrientation]None预览暂停时使用的方向
模式与能力flash_modeOptional[FlashMode]None当前闪光灯模式
模式与能力exposure_modeOptional[ExposureMode]None当前曝光模式
模式与能力focus_modeOptional[FocusMode]None当前对焦模式
模式与能力exposure_point_supportedOptional[bool]None是否支持自定义曝光测光点
模式与能力focus_point_supportedOptional[bool]None是否支持自定义对焦点
预览与元数据preview_sizeOptional[CameraPreviewSize]None预览尺寸(宽高,逻辑像素)
预览与元数据aspect_ratioOptional[ft.Number]None预览宽高比
错误状态error_descriptionOptional[str]None控制器出错时的错误描述
错误状态has_errorOptional[bool]None控制器是否处于错误状态
设备信息descriptionOptional[CameraDescription]None底层相机设备描述

关键字段说明

  • is_initialized/is_taking_picture/is_recording_*/is_streaming_images/is_preview_paused:这 6 个布尔标志位是驱动 UI 的核心。官方示例用它来启用/禁用拍照、录像、暂停、推流按钮,并同步显示「正在拍照…」「录制已暂停…」等状态文本(见下文实战章节)。
  • 方向相关 4 字段device_orientation是设备实时方向;locked_capture_orientationrecording_orientationpreview_pause_orientation则对应三个相互独立的方向锁定场景。例如在 Android 上锁定采集方向后,可通过locked_capture_orientation判断当前应使用的旋转角度。
  • 模式与能力字段flash_modeexposure_modefocus_mode对应枚举FlashModeExposureModeFocusMode(定义同在 types.py),例如FlashMode.OFF/AUTO/ALWAYS/TORCHExposureMode.AUTO/LOCKEDFocusMode.AUTO/LOCKEDexposure_point_supportedfocus_point_supported则告知应用当前设备是否支持set_exposure_point()/set_focus_point()这类点按对焦/测光操作。
  • has_error+error_description:底层相机出现错误时,has_errorTrueerror_description携带人类可读的错误信息。官方示例在has_error为真时直接把error_description展示到状态文本中,这是相机应用中必须处理的兜底分支。
  • description:携带与当前控制器绑定的CameraDescription(含namelens_directionlens_typesensor_orientation)。官方示例利用它校验事件是否来自当前选中的相机,避免多个相机设备间的状态串扰。

三、触发机制:从 Flutter CameraValue 到 Python 事件

CameraStateEvent的字段并非凭空而来,而是直接映射自 Flutter 官方camera包中CameraController.value(类型为CameraValue)。flet-camera 在 Flutter 侧监听该 value 变化,再通过 Flet 的事件通道推送给 Python。

3.1 Flutter 侧:监听与序列化

CameraControl的 State 中,控制器通过controller.addListener(_onControllerValueChanged)挂上监听(camera.dart):

void _onControllerValueChanged() { final controller = _controller; if (controller == null) { return; } if (widget.control.getBool("on_state_change", false)!) { widget.control .triggerEvent("state_change", cameraValueToMap(controller.value)); } if (mounted) { setState(() {}); } }

关键点:

  1. 仅在 Python 侧订阅了on_state_change时才触发事件getBool("on_state_change", false)),没有监听者时零开销;
  2. 事件名固定为"state_change",对应 Python 侧属性on_state_change
  3. 无论是否触发事件,setState都会执行,保证预览 UI 同步刷新。

序列化由cameraValueToMap()完成(utils/camera.dart),它把CameraValue的每个属性映射为与 Python 字段一一对应的键,并removeWhere((_, v) => v == null)剔除空值。例如:

Map<String, dynamic> cameraValueToMap(CameraValue value) { return { "is_initialized": value.isInitialized, "is_recording_video": value.isRecordingVideo, "is_recording_paused": value.isRecordingPaused, "is_taking_picture": value.isTakingPicture, "is_streaming_images": value.isStreamingImages, "is_preview_paused": value.isPreviewPaused, "is_capture_orientation_locked": value.isCaptureOrientationLocked, "locked_capture_orientation": value.lockedCaptureOrientation?.name, "recording_orientation": value.recordingOrientation?.name, "device_orientation": value.deviceOrientation.name, "flash_mode": value.flashMode.name, "exposure_mode": value.exposureMode.name, "focus_mode": value.focusMode.name, "exposure_point_supported": value.exposurePointSupported, "focus_point_supported": value.focusPointSupported, "preview_pause_orientation": value.previewPauseOrientation?.name, "preview_size": sizeToMap(value.previewSize), "aspect_ratio": value.previewSize != null ? value.aspectRatio : null, "error_description": value.errorDescription, "has_error": value.hasError, "description": cameraDescriptionToMap(value.description), }..removeWhere((_, v) => v == null); }

可以看到preview_size被序列化为{"width": ..., "height": ...}字典,这正是 Python 侧CameraPreviewSize@ft.value类)反序列化的输入;description则复用cameraDescriptionToMap()序列化为CameraDescription。方向、模式、闪光灯等枚举值统一以.name字符串传输。

3.2 Python 侧:反序列化为数据类

Python 侧事件处理由 Flet 框架将"state_change"事件载荷反序列化,自动构造CameraStateEvent实例并调用on_state_change回调。由于CameraStateEvent@dataclass且字段名与载荷键一一对应,应用侧拿到的就是一个类型安全的ft.Event子类,可以直接通过属性访问,无需手动解析字典。

3.3 触发时机总结

CameraController的语义可以推断,state_change事件在以下场景会被触发(这些操作在 camera.py 中均有对应方法,且 Flutter 侧均会改变CameraValue):

  • 调用initialize()完成初始化后(is_initialized变为True);
  • 调用take_picture()start_video_recording()/pause_video_recording()/resume_video_recording()/stop_video_recording()等拍摄操作前后;
  • 调用start_image_stream()/stop_image_stream()切换图像流时(is_streaming_images变化);
  • 调用pause_preview()/resume_preview()时(is_preview_paused变化);
  • 调用lock_capture_orientation()/unlock_capture_orientation()以及设备旋转时(方向字段变化);
  • 调用set_flash_mode()/set_exposure_mode()/set_focus_mode()等模式设置后;
  • 底层相机发生错误时(has_error/error_description被填充)。

四、实战:基于 on_state_change 构建状态驱动相机 UI

仓库自带的官方示例 camera_playground/main.py 是CameraStateEvent最完整的用法示范。其核心思想是:不依赖调用方自己维护状态,而是让事件回调成为状态源

4.1 订阅事件

async def on_state_change(e: fc.CameraStateEvent): if e.description == state.selected_camera: state.device_orientation = e.device_orientation state.is_recording = e.is_recording_video state.is_recording_paused = e.is_recording_paused state.is_streaming = e.is_streaming_images state.is_preview_paused = e.is_preview_paused sync_action_buttons() if e.has_error: status.value = f"Camera error: {e.error_description}" elif e.is_taking_picture: status.value = "Taking picture..." elif e.is_recording_paused: status.value = "Recording paused" elif e.is_recording_video: status.value = "Recording video..." elif e.is_streaming_images: status.value = "Streaming images..." elif e.is_preview_paused: status.value = "Preview paused" else: status.value = "Camera ready" page.update() preview.on_state_change = on_state_change

出处:camera_playground/main.py。

这段代码示范了三个重要实践:

  1. 设备身份校验:用e.description == state.selected_camera确认事件来自当前选中的相机,防止切换相机过程中旧控制器的事件污染 UI;
  2. 状态收敛:把 5 个核心布尔标志位同步到应用级state,并调用sync_action_buttons()统一刷新按钮的disabled/selected状态;
  3. 优先级分支:按has_error>is_taking_picture>is_recording_paused>is_recording_video>is_streaming_images>is_preview_paused的优先级输出状态文本——错误始终最优先展示。

4.2 最小可用示例

参照官方示例,一个最小化的订阅流程如下(需先按 Camera 控件文档 配置权限并安装flet-camera):

import flet as ft import flet_camera as fc async def main(page: ft.Page): cam = fc.Camera() status = ft.Text("Not initialized") async def on_state_change(e: fc.CameraStateEvent): if e.has_error: status.value = f"Error: {e.error_description}" elif e.is_recording_video: status.value = "Recording..." elif e.is_streaming_images: status.value = "Streaming..." else: status.value = f"Ready (initialized={e.is_initialized})" page.update() cam.on_state_change = on_state_change async def init_camera(e): cameras = await cam.get_available_cameras() if cameras: await cam.initialize( description=cameras[0], resolution_preset=fc.ResolutionPreset.MEDIUM, ) page.add( cam, status, ft.FilledButton("Initialize", on_click=init_camera), ) ft.run(main)

4.3 与操作按钮的联动模式

官方示例中sync_action_buttons()展示了如何用事件字段驱动控件可用性,这种「事件驱动按钮状态」的模式值得直接复用:

def sync_action_buttons(): take_photo_btn.disabled = not state.is_initialized record_btn.disabled = not state.is_initialized pause_recording_btn.disabled = not state.is_recording stream_btn.disabled = not (state.is_initialized and state.is_streaming_supported) preview_btn.disabled = not state.is_initialized

(完整版本见 camera_playground/main.py)

五、使用要点与注意事项

5.1 平台支持范围

Camera控件仅在 Android、iOS 与 Web 平台可用,before_update()会对非支持平台抛出FletUnsupportedPlatformException(camera.py)。因此基于CameraStateEvent的应用同样受此限制,桌面端(Windows/macOS/Linux)需要自行处理降级提示。

5.2 权限前置

移动端使用相机前必须请求相机权限(录制含音频的视频还需麦克风权限),否则控制器初始化会失败,并通过has_error/error_description反映到CameraStateEvent中。可参考 权限处理文档 使用PermissionHandler,或在pyproject.toml中声明预置权限包(见 发布文档 的「预定义跨平台权限包」一节)。

5.3 事件频率与空值语义

  • 事件会在控制器状态每次变化时触发,而不是周期心跳;高频场景(如图像流运行期间方向或模式变化)下回调可能较频繁,回调内应避免重量级同步操作。
  • Flutter 侧序列化时会剔除null值,Python 侧可选字段(Optional[...])在对应能力不可用或尚未确定时即为None。访问方向、模式、尺寸等可选字段前建议判空。
  • preview_sizeaspect_ratio在预览可用时才非空(Dart 侧value.previewSize != null才填充),若需布局依赖它们,请以is_initialized作为前置条件。

5.4 与 CameraImageEvent 的分工

CameraStateEvent描述「相机状态」,而 CameraImageEvent 描述「图像流中的一帧数据」(宽高、格式、编码字节、光圈、曝光时间、ISO 等),由on_stream_image回调承载。两者经常配合使用:用状态事件控制流开关、用图像事件渲染帧。官方示例即同时订阅了两个事件(camera_playground/main.py)。

六、小结

CameraStateEvent是 flet-camera 与 Flutter 底层CameraValue之间的状态桥:Dart 侧每次控制器状态变化时把全量快照序列化为字典(cameraValueToMap),Python 侧将其反序列化为 21 个字段的类型安全 dataclass 并派发给on_state_change回调。用好它,你可以在纯 Python 代码中构建出「初始化 → 拍照/录像/推流 → 错误处理」全链路状态驱动的相机界面,无需接触任何前端代码。完整的字段定义、枚举取值与配套 API 请查阅 types.py 与 Camera 控件文档。

【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询