camera: Dart facade + backend + channel + preview + tests
Phase 1c+1d of the ux.camera plan (see ~/banlu/plans/ux_camera.md).
lib/src/camera/camera.dart — UxCameraController (ValueNotifier),
UxCameraValue, UxCameraDescription,
enums, UxCameraException,
uxAvailableCameras().
lib/src/camera/camera_backend.dart — abstract UxCameraBackend +
UxCameraCreateResult + sealed
UxCameraEvent variants.
lib/src/camera/camera_channel.dart — MethodChannelUxCameraBackend over
ux/camera + ux/camera/events. Per-
handle event demux. Maps
PlatformException → UxCameraException.
lib/src/camera/camera_preview.dart — UxCameraPreview: Texture-backed,
Hero-flightable preview widget.
lib/src/testing/fake_camera.dart — FakeUxCameraBackend with per-method
call lists + emitXxx event injection.
Exported from package:ux/testing.dart.
test/camera/camera_controller_test — 16 tests covering init/dispose,
orientation events, takePicture
(explicit + UxSensor fallback),
startVideoRecording / stop,
flip, flash, lock/unlock,
multi-instance, error propagation.
test/camera/camera_channel_test — 10 tests pinning the wire format
for every method + PlatformException
mapping.
Orientation snapshot for capture is computed Dart-side and passed in as an
explicit arg to takePicture / startVideoRecording (default falls back to
UxSensor.orientation at call time). Native never queries UIDevice itself
for the snapshot — Dart-side fakes drive orientation deterministically.
Native plugin code lands in Phase 2+; today every channel call throws
MissingPluginException at runtime, which is fine — the controller is only
mounted from the camera page once Phase 5 cuts over. The test backend
already exercises the full controller surface.
This commit is contained in:
29
lib/src/camera/camera_preview.dart
Normal file
29
lib/src/camera/camera_preview.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'camera.dart' show UxCameraController, UxCameraValue;
|
||||
|
||||
/// Renders the live preview for [controller] into a [Texture]. Sizes
|
||||
/// itself to the parent — wrap in `AspectRatio` / `FittedBox` / `Hero`
|
||||
/// to control framing.
|
||||
///
|
||||
/// While the controller is not yet initialized, this falls back to a
|
||||
/// transparent placeholder. The widget rebuilds on every
|
||||
/// `UxCameraValue` change, so once the native session starts
|
||||
/// producing frames the texture appears automatically.
|
||||
class UxCameraPreview extends StatelessWidget {
|
||||
const UxCameraPreview({super.key, required this.controller});
|
||||
|
||||
final UxCameraController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<UxCameraValue>(
|
||||
valueListenable: controller,
|
||||
builder: (context, _, __) {
|
||||
final id = controller.textureId;
|
||||
if (id == null) return const SizedBox.expand();
|
||||
return Texture(textureId: id);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user