diff --git a/lib/screens/metering/communication/bloc_communication_metering.dart b/lib/screens/metering/communication/bloc_communication_metering.dart index f80866c..40dcd73 100644 --- a/lib/screens/metering/communication/bloc_communication_metering.dart +++ b/lib/screens/metering/communication/bloc_communication_metering.dart @@ -8,6 +8,7 @@ class MeteringCommunicationBloc extends Bloc((_, emit) => emit(MeasureState())); + on((event, emit) => emit(EquipmentProfileChangedState(event.profile))); on((event, emit) => emit(MeteringInProgressState(event.ev100))); on((event, emit) => emit(MeteringEndedState(event.ev100))); on((_, emit) => emit(const SettingsOpenedState())); diff --git a/lib/screens/metering/communication/event_communication_metering.dart b/lib/screens/metering/communication/event_communication_metering.dart index 8872fa0..8c12656 100644 --- a/lib/screens/metering/communication/event_communication_metering.dart +++ b/lib/screens/metering/communication/event_communication_metering.dart @@ -1,19 +1,29 @@ +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + abstract class MeteringCommunicationEvent { const MeteringCommunicationEvent(); } -abstract class SourceEvent extends MeteringCommunicationEvent { - const SourceEvent(); -} - +/// Events sent by the screen to the current metering source. abstract class ScreenEvent extends MeteringCommunicationEvent { const ScreenEvent(); } +/// Event sent by the current metering source in response for the screen events. +abstract class SourceEvent extends MeteringCommunicationEvent { + const SourceEvent(); +} + class MeasureEvent extends ScreenEvent { const MeasureEvent(); } +class EquipmentProfileChangedEvent extends ScreenEvent { + final EquipmentProfile profile; + + const EquipmentProfileChangedEvent(this.profile); +} + abstract class MeasuredEvent extends SourceEvent { final double? ev100; diff --git a/lib/screens/metering/communication/state_communication_metering.dart b/lib/screens/metering/communication/state_communication_metering.dart index 2d3991e..1090bdc 100644 --- a/lib/screens/metering/communication/state_communication_metering.dart +++ b/lib/screens/metering/communication/state_communication_metering.dart @@ -1,3 +1,5 @@ +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + sealed class MeteringCommunicationState { const MeteringCommunicationState(); } @@ -18,6 +20,12 @@ class MeasureState extends SourceState { const MeasureState(); } +class EquipmentProfileChangedState extends SourceState { + final EquipmentProfile profile; + + const EquipmentProfileChangedState(this.profile); +} + sealed class MeasuredState extends ScreenState { final double? ev100; diff --git a/lib/screens/metering/components/camera_container/bloc_container_camera.dart b/lib/screens/metering/components/camera_container/bloc_container_camera.dart index ef6125e..99d78a8 100644 --- a/lib/screens/metering/components/camera_container/bloc_container_camera.dart +++ b/lib/screens/metering/components/camera_container/bloc_container_camera.dart @@ -26,11 +26,12 @@ class CameraContainerBloc extends EvSourceBlocBase camera.lensDirection == CameraLensDirection.back, + orElse: () => cameras.last, + ); + } } - _cameraController = CameraController( - cameras.firstWhere( - (camera) => camera.lensDirection == CameraLensDirection.back, - orElse: () => cameras.last, - ), + + final cameraController = CameraController( + _camera!, ResolutionPreset.low, enableAudio: false, ); + await cameraController.initialize(); + await cameraController.setFlashMode(FlashMode.off); + await cameraController.lockCaptureOrientation(DeviceOrientation.portraitUp); - await _cameraController!.initialize(); - await _cameraController!.setFlashMode(FlashMode.off); - await _cameraController!.lockCaptureOrientation(DeviceOrientation.portraitUp); + if (_exposureOffsetRange == null) { + await Future.wait([ + cameraController.getMinExposureOffset(), + cameraController.getMaxExposureOffset(), + cameraController.getExposureOffsetStepSize(), + ]).then((value) { + _exposureOffsetRange = RangeValues( + math.max(_exposureMaxRange.start, value[0]), + math.min(_exposureMaxRange.end, value[1]), + ); + _currentExposureOffset = 0.0; + _exposureStep = value[2] == 0 ? 0.1 : value[2]; + }); + } - _zoomRange = await Future.wait([ - _cameraController!.getMinZoomLevel(), - _cameraController!.getMaxZoomLevel(), - ]).then((levels) => RangeValues(math.max(1.0, levels[0]), math.min(_maxZoom, levels[1]))); - _currentZoom = _zoomRange!.start; + if (_zoomRange == null) { + await Future.wait([ + cameraController.getMinZoomLevel(), + cameraController.getMaxZoomLevel(), + ]).then((value) { + _zoomRange = RangeValues( + math.max(1.0, value[0]), + math.min(_maxZoom, value[1]), + ); + if (_currentZoom < _zoomRange!.start || _currentZoom > _zoomRange!.end) { + _currentZoom = _zoomRange!.start; + } + }); + } - _exposureOffsetRange = await Future.wait([ - _cameraController!.getMinExposureOffset(), - _cameraController!.getMaxExposureOffset(), - ]).then( - (levels) => RangeValues( - math.max(_exposureMaxRange.start, levels[0]), - math.min(_exposureMaxRange.end, levels[1]), - ), - ); - await _cameraController!.getExposureOffsetStepSize().then((value) { - _exposureStep = value == 0 ? 0.1 : value; - }); - _currentExposureOffset = 0.0; - - emit(CameraInitializedState(_cameraController!)); + /// For app startup initialization this effectively isn't executed. + await Future.wait([ + if (_currentZoom != 1.0) cameraController.setZoomLevel(_currentZoom), + if (_currentExposureOffset != 0.0) cameraController.setExposureOffset(_currentExposureOffset), + ]); + _cameraController = cameraController; + emit(CameraInitializedState(cameraController)); _emitActiveState(emit); } catch (e) { emit(const CameraErrorState(CameraErrorType.other)); @@ -169,7 +196,7 @@ class CameraContainerBloc extends EvSourceBlocBase _onZoomChanged(ZoomChangedEvent event, Emitter emit) async { - if (_cameraController != null) { + if (_cameraController != null && _zoomRange != null) { final double zoom = event.value.clamp(_zoomRange!.start, _zoomRange!.end); _cameraController!.setZoomLevel(zoom); _currentZoom = zoom; diff --git a/lib/screens/metering/components/camera_container/components/camera_controls/components/zoom_slider/widget_slider_zoom.dart b/lib/screens/metering/components/camera_container/components/camera_controls/components/zoom_slider/widget_slider_zoom.dart index 316d547..48ec91f 100644 --- a/lib/screens/metering/components/camera_container/components/camera_controls/components/zoom_slider/widget_slider_zoom.dart +++ b/lib/screens/metering/components/camera_container/components/camera_controls/components/zoom_slider/widget_slider_zoom.dart @@ -3,7 +3,7 @@ import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/screens/shared/ruler_slider/widget_slider_ruler.dart'; import 'package:lightmeter/utils/double_to_zoom.dart'; -class ZoomSlider extends StatefulWidget { +class ZoomSlider extends StatelessWidget { final RangeValues range; final double value; final ValueChanged onChanged; @@ -15,23 +15,12 @@ class ZoomSlider extends StatefulWidget { super.key, }); - @override - State createState() => _ZoomSliderState(); -} - -class _ZoomSliderState extends State { - @override - void didChangeDependencies() { - super.didChangeDependencies(); - widget.onChanged(EquipmentProfiles.selectedOf(context).lensZoom); - } - @override Widget build(BuildContext context) { return RulerSlider( - range: widget.range, - value: widget.value, - onChanged: widget.onChanged, + range: range, + value: value, + onChanged: onChanged, icon: Icons.search_outlined, defaultValue: EquipmentProfiles.selectedOf(context).lensZoom, rulerValueAdapter: (value) => value.toStringAsFixed(0), diff --git a/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart b/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart index 4930c46..45560e8 100644 --- a/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart +++ b/lib/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart @@ -1,13 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; +import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; +import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -class EquipmentProfilePicker extends StatelessWidget { +class EquipmentProfilePicker extends StatefulWidget { const EquipmentProfilePicker(); + @override + State createState() => _EquipmentProfilePickerState(); +} + +class _EquipmentProfilePickerState extends State { + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final profile = EquipmentProfiles.selectedOf(context); + context.read().add(EquipmentProfileChangedEvent(profile)); + } + @override Widget build(BuildContext context) { return AnimatedDialogPicker( @@ -16,7 +31,10 @@ class EquipmentProfilePicker extends StatelessWidget { selectedValue: EquipmentProfiles.selectedOf(context), values: EquipmentProfiles.inUseOf(context), itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), - onChanged: EquipmentProfilesProvider.of(context).selectProfile, + onChanged: (profile) { + EquipmentProfilesProvider.of(context).selectProfile(profile); + context.read().add(EquipmentProfileChangedEvent(profile)); + }, closedChild: ReadingValueContainer.singleValue( value: ReadingValue( label: S.of(context).equipmentProfile, diff --git a/test/screens/metering/components/shared/readings_container/equipment_profile_picker_test.dart b/test/screens/metering/components/shared/readings_container/equipment_profile_picker_test.dart index bf97fea..4fb63d9 100644 --- a/test/screens/metering/components/shared/readings_container/equipment_profile_picker_test.dart +++ b/test/screens/metering/components/shared/readings_container/equipment_profile_picker_test.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; +import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -33,8 +35,11 @@ void main() { ], child: EquipmentProfilesProvider( storageService: storageService, - child: const WidgetTestApplicationMock( - child: Row(children: [Expanded(child: EquipmentProfilePicker())]), + child: WidgetTestApplicationMock( + child: BlocProvider( + create: (_) => MeteringCommunicationBloc(), + child: const Row(children: [Expanded(child: EquipmentProfilePicker())]), + ), ), ), ),