From e0d7e628bab8e8e559b135f7fba8a1b013bba0c6 Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Sun, 2 Aug 2026 10:21:58 +0100 Subject: [PATCH] Match tablet camera to preview ratio Signed-off-by: kenny lopez --- .../channels/compose_bar/attachments.dart | 28 ++++++++++++++++++- .../channels/compose_bar/camera_preview.dart | 22 +++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/mobile/lib/features/channels/compose_bar/attachments.dart b/mobile/lib/features/channels/compose_bar/attachments.dart index 7c53ae109..37f90944f 100644 --- a/mobile/lib/features/channels/compose_bar/attachments.dart +++ b/mobile/lib/features/channels/compose_bar/attachments.dart @@ -9,6 +9,8 @@ const _attachmentMenuItemSpacing = Grid.xxs; const _attachmentMenuIconSize = 24.0; const _attachmentMenuIconSlotWidth = 28.0; const _attachmentExpandedHeight = 372.0; +const _tabletAttachmentCameraMaxHeight = 640.0; +const _tabletShortestSideBreakpoint = 600.0; @immutable class _AttachmentMenuLayout { @@ -97,6 +99,7 @@ class _AttachmentSurfacePanel extends HookWidget { final renderedExpandedSurface = useState<_AttachmentSurface?>( isExpanded ? surface : null, ); + final cameraPreviewAspectRatio = useState(null); final latestSurface = useRef(surface); latestSurface.value = surface; @@ -123,6 +126,13 @@ class _AttachmentSurfacePanel extends HookWidget { return null; }, [isExpanded, morphController, surface]); + useEffect(() { + if (surface != _AttachmentSurface.camera) { + cameraPreviewAspectRatio.value = null; + } + return null; + }, [surface]); + final visibleExpandedSurface = renderedExpandedSurface.value ?? (isExpanded ? surface : null); final cameraInitializationReady = @@ -141,6 +151,11 @@ class _AttachmentSurfacePanel extends HookWidget { initializeCamera: cameraInitializationReady, onClose: onBack, onCapture: onCapture, + onPreviewAspectRatio: (aspectRatio) { + if (cameraPreviewAspectRatio.value != aspectRatio) { + cameraPreviewAspectRatio.value = aspectRatio; + } + }, ), ), ), @@ -170,7 +185,18 @@ class _AttachmentSurfacePanel extends HookWidget { return LayoutBuilder( builder: (context, constraints) { final expandedWidth = constraints.maxWidth; - const expandedHeight = _attachmentExpandedHeight; + final isTablet = + MediaQuery.sizeOf(context).shortestSide >= + _tabletShortestSideBreakpoint; + final expandedHeight = + visibleExpandedSurface == _AttachmentSurface.camera && + isTablet && + cameraPreviewAspectRatio.value != null + ? math.min( + expandedWidth / cameraPreviewAspectRatio.value!, + _tabletAttachmentCameraMaxHeight, + ) + : _attachmentExpandedHeight; final width = _attachmentMenuWidth + ((expandedWidth - _attachmentMenuWidth) * sizeProgress); diff --git a/mobile/lib/features/channels/compose_bar/camera_preview.dart b/mobile/lib/features/channels/compose_bar/camera_preview.dart index e51c2d16a..cd647a762 100644 --- a/mobile/lib/features/channels/compose_bar/camera_preview.dart +++ b/mobile/lib/features/channels/compose_bar/camera_preview.dart @@ -4,11 +4,13 @@ class _InlineCameraPreview extends HookConsumerWidget { final bool initializeCamera; final Future Function(XFile image) onCapture; final VoidCallback onClose; + final ValueChanged onPreviewAspectRatio; const _InlineCameraPreview({ required this.initializeCamera, required this.onCapture, required this.onClose, + required this.onPreviewAspectRatio, }); @override @@ -124,6 +126,18 @@ class _InlineCameraPreview extends HookConsumerWidget { } final activeController = controller.value; + final previewSize = activeController?.value.previewSize; + final previewAspectRatio = previewSize == null + ? null + : MediaQuery.orientationOf(context) == Orientation.landscape + ? activeController!.value.aspectRatio + : 1 / activeController!.value.aspectRatio; + useEffect(() { + if (previewAspectRatio != null) { + onPreviewAspectRatio(previewAspectRatio); + } + return null; + }, [onPreviewAspectRatio, previewAspectRatio]); final usesAndroidCameraLayout = defaultTargetPlatform == TargetPlatform.android; return ColoredBox( @@ -176,13 +190,17 @@ class _CameraFeed extends StatelessWidget { Widget build(BuildContext context) { final previewSize = controller.value.previewSize; if (previewSize == null) return const ColoredBox(color: Colors.black); + final previewAspectRatio = + MediaQuery.orientationOf(context) == Orientation.landscape + ? controller.value.aspectRatio + : 1 / controller.value.aspectRatio; return ClipRect( child: FittedBox( fit: BoxFit.cover, child: SizedBox( - width: previewSize.height, - height: previewSize.width, + width: previewAspectRatio, + height: 1, child: camera.CameraPreview(controller), ), ),