camera: mirror preview only, not capture (telegram fidelity)

Drop isVideoMirrored on the AVCaptureVideoDataOutput connection — the
data output feeds both the preview texture AND the recorder, so any
mirror set there ended up baked into the recorded MP4. Recorded video
+ captured JPEG now carry the raw sensor feed ("as others see you"),
matching telegram-iOS and the stock iOS Camera app default.

The selfie preview is mirrored inside UxCameraPreview itself
(Transform.flip(flipX: true) around the Texture when
description.lens == front) — the analog of telegram's
CameraPreviewView.mirroring CALayer transform. Consumers
(CameraThumb, etc.) don't need to know which lens is active.
This commit is contained in:
agra
2026-05-13 17:12:07 +03:00
parent 73a69b6374
commit 35151bb325
2 changed files with 23 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
import 'package:flutter/widgets.dart';
import 'camera.dart' show UxCameraController, UxCameraValue;
import 'camera.dart' show UxCameraController, UxCameraLens, UxCameraValue;
/// Renders the live preview for [controller] into a [Texture]. Sizes
/// itself to the parent — wrap in `AspectRatio` / `FittedBox` / `Hero`
@@ -10,6 +10,11 @@ import 'camera.dart' show UxCameraController, UxCameraValue;
/// transparent placeholder. The widget rebuilds on every
/// `UxCameraValue` change, so once the native session starts
/// producing frames the texture appears automatically.
///
/// Front-camera preview is auto-mirrored here (the analog of
/// telegram-iOS's `CameraPreviewView.mirroring` property), so the
/// recorded MP4 + captured JPEG carry the raw sensor feed while the
/// on-screen preview still reads as a natural mirror to the user.
class UxCameraPreview extends StatelessWidget {
const UxCameraPreview({super.key, required this.controller});
@@ -19,10 +24,14 @@ class UxCameraPreview extends StatelessWidget {
Widget build(BuildContext context) {
return ValueListenableBuilder<UxCameraValue>(
valueListenable: controller,
builder: (context, _, __) {
builder: (context, value, _) {
final id = controller.textureId;
if (id == null) return const SizedBox.expand();
return Texture(textureId: id);
if (id == null) return SizedBox.expand();
final mirror = value.description.lens == UxCameraLens.front;
final texture = Texture(textureId: id);
return mirror
? Transform.flip(flipX: true, child: texture)
: texture;
},
);
}