Conversation
Introduces the flet-camera package with Python and Flutter bindings, including camera control, types, and documentation. Integrates flet_camera into the client app, updates dependencies, and provides a usage example for camera control in Flet Python apps.
Added detailed docstrings to enums, dataclasses, and methods in camera.py and types.py for improved API documentation and clarity. Implemented CameraDescription.to_dict() for proper serialization when passing camera descriptions. Updated method argument handling to use serialized camera descriptions and clarified return values and arguments in method docstrings.
Refactored Python camera example to use a dataclass for state management, improved event handling, and added support for image streaming and preview controls. Updated camera API to remove enum-to-value conversions and dictionary serialization, passing objects directly. Adjusted Flutter implementation to match new API, including support for image streaming and controller initialization changes.
Added a ValueKey to DropdownMenu based on the options set to ensure the menu recalculates its intrinsic width when options change, preventing incorrect sizing when new, longer options are added.
Session reconnect now uses an async task to avoid blocking the websocket receive loop with user handlers. Errors during reconnect are logged and reported to the session if possible.
Replaced print statements with logging for better output control, fixed color constant typo, removed redundant camera initialization, and set up camera retrieval on page connect. These changes streamline the example and improve maintainability.
Register flet_camera and flet_code_editor (imports and extension entries) in client main, reorder some extension registrations and tidy a desktop-mode exception formatting. Update camera package usage and utils: change resolution preset parsing to use a nullable default and non-null return, replace parseOffsetFromJson calls with parseOffset, simplify parseCameraDescription to use parseEnum with defaults, remove the old parseOffsetFromJson/parseEnum implementations, and add flet import in utils. Also add the flet_camera package (path) to pubspec.lock and apply dependency lock updates.
Register the new flet-camera extension across the repository: update sdk/python/packages/flet/pyproject.toml (extensions list), add flet-camera to the example app sdk/python/examples/apps/flet_build_test/pyproject.toml (dependencies, editable package and local path mapping), and include flet-camera in the GitHub Actions workflow (.github/workflows/ci.yml) in both the build_flet_extensions PACKAGES list and the py_publish publish loop. Also clarify the SKILL.md guidance to remind to add the extension in both CI locations.
Replace debug print with logic to render incoming camera frames by setting last_image.src and calling last_image.update(). Wrap rendering in a try/except and log exceptions with logging.exception to avoid crashing the stream handler and improve robustness.
Extend the camera example with video recording capabilities: import datetime, change dropdown label to "Select camera", and add a recorded_video_path Text control. Implement detect_video_extension to infer webm/mp4/mov from bytes and add start/pause/resume/stop recording handlers that save the recorded bytes using FilePicker with a timestamped filename. Update camera state handling to correctly reflect paused vs recording states and add UI buttons for recording controls alongside the existing photo/streaming controls.
Enhance the camera example with full recording/streaming UI and state handling: add flags to State (is_streaming, is_recording, preview/recording paused, streaming support), replace text buttons with icon buttons (take photo, record, pause recording, stream, preview), and wire up toggle helpers. Introduce sync_action_buttons to keep button states in sync, update handlers to set state and call page.update(), check for image streaming support, and handle CameraState events to reflect runtime changes. Also remove unused logging setup and tweak the dropdown label.
Deploying flet-examples with
|
| Latest commit: |
ce08d50
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://63ad2153.flet-examples.pages.dev |
| Branch Preview URL: | https://flet-camera.flet-examples.pages.dev |
Document required platform permissions for the Camera control: add iOS Info.plist keys (NSCameraUsageDescription, NSMicrophoneUsageDescription) and Android permission flags (android.permission.CAMERA, android.permission.RECORD_AUDIO). Clarifies that microphone permissions are only needed for audio-enabled video recording and links to the platform-specific permissions docs.
Deploying flet-docs with
|
| Latest commit: |
ce08d50
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://63b9db5d.flet-docs.pages.dev |
| Branch Preview URL: | https://flet-camera.flet-docs.pages.dev |
There was a problem hiding this comment.
Pull request overview
Adds a new flet-camera extension/control to the Flet Python SDK (with Flutter-side implementation), wiring it into docs, examples, CI, and the Flutter client app so camera preview/capture/streaming is available on supported platforms.
Changes:
- Introduces the new
flet-cameraPython package + Flutter extension implementation, and registers it across workspace/CI/client dependencies. - Adds API docs pages and a full Python example for the Camera control.
- Includes a couple of unrelated runtime/UI fixes: async reconnect handling in
flet-weband a DropdownMenu rebuild key to recalc intrinsic width.
Reviewed changes
Copilot reviewed 31 out of 32 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/python/pyproject.toml | Adds flet-camera to workspace dependencies + isort config. |
| sdk/python/packages/flet/pyproject.toml | Registers flet-camera as a built-in extension. |
| sdk/python/packages/flet/mkdocs.yml | Adds nav entries for Camera docs (but also duplicates Audio/AudioRecorder). |
| sdk/python/packages/flet/docs/extend/built-in-extensions.md | Lists flet-camera among built-in extensions. |
| sdk/python/packages/flet/docs/camera/index.md | New Camera control documentation page. |
| sdk/python/packages/flet/docs/camera/types/camera_description.md | New type doc stub. |
| sdk/python/packages/flet/docs/camera/types/camera_image.md | New type doc stub. |
| sdk/python/packages/flet/docs/camera/types/camera_state.md | New type doc stub. |
| sdk/python/packages/flet-web/src/flet_web/fastapi/flet_app_manager.py | Changes reconnect behavior to run session.connect() in a background task. |
| sdk/python/packages/flet-camera/src/flutter/flet_camera/pubspec.yaml | New Flutter extension package definition. |
| sdk/python/packages/flet-camera/src/flutter/flet_camera/lib/src/utils/camera.dart | New camera parsing + image encoding utilities (pixel conversion in Dart). |
| sdk/python/packages/flet-camera/src/flutter/flet_camera/lib/src/camera.dart | New Camera control widget + invoke-method handling. |
| sdk/python/packages/flet-camera/src/flutter/flet_camera/lib/src/extension.dart | Registers the Camera widget with the Flet extension. |
| sdk/python/packages/flet-camera/src/flet_camera/types.py | New Python dataclasses/enums for camera types/events. |
| sdk/python/packages/flet-camera/src/flet_camera/camera.py | New Python Camera control wrapper with async API methods. |
| sdk/python/examples/controls/camera/example_1.py | New end-to-end example showcasing preview, photos, video, streaming. |
| sdk/python/examples/apps/flet_build_test/pyproject.toml | Adds flet-camera to build-test app dependencies. |
| packages/flet/lib/src/controls/dropdown.dart | Forces DropdownMenu rebuild when options change to recalc intrinsic width. |
| client/pubspec.yaml | Adds the Flutter flet_camera extension dependency to the client. |
| client/pubspec.lock | Locks new transitive deps for camera/image, etc. |
| client/lib/main.dart | Imports/registers the camera extension in the client extensions list. |
| client/ios/Runner/Info.plist | Adds NSCameraUsageDescription for iOS. |
| .github/workflows/ci.yml | Adds flet-camera to extension build/publish workflows. |
| .codex/skills/implement-flet-extension/SKILL.md | Updates extension implementation checklist to reflect CI update locations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| # Run connect asynchronously so websocket receive loop isn't blocked by | ||
| # user handlers (e.g., on_connect invoking _invoke_method). | ||
|
|
||
| async def _connect(): | ||
| try: | ||
| await session.connect(conn) | ||
| except Exception as e: | ||
| logger.error( | ||
| f"Unhandled error reconnecting session {session_id}: {e}", | ||
| exc_info=True, | ||
| ) | ||
| try: | ||
| session.error(str(e)) | ||
| except Exception: | ||
| logger.error( | ||
| "Failed to report reconnect error to session", | ||
| exc_info=True, | ||
| ) | ||
|
|
||
| asyncio.create_task(_connect()) |
There was a problem hiding this comment.
reconnect_session() now schedules session.connect(conn) in a background task and returns immediately. This introduces a race where subsequent logic (e.g. in flet_app.py right after awaiting reconnect_session) can run while the session is still considered disconnected (expires_at not cleared), causing outbound messages/updates to be dropped rather than buffered/sent. Consider keeping the connection attachment step synchronous/awaited (at least until Session.__conn is set and expires_at cleared), and only run the user connect event handlers asynchronously if needed (e.g., split Session.connect() into an attach+flush step and a separately-scheduled event dispatch).
| # Run connect asynchronously so websocket receive loop isn't blocked by | |
| # user handlers (e.g., on_connect invoking _invoke_method). | |
| async def _connect(): | |
| try: | |
| await session.connect(conn) | |
| except Exception as e: | |
| logger.error( | |
| f"Unhandled error reconnecting session {session_id}: {e}", | |
| exc_info=True, | |
| ) | |
| try: | |
| session.error(str(e)) | |
| except Exception: | |
| logger.error( | |
| "Failed to report reconnect error to session", | |
| exc_info=True, | |
| ) | |
| asyncio.create_task(_connect()) | |
| try: | |
| # Await connect so that session state (e.g., connection attachment, | |
| # expiration flags) is updated before returning to the caller. | |
| await session.connect(conn) | |
| except Exception as e: | |
| logger.error( | |
| f"Unhandled error reconnecting session {session_id}: {e}", | |
| exc_info=True, | |
| ) | |
| try: | |
| session.error(str(e)) | |
| except Exception: | |
| logger.error( | |
| "Failed to report reconnect error to session", | |
| exc_info=True, | |
| ) |
sdk/python/packages/flet-camera/src/flutter/flet_camera/lib/src/camera.dart
Show resolved
Hide resolved
| - Audio: audio/index.md | ||
| - AudioRecorder: audio_recorder/index.md |
There was a problem hiding this comment.
This nav block adds Audio and AudioRecorder under API Reference -> Controls, but those entries already exist under API Reference -> Services later in the file. Unless the intent is to move them (and remove the existing entries), this duplicates pages in the nav and can lead to confusing/duplicated sidebar structure. Consider removing the new Audio/AudioRecorder entries here (or deleting the existing ones under Services if you’re intentionally relocating them).
| - Audio: audio/index.md | |
| - AudioRecorder: audio_recorder/index.md |
|
|
||
| /// admonition | Permissions | ||
| type: tip | ||
| Request camera (and microphone if recording video with audio) permissions on mobile and desktop platforms before initializing the control. You can use [`flet-permission-handler`](https://pypi.org/project/flet-permission-handler/) to prompt the user. |
There was a problem hiding this comment.
The permissions note mentions requesting camera/microphone permissions on “mobile and desktop platforms”, but the Platform Support table above indicates Windows/macOS/Linux are not supported. Consider updating this text to avoid implying desktop support (or update the support table if desktop support is intended).
| Request camera (and microphone if recording video with audio) permissions on mobile and desktop platforms before initializing the control. You can use [`flet-permission-handler`](https://pypi.org/project/flet-permission-handler/) to prompt the user. | |
| Request camera (and microphone if recording video with audio) permissions on mobile platforms before initializing the control. You can use [`flet-permission-handler`](https://pypi.org/project/flet-permission-handler/) to prompt the user. |
There was a problem hiding this comment.
Seems legit?
Maybe we could even completely remove the platform mentions...
Also, for the flet-permission-handler link, maybe instead point to its docs page directly?
sdk/python/packages/flet-camera/src/flutter/flet_camera/lib/src/utils/camera.dart
Outdated
Show resolved
Hide resolved
Pass image_format_group=fc.ImageFormatGroup.JPEG when starting the camera preview so frames are delivered as JPEG. This ensures consistent encoding for image streaming and downstream processing.
Move camera image encoding off the main thread by serializing CameraImage into a payload and running encoding in an Isolate. Import dart:isolate and update _processStreamImage to use Isolate.run for non-JPEG formats. Add cameraImageToPayload and encodeCameraImagePayload helpers and refactor BGRA/NV21/YUV420 encoders to accept payload maps (with stronger validation and bounds checks). Keep JPEG fast-path returning plane bytes directly. This reduces UI jank and ensures safe cross-isolate data handling.
Set gapless_playback=True on the media widget in sdk/python/examples/controls/camera/example_1.py to avoid visible reload/flicker when the source is updated (e.g., switching camera frames). This makes playback smoother in the camera example.
ndonkoHenri
left a comment
There was a problem hiding this comment.
Docs: only 4 classes were documented. Still about 8 left.
Example: on web - i was able to record a video, and when I stopped the recording it saved as .webm into my downloads folder - awesome! This was on the first try though. On the next tries though, i didnt work :( Flow: launch with fvm flutter run -d chrome, grant permission, then click take picture or preview buttons. A flutter exception (see logs below) is shown immediately after the app launches (when await get_cameras() on line 358 is executed). The Python exception is shown when i click a button.
Logs
Received message: [2, {id: 1, patch: [[0, {views: [1, {0: [2]}]}], [0, 0, title, Camera control], [0, 0, on_connect, true], [0, 2, padding, 16], [0, 2, scroll, auto], [0, 2, horizontal_alignment, stretch], [0, 2, controls, [{_i:
118, _c: SafeArea, content: {_i: 117, _c: Column, _internals: {host_expanded: true}, controls: [{_i: 113, _c: Row, _internals: {host_expanded: true}, controls: [{_i: 105, _c: Dropdown, on_select: true, label: Camera}, {_i: 112,
_c: IconButton, icon: 71733, on_click: true}], wrap: true}, {_i: 114, _c: Container, _internals: {skip_properties: [width, height, margin]}, height: 320, content: {_i: 102, _c: Camera, expand: true, content: {_i: 101, _c:
Container, _internals: {skip_properties: [width, height, margin]}, content: {_i: 100, _c: Icon, icon: 66817, color: white70, size: 48}, alignment: {x: 0, y: 0}}, on_state_change: true, on_stream_image: true}, bgcolor: black,
border_radius: 3}, {_i: 103, _c: Text, value: Select a camera, size: 12}, {_i: 115, _c: Row, _internals: {host_expanded: true}, controls: [{_i: 107, _c: FilledIconButton, tooltip: Take photo, icon: 71254, on_click: true}, {_i:
108, _c: FilledTonalIconButton, tooltip: Start / stop recording, icon: 73878, selected: false, selected_icon: 72931, on_click: true}, {_i: 109, _c: OutlinedIconButton, tooltip: Pause / resume recording, disabled: true, icon:
70989, selected: false, selected_icon: 71356, on_click: true}, {_i: 110, _c: OutlinedIconButton, tooltip: Start / stop image stream, visible: false, icon: 71356, selected: false, selected_icon: 72931, on_click: true}, {_i: 111,
_c: OutlinedIconButton, tooltip: Pause / resume preview, icon: 73987, selected: true, selected_icon: 73986, on_click: true}], wrap: true}, {_i: 106, _c: Text, value: Recorded video: not saved yet, size: 12}, {_i: 116, _c: Text,
value: Last photo}, {_i: 104, _c: Image, _internals: {skip_properties: [width, height]}, height: 200, src: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wIAAgMBAp0YVwAAAABJRU5ErkJggg==, fit: contain,
gapless_playback: true}]}}]]]}]
Control(1).applyPatch: [[0, {views: [1, {0: [2]}]}], [0, 0, title, Camera control], [0, 0, on_connect, true], [0, 2, padding, 16], [0, 2, scroll, auto], [0, 2, horizontal_alignment, stretch], [0, 2, controls, [{_i: 118, _c:
SafeArea, content: {_i: 117, _c: Column, _internals: {host_expanded: true}, controls: [{_i: 113, _c: Row, _internals: {host_expanded: true}, controls: [{_i: 105, _c: Dropdown, on_select: true, label: Camera}, {_i: 112, _c:
IconButton, icon: 71733, on_click: true}], wrap: true}, {_i: 114, _c: Container, _internals: {skip_properties: [width, height, margin]}, height: 320, content: {_i: 102, _c: Camera, expand: true, content: {_i: 101, _c: Container,
_internals: {skip_properties: [width, height, margin]}, content: {_i: 100, _c: Icon, icon: 66817, color: white70, size: 48}, alignment: {x: 0, y: 0}}, on_state_change: true, on_stream_image: true}, bgcolor: black, border_radius:
3}, {_i: 103, _c: Text, value: Select a camera, size: 12}, {_i: 115, _c: Row, _internals: {host_expanded: true}, controls: [{_i: 107, _c: FilledIconButton, tooltip: Take photo, icon: 71254, on_click: true}, {_i: 108, _c:
FilledTonalIconButton, tooltip: Start / stop recording, icon: 73878, selected: false, selected_icon: 72931, on_click: true}, {_i: 109, _c: OutlinedIconButton, tooltip: Pause / resume recording, disabled: true, icon: 70989,
selected: false, selected_icon: 71356, on_click: true}, {_i: 110, _c: OutlinedIconButton, tooltip: Start / stop image stream, visible: false, icon: 71356, selected: false, selected_icon: 72931, on_click: true}, {_i: 111, _c:
OutlinedIconButton, tooltip: Pause / resume preview, icon: 73987, selected: true, selected_icon: 73986, on_click: true}], wrap: true}, {_i: 106, _c: Text, value: Recorded video: not saved yet, size: 12}, {_i: 116, _c: Text,
value: Last photo}, {_i: 104, _c: Image, _internals: {skip_properties: [width, height]}, height: 200, src: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wIAAgMBAp0YVwAAAABJRU5ErkJggg==, fit: contain,
gapless_playback: true}]}}]]], shouldNotify = true
Notify Page(1)
Page updated: TargetPlatform.macOS TargetPlatform.macOS
Notify Page(1)
Page updated: TargetPlatform.macOS TargetPlatform.macOS
Notify View(94)
Notify View(94)
Notify View(94)
Notify View(94)
Received message: [5, {control_id: 102, call_id: ayGpSZOPQh, name: get_available_cameras}]
Camera(102).get_available_cameras(null)
Page.didUpdateWidget: 1
Page.build: 1
Page navigator build: 1
View.didUpdateWidget: 94
View.build: 94
ScrollableControl build: 94
SafeArea build: 118
Column build: 117
ScrollableControl build: 117
Row build: 113
ScrollableControl build: 113
DropdownMenu build: 105
IconButton build: 112
Container build: 114
Camera build: 102
Text build: 103
Row build: 115
ScrollableControl build: 115
IconButton build: 107
IconButton build: 108
IconButton build: 109
IconButton build: 111
Text build: 106
Text build: 116
Image build: 104
Camera.get_available_cameras(null)
══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
The following DomException object was thrown resolving an image frame:
EncodingError: Failed to decode frame at index 0
When the exception was thrown, this was the stack
════════════════════════════════════════════════════════════════════════════════════════════════════
_send: MessageAction.controlEvent {target: 1, name: app_lifecycle_state_change, data: {state: inactive}}
_send: MessageAction.controlEvent {target: 1, name: app_lifecycle_state_change, data: {state: resume}}
_send: MessageAction.invokeControlMethod {control_id: 102, call_id: ayGpSZOPQh, result: [{name: MacBook Pro Camera (0000:0001), lens_direction: external, sensor_orientation: 0, lens_type: unknown}, {name: OBS Virtual Camera,
lens_direction: external, sensor_orientation: 0, lens_type: unknown}], error: null}
Received message: [2, {id: 1, patch: [[0, {views: [1, {0: [2, {controls: [3, {0: [4, {content: [5, {controls: [6, {0: [7, {controls: [8, {0: [9]}]}]}]}]}]}]}]}]}], [0, 9, options, [{_i: 119, _c: DropdownOption, key: MacBook Pro
Camera (0000:0001), text: MacBook Pro Camera (0000:0001)}, {_i: 120, _c: DropdownOption, key: OBS Virtual Camera, text: OBS Virtual Camera}]]]}]
Control(1).applyPatch: [[0, {views: [1, {0: [2, {controls: [3, {0: [4, {content: [5, {controls: [6, {0: [7, {controls: [8, {0: [9]}]}]}]}]}]}]}]}]}], [0, 9, options, [{_i: 119, _c: DropdownOption, key: MacBook Pro Camera
(0000:0001), text: MacBook Pro Camera (0000:0001)}, {_i: 120, _c: DropdownOption, key: OBS Virtual Camera, text: OBS Virtual Camera}]]], shouldNotify = true
Notify Dropdown(105)
DropdownMenu build: 105
OutlinedIconButton(111).on_click(null)
_send: MessageAction.controlEvent {target: 111, name: click, data: null}
Received message: [5, {control_id: 102, call_id: QTb9SabhZz, name: pause_preview}]
Camera(102).pause_preview(null)
Camera.pause_preview(null)
_send: MessageAction.invokeControlMethod {control_id: 102, call_id: QTb9SabhZz, result: null, error: Exception: Camera is not initialized. Call initialize() first.}
Received message: [6, {message: Exception: Camera is not initialized. Call initialize() first.
Traceback (most recent call last):
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/packages/flet/src/flet/messaging/session.py", line 319, in dispatch_event
await control._trigger_event(event_name, event_data)
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/packages/flet/src/flet/controls/base_control.py", line 414, in _trigger_event
await event_handler()
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/examples/controls/camera/example_1.py", line 312, in toggle_preview
await pause_preview()
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/examples/controls/camera/example_1.py", line 279, in pause_preview
await preview.pause_preview()
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/packages/flet-camera/src/flet_camera/camera.py", line 134, in pause_preview
await self._invoke_method("pause_preview")
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/packages/flet/src/flet/controls/base_control.py", line 360, in _invoke_method
return await self.page.session.invoke_method(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/ndonkohenri/PycharmProjects/flet-dev/flet/sdk/python/packages/flet/src/flet/messaging/session.py", line 374, in invoke_method
raise RuntimeError(err)
RuntimeError: Exception: Camera is not initialized. Call initialize() first.
}]| [](https://pypi.python.org/pypi/flet-camera) | ||
| [](https://pepy.tech/project/flet-camera) | ||
| [](https://pypi.org/project/flet-camera) | ||
| [](https://github.com/flet-dev/flet/tree/main/sdk/python/packages/flet/docs/assets/badges/docs-coverage) |
There was a problem hiding this comment.
Lets point the link to the image? The docs-coverage folder isnt quite useful.
| [](https://github.com/flet-dev/flet/tree/main/sdk/python/packages/flet/docs/assets/badges/docs-coverage) | |
| [](https://docs.flet.dev/assets/badges/docs-coverage/flet-camera.svg) |
| [](https://github.com/flet-dev/flet/tree/main/sdk/python/packages/flet/docs/assets/badges/docs-coverage) | ||
| [](https://github.com/flet-dev/flet/blob/main/sdk/python/packages/flet-camera/LICENSE) | ||
|
|
||
| A cross-platform camera control for [Flet](https://flet.dev) apps. |
There was a problem hiding this comment.
Doenst seem fully "cross-platform" though 😅
| // ignore: implementation_imports | ||
| import 'package:flet/src/controls/control_widget.dart'; |
There was a problem hiding this comment.
Export this file lib/flet.dart? Its kind of a util.
export 'src/controls/control_widget.dart';| DeviceOrientation? parseDeviceOrientation(dynamic value) { | ||
| return parseEnum(DeviceOrientation.values, value); | ||
| } |
There was a problem hiding this comment.
Exists in utils/device_info.dart, but needs to be exposed/exported in lib/flet.dart.
| ImageFormatGroup? parseImageFormatGroup(dynamic value) { | ||
| return parseEnum(ImageFormatGroup.values, value); | ||
| } | ||
|
|
||
| FlashMode? parseFlashMode(dynamic value) { | ||
| return parseEnum(FlashMode.values, value); | ||
| } | ||
|
|
||
| ExposureMode? parseExposureMode(dynamic value) { | ||
| return parseEnum(ExposureMode.values, value); | ||
| } | ||
|
|
||
| FocusMode? parseFocusMode(dynamic value) { | ||
| return parseEnum(FocusMode.values, value); | ||
| } |
There was a problem hiding this comment.
Add defaultValue param.
| --- | ||
| class_name: flet_camera.CameraState | ||
| separate_signature: false | ||
| --- | ||
|
|
||
| # CameraState | ||
|
|
||
| {{ class_all_options(class_name) }} |
There was a problem hiding this comment.
| --- | |
| class_name: flet_camera.CameraState | |
| separate_signature: false | |
| --- | |
| # CameraState | |
| {{ class_all_options(class_name) }} | |
| {{ class_all_options("flet_camera.CameraState") }} |
| --- | ||
| class_name: flet_camera.CameraDescription | ||
| separate_signature: false | ||
| --- | ||
|
|
||
| # CameraDescription | ||
|
|
||
| {{ class_all_options(class_name) }} |
There was a problem hiding this comment.
| --- | |
| class_name: flet_camera.CameraDescription | |
| separate_signature: false | |
| --- | |
| # CameraDescription | |
| {{ class_all_options(class_name) }} | |
| {{ class_all_options("flet_camera.CameraDescription") }} |
| recorded_video_path.value = "Recorded video save canceled" | ||
| page.update() | ||
|
|
||
| async def on_state_change(e: ft.Event[fc.CameraState]): |
There was a problem hiding this comment.
| async def on_state_change(e: ft.Event[fc.CameraState]): | |
| async def on_state_change(e: fc.CameraState): |
Or CameraStateEvent as suggested in another comment.
| on_click=lambda _: None, | ||
| tooltip="Start / stop image stream", | ||
| visible=False, | ||
| ) | ||
| preview_btn = ft.OutlinedIconButton( | ||
| icon=ft.Icons.VISIBILITY_OFF, | ||
| selected_icon=ft.Icons.VISIBILITY, | ||
| selected=True, | ||
| on_click=lambda _: None, | ||
| tooltip="Pause / resume preview", |
There was a problem hiding this comment.
lambda _: None is honestly not a good style.
Will suggest you move these declarations below the events callbacks so we can set them directly.
| def detect_video_extension(data: bytes) -> str: | ||
| # Matroska/WebM EBML header. | ||
| if data.startswith(b"\x1a\x45\xdf\xa3"): | ||
| return "webm" | ||
|
|
||
| # ISO BMFF (mp4/mov) starts with a box size + "ftyp". | ||
| if len(data) >= 12 and data[4:8] == b"ftyp": | ||
| brand = data[8:12] | ||
| if brand == b"qt ": | ||
| return "mov" | ||
| return "mp4" | ||
|
|
||
| return "bin" |
There was a problem hiding this comment.
This seems pretty handy. Should we add it to package too? perhaps as util or method of a class?

Summary by Sourcery
Add a new cross-platform Camera control as a first-class Flet extension and integrate it into the client, SDK, docs, CI, and examples.
New Features:
Bug Fixes:
Enhancements:
Build:
CI:
Documentation: