import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/features.dart'; import 'package:lightmeter/platform_config.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_controls/widget_camera_controls.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_controls_placeholder/widget_placeholder_camera_controls.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_preview/widget_camera_preview.dart'; import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart'; import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart'; import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class CameraContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; final Film film; final IsoValue iso; final NdValue nd; final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; const CameraContainer({ required this.fastest, required this.slowest, required this.film, required this.iso, required this.nd, required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, super.key, }); @override Widget build(BuildContext context) { final double meteringContainerHeight = _meteringContainerHeight(context); final double cameraPreviewHeight = _cameraPreviewHeight(context); final double topBarOverflow = meteringContainerHeight - cameraPreviewHeight; return Stack( children: [ Positioned( left: 0, top: 0, right: 0, child: MeteringTopBar( readingsContainer: ReadingsContainer( fastest: fastest, slowest: slowest, film: film, iso: iso, nd: nd, onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, ), appendixHeight: topBarOverflow, preview: const _CameraViewBuilder(), ), ), SafeArea( bottom: false, child: Column( children: [ SizedBox( height: min(meteringContainerHeight, cameraPreviewHeight) + Dimens.paddingM * 2, ), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM), child: Row( children: [ Expanded( child: Padding( padding: EdgeInsets.only(top: topBarOverflow >= 0 ? topBarOverflow : 0), child: ExposurePairsList(exposurePairs), ), ), const SizedBox(width: Dimens.grid8), Expanded( child: Padding( padding: EdgeInsets.only(top: topBarOverflow <= 0 ? -topBarOverflow : 0), child: const _CameraControlsBuilder(), ), ), ], ), ), ), ], ), ) ], ); } double _meteringContainerHeight(BuildContext context) { double enabledFeaturesHeight = 0; if (FeaturesConfig.equipmentProfilesEnabled) { enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; enabledFeaturesHeight += Dimens.paddingS; } if (UserPreferencesProvider.meteringScreenFeatureOf( context, MeteringScreenLayoutFeature.extremeExposurePairs, )) { enabledFeaturesHeight += Dimens.readingContainerDoubleValueHeight; enabledFeaturesHeight += Dimens.paddingS; } if (UserPreferencesProvider.meteringScreenFeatureOf( context, MeteringScreenLayoutFeature.filmPicker, )) { enabledFeaturesHeight += Dimens.readingContainerSingleValueHeight; enabledFeaturesHeight += Dimens.paddingS; } return enabledFeaturesHeight + Dimens.readingContainerSingleValueHeight; // ISO & ND } double _cameraPreviewHeight(BuildContext context) { return ((MediaQuery.of(context).size.width - Dimens.grid8 - 2 * Dimens.paddingM) / 2) / PlatformConfig.cameraPreviewAspectRatio; } } class _CameraViewBuilder extends StatelessWidget { const _CameraViewBuilder(); @override Widget build(BuildContext context) { return BlocBuilder( buildWhen: (previous, current) => current is! CameraActiveState, builder: (context, state) => CameraPreview( controller: state is CameraInitializedState ? state.controller : null, error: state is CameraErrorState ? state.error : null, ), ); } } class _CameraControlsBuilder extends StatelessWidget { const _CameraControlsBuilder(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), child: BlocBuilder( builder: (context, state) { late final Widget child; if (state is CameraActiveState) { child = CameraControls( exposureOffsetRange: state.exposureOffsetRange, exposureOffsetValue: state.currentExposureOffset, onExposureOffsetChanged: (value) { context.read().add(ExposureOffsetChangedEvent(value)); }, zoomRange: state.zoomRange, zoomValue: state.currentZoom, onZoomChanged: (value) { context.read().add(ZoomChangedEvent(value)); }, ); } else if (state is CameraErrorState) { child = CameraControlsPlaceholder( error: state.error, onReset: () { context.read().add( state.error == CameraErrorType.permissionNotGranted ? const OpenAppSettingsEvent() : const InitializeEvent(), ); }, ); } else { child = const Column( children: [Expanded(child: SizedBox.shrink())], ); } return AnimatedSwitcher( duration: Dimens.switchDuration, child: child, ); }, ), ); } }