mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-21 23:10:40 +00:00
Added camera
package to project
added camera plugin to project more reverse naming added communication bloc layout fix
This commit is contained in:
parent
7018f06270
commit
9b0b387514
18 changed files with 413 additions and 58 deletions
|
@ -43,7 +43,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 21
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
|
@ -4,4 +4,7 @@
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
126
lib/data/ev_source/camera/bloc_camera.dart
Normal file
126
lib/data/ev_source/camera/bloc_camera.dart
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:exif/exif.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event;
|
||||||
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
||||||
|
import 'package:lightmeter/utils/log_2.dart';
|
||||||
|
|
||||||
|
import 'event_camera.dart';
|
||||||
|
import 'state_camera.dart';
|
||||||
|
|
||||||
|
class CameraBloc extends Bloc<CameraEvent, CameraState> {
|
||||||
|
final MeteringCommunicationBloc _communicationBloc;
|
||||||
|
late final StreamSubscription<communication_states.MeteringCommunicationState> _communicationSubscription;
|
||||||
|
|
||||||
|
late final _WidgetsBindingObserver _observer;
|
||||||
|
CameraController? _cameraController;
|
||||||
|
CameraController? get cameraController => _cameraController;
|
||||||
|
|
||||||
|
CameraBloc(this._communicationBloc) : super(const CameraInitState()) {
|
||||||
|
_communicationSubscription = _communicationBloc.stream.listen(_onCommunicationState);
|
||||||
|
|
||||||
|
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
||||||
|
WidgetsBinding.instance.addObserver(_observer);
|
||||||
|
|
||||||
|
on<InitializeEvent>(_onInitialized);
|
||||||
|
|
||||||
|
add(const InitializeEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
WidgetsBinding.instance.removeObserver(_observer);
|
||||||
|
_cameraController?.dispose();
|
||||||
|
await _communicationSubscription.cancel();
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onCommunicationState(communication_states.MeteringCommunicationState communicationState) {
|
||||||
|
if (communicationState is communication_states.MeasureState) {
|
||||||
|
_takePhoto().then((ev100) {
|
||||||
|
if (ev100 != null) {
|
||||||
|
_communicationBloc.add(communication_event.MeasuredEvent(ev100));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onInitialized(_, Emitter emit) async {
|
||||||
|
emit(const CameraLoadingState());
|
||||||
|
try {
|
||||||
|
final cameras = await availableCameras();
|
||||||
|
_cameraController = CameraController(
|
||||||
|
cameras.firstWhere(
|
||||||
|
(camera) => camera.lensDirection == CameraLensDirection.back,
|
||||||
|
orElse: () => cameras.last,
|
||||||
|
),
|
||||||
|
ResolutionPreset.medium,
|
||||||
|
enableAudio: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _cameraController!.initialize();
|
||||||
|
await _cameraController!.setFlashMode(FlashMode.off);
|
||||||
|
emit(CameraReadyState(_cameraController!));
|
||||||
|
} catch (e) {
|
||||||
|
emit(const CameraErrorState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<double?> _takePhoto() async {
|
||||||
|
if (_cameraController == null ||
|
||||||
|
!_cameraController!.value.isInitialized ||
|
||||||
|
_cameraController!.value.isTakingPicture) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final file = await _cameraController!.takePicture();
|
||||||
|
final Uint8List bytes = await file.readAsBytes();
|
||||||
|
Directory(file.path).deleteSync(recursive: true);
|
||||||
|
|
||||||
|
final tags = await readExifFromBytes(bytes);
|
||||||
|
final iso = double.parse("${tags["EXIF ISOSpeedRatings"]}");
|
||||||
|
final apertureValueRatio = (tags["EXIF FNumber"]!.values as IfdRatios).ratios.first;
|
||||||
|
final aperture = apertureValueRatio.numerator / apertureValueRatio.denominator;
|
||||||
|
final speedValueRatio = (tags["EXIF ExposureTime"]!.values as IfdRatios).ratios.first;
|
||||||
|
final speed = speedValueRatio.numerator / speedValueRatio.denominator;
|
||||||
|
|
||||||
|
return log2(pow(aperture, 2)) - log2(speed) - log2(iso / 100);
|
||||||
|
} on CameraException catch (e) {
|
||||||
|
debugPrint('Error: ${e.code}\nError Message: ${e.description}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _appLifecycleStateObserver(AppLifecycleState state) async {
|
||||||
|
switch (state) {
|
||||||
|
case AppLifecycleState.resumed:
|
||||||
|
add(const InitializeEvent());
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.paused:
|
||||||
|
case AppLifecycleState.inactive:
|
||||||
|
_cameraController?.dispose();
|
||||||
|
_cameraController = null;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is needed only because we cannot use `with` with mixins
|
||||||
|
class _WidgetsBindingObserver with WidgetsBindingObserver {
|
||||||
|
final ValueChanged<AppLifecycleState> onLifecycleStateChanged;
|
||||||
|
|
||||||
|
_WidgetsBindingObserver(this.onLifecycleStateChanged);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
onLifecycleStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
7
lib/data/ev_source/camera/event_camera.dart
Normal file
7
lib/data/ev_source/camera/event_camera.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
abstract class CameraEvent {
|
||||||
|
const CameraEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitializeEvent extends CameraEvent {
|
||||||
|
const InitializeEvent();
|
||||||
|
}
|
23
lib/data/ev_source/camera/state_camera.dart
Normal file
23
lib/data/ev_source/camera/state_camera.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:camera/camera.dart';
|
||||||
|
|
||||||
|
abstract class CameraState {
|
||||||
|
const CameraState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CameraInitState extends CameraState {
|
||||||
|
const CameraInitState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CameraLoadingState extends CameraState {
|
||||||
|
const CameraLoadingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CameraReadyState extends CameraState {
|
||||||
|
final CameraController controller;
|
||||||
|
|
||||||
|
const CameraReadyState(this.controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CameraErrorState extends CameraState {
|
||||||
|
const CameraErrorState();
|
||||||
|
}
|
|
@ -1,16 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
import 'package:lightmeter/data/permissions_service.dart';
|
||||||
import 'package:lightmeter/screens/settings/settings_screen.dart';
|
import 'package:lightmeter/screens/settings/settings_screen.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'generated/l10n.dart';
|
import 'generated/l10n.dart';
|
||||||
import 'models/photography_value.dart';
|
|
||||||
import 'res/theme.dart';
|
import 'res/theme.dart';
|
||||||
import 'screens/metering/metering_bloc.dart';
|
import 'screens/metering/flow_metering.dart';
|
||||||
import 'screens/metering/metering_screen.dart';
|
|
||||||
import 'utils/stop_type_provider.dart';
|
import 'utils/stop_type_provider.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
@ -45,30 +42,27 @@ class _ApplicationState extends State<Application> {
|
||||||
return Provider(
|
return Provider(
|
||||||
create: (context) => PermissionsService(),
|
create: (context) => PermissionsService(),
|
||||||
child: StopTypeProvider(
|
child: StopTypeProvider(
|
||||||
child: BlocProvider(
|
child: MaterialApp(
|
||||||
create: (context) => MeteringBloc(context.read<StopType>()),
|
theme: ThemeData(
|
||||||
child: MaterialApp(
|
useMaterial3: true,
|
||||||
theme: ThemeData(
|
colorScheme: lightColorScheme,
|
||||||
useMaterial3: true,
|
|
||||||
colorScheme: lightColorScheme,
|
|
||||||
),
|
|
||||||
localizationsDelegates: const [
|
|
||||||
S.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
supportedLocales: S.delegate.supportedLocales,
|
|
||||||
builder: (context, child) => MediaQuery(
|
|
||||||
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
|
||||||
child: child!,
|
|
||||||
),
|
|
||||||
home: const MeteringScreen(),
|
|
||||||
routes: {
|
|
||||||
"metering": (context) => const MeteringScreen(),
|
|
||||||
"settings": (context) => const SettingsScreen(),
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
S.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: S.delegate.supportedLocales,
|
||||||
|
builder: (context, child) => MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
|
||||||
|
child: child!,
|
||||||
|
),
|
||||||
|
home: const MeteringFlow(),
|
||||||
|
routes: {
|
||||||
|
"metering": (context) => const MeteringFlow(),
|
||||||
|
"settings": (context) => const SettingsScreen(),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -7,19 +8,25 @@ import 'package:lightmeter/models/iso_value.dart';
|
||||||
import 'package:lightmeter/models/nd_value.dart';
|
import 'package:lightmeter/models/nd_value.dart';
|
||||||
import 'package:lightmeter/models/photography_value.dart';
|
import 'package:lightmeter/models/photography_value.dart';
|
||||||
import 'package:lightmeter/models/shutter_speed_value.dart';
|
import 'package:lightmeter/models/shutter_speed_value.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_events;
|
||||||
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
||||||
import 'package:lightmeter/utils/log_2.dart';
|
import 'package:lightmeter/utils/log_2.dart';
|
||||||
|
|
||||||
import 'metering_event.dart';
|
import 'communication/bloc_communication_metering.dart';
|
||||||
import 'metering_state.dart';
|
import 'event_metering.dart';
|
||||||
|
import 'state_metering.dart';
|
||||||
|
|
||||||
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
|
final MeteringCommunicationBloc _communicationBloc;
|
||||||
|
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
|
||||||
|
|
||||||
List<ApertureValue> get _apertureValues => apertureValues.whereStopType(stopType);
|
List<ApertureValue> get _apertureValues => apertureValues.whereStopType(stopType);
|
||||||
List<ShutterSpeedValue> get _shutterSpeedValues => shutterSpeedValues.whereStopType(stopType);
|
List<ShutterSpeedValue> get _shutterSpeedValues => shutterSpeedValues.whereStopType(stopType);
|
||||||
final _random = Random();
|
final _random = Random();
|
||||||
|
|
||||||
StopType stopType;
|
StopType stopType;
|
||||||
|
|
||||||
MeteringBloc(this.stopType)
|
MeteringBloc(this._communicationBloc, this.stopType)
|
||||||
: super(
|
: super(
|
||||||
MeteringState(
|
MeteringState(
|
||||||
iso: isoValues.where((element) => element.value == 100).first,
|
iso: isoValues.where((element) => element.value == 100).first,
|
||||||
|
@ -29,14 +36,42 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
exposurePairs: [],
|
exposurePairs: [],
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
|
_communicationSubscription = _communicationBloc.stream
|
||||||
|
.where((state) => state is communication_states.ScreenState)
|
||||||
|
.map((state) => state as communication_states.ScreenState)
|
||||||
|
.listen(_onCommunicationState);
|
||||||
|
|
||||||
on<StopTypeChangedEvent>(_onStopTypeChanged);
|
on<StopTypeChangedEvent>(_onStopTypeChanged);
|
||||||
on<IsoChangedEvent>(_onIsoChanged);
|
on<IsoChangedEvent>(_onIsoChanged);
|
||||||
on<NdChangedEvent>(_onNdChanged);
|
on<NdChangedEvent>(_onNdChanged);
|
||||||
on<MeasureEvent>(_onMeasure);
|
on<MeasureEvent>((_, __) => _communicationBloc.add(const communication_events.MeasureEvent()));
|
||||||
|
on<MeasuredEvent>(_onMeasured);
|
||||||
|
|
||||||
add(const MeasureEvent());
|
add(const MeasureEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _communicationSubscription.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onCommunicationState(communication_states.ScreenState communicationState) {
|
||||||
|
if (communicationState is communication_states.MeasuredState) {
|
||||||
|
add(MeasuredEvent(communicationState.ev100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://www.scantips.com/lights/exposurecalc.html
|
||||||
|
Future<double> measureEv() async {
|
||||||
|
final aperture = _apertureValues[_random.nextInt(_apertureValues.length)];
|
||||||
|
final shutterSpeed = _shutterSpeedValues[_random.nextInt(_shutterSpeedValues.thirdStops().length)];
|
||||||
|
final iso = isoValues[_random.nextInt(isoValues.thirdStops().length)];
|
||||||
|
|
||||||
|
final evAtSystemIso = log2(pow(aperture.value, 2).toDouble() / shutterSpeed.value);
|
||||||
|
return evAtSystemIso - log2(iso.value / state.iso.value);
|
||||||
|
}
|
||||||
|
|
||||||
void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) {
|
void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) {
|
||||||
stopType = event.stopType;
|
stopType = event.stopType;
|
||||||
emit(MeteringState(
|
emit(MeteringState(
|
||||||
|
@ -70,15 +105,8 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://www.scantips.com/lights/exposurecalc.html
|
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
||||||
void _onMeasure(_, Emitter emit) {
|
final ev = event.ev100 + log2(state.iso.value / 100);
|
||||||
final aperture = _apertureValues[_random.nextInt(_apertureValues.length)];
|
|
||||||
final shutterSpeed = _shutterSpeedValues[_random.nextInt(_shutterSpeedValues.thirdStops().length)];
|
|
||||||
final iso = isoValues[_random.nextInt(isoValues.thirdStops().length)];
|
|
||||||
|
|
||||||
final evAtSystemIso = log2(pow(aperture.value, 2).toDouble() / shutterSpeed.value);
|
|
||||||
final ev = evAtSystemIso - log2(iso.value / state.iso.value);
|
|
||||||
|
|
||||||
emit(MeteringState(
|
emit(MeteringState(
|
||||||
iso: state.iso,
|
iso: state.iso,
|
||||||
ev: ev,
|
ev: ev,
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import 'event_communication_metering.dart';
|
||||||
|
import 'state_communication_metering.dart';
|
||||||
|
|
||||||
|
class MeteringCommunicationBloc extends Bloc<MeteringCommunicationEvent, MeteringCommunicationState> {
|
||||||
|
MeteringCommunicationBloc() : super(const InitState()) {
|
||||||
|
on<MeasureEvent>((_, emit) => emit(const MeasureState()));
|
||||||
|
on<MeasuredEvent>((event, emit) => emit(MeasuredState(event.ev100)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
abstract class MeteringCommunicationEvent {
|
||||||
|
const MeteringCommunicationEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SourceEvent extends MeteringCommunicationEvent {
|
||||||
|
const SourceEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ScreenEvent extends MeteringCommunicationEvent {
|
||||||
|
const ScreenEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeasureEvent extends ScreenEvent {
|
||||||
|
const MeasureEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeasuredEvent extends SourceEvent {
|
||||||
|
final double ev100;
|
||||||
|
|
||||||
|
const MeasuredEvent(this.ev100);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
abstract class MeteringCommunicationState {
|
||||||
|
const MeteringCommunicationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitState extends MeteringCommunicationState {
|
||||||
|
const InitState();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SourceState extends MeteringCommunicationState {
|
||||||
|
const SourceState();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ScreenState extends MeteringCommunicationState {
|
||||||
|
const ScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeasureState extends SourceState {
|
||||||
|
const MeasureState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MeasuredState extends ScreenState {
|
||||||
|
final double ev100;
|
||||||
|
|
||||||
|
const MeasuredState(this.ev100);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
|
class ZoomSlider extends StatelessWidget {
|
||||||
|
const ZoomSlider({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingL),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.zoom_out),
|
||||||
|
Expanded(
|
||||||
|
child: Slider(
|
||||||
|
value: 0,
|
||||||
|
onChanged: (value) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(Icons.zoom_in),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/camera/bloc_camera.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/camera/state_camera.dart';
|
||||||
|
|
||||||
|
class CameraView extends StatelessWidget {
|
||||||
|
const CameraView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 3 / 4,
|
||||||
|
child: BlocBuilder<CameraBloc, CameraState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CameraReadyState) {
|
||||||
|
final value = state.controller.value;
|
||||||
|
return ValueListenableBuilder<CameraValue>(
|
||||||
|
valueListenable: state.controller,
|
||||||
|
builder: (_, __, ___) => AspectRatio(
|
||||||
|
aspectRatio:
|
||||||
|
_isLandscape(value) ? value.aspectRatio : (1 / value.aspectRatio),
|
||||||
|
child: RotatedBox(
|
||||||
|
quarterTurns: _getQuarterTurns(value),
|
||||||
|
child: state.controller.buildPreview(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const ColoredBox(color: Colors.black);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isLandscape(CameraValue value) {
|
||||||
|
return <DeviceOrientation>[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]
|
||||||
|
.contains(_getApplicableOrientation(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getQuarterTurns(CameraValue value) {
|
||||||
|
final Map<DeviceOrientation, int> turns = <DeviceOrientation, int>{
|
||||||
|
DeviceOrientation.portraitUp: 0,
|
||||||
|
DeviceOrientation.landscapeRight: 1,
|
||||||
|
DeviceOrientation.portraitDown: 2,
|
||||||
|
DeviceOrientation.landscapeLeft: 3,
|
||||||
|
};
|
||||||
|
return turns[_getApplicableOrientation(value)]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceOrientation _getApplicableOrientation(CameraValue value) {
|
||||||
|
return value.isRecordingVideo
|
||||||
|
? value.recordingOrientation!
|
||||||
|
: (value.previewPauseOrientation ?? value.lockedCaptureOrientation ?? value.deviceOrientation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import 'package:lightmeter/models/nd_value.dart';
|
||||||
import 'package:lightmeter/models/photography_value.dart';
|
import 'package:lightmeter/models/photography_value.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
|
||||||
|
import 'components/camera_preview.dart';
|
||||||
import 'components/shared/animated_dialog.dart';
|
import 'components/shared/animated_dialog.dart';
|
||||||
import 'components/dialog_picker.dart';
|
import 'components/dialog_picker.dart';
|
||||||
import 'components/reading_container.dart';
|
import 'components/reading_container.dart';
|
||||||
|
@ -74,6 +75,13 @@ class MeteringTopBar extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
|
ReadingContainer.singleValue(
|
||||||
|
value: ReadingValue(
|
||||||
|
label: 'EV',
|
||||||
|
value: ev.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const _InnerPadding(),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -111,18 +119,8 @@ class MeteringTopBar extends StatelessWidget {
|
||||||
const _InnerPadding(),
|
const _InnerPadding(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
children: [
|
||||||
AnimatedDialog(
|
const CameraView(),
|
||||||
openedSize: Size(
|
|
||||||
MediaQuery.of(context).size.width - Dimens.paddingM * 2,
|
|
||||||
(MediaQuery.of(context).size.width - Dimens.paddingM * 2) / 3 * 4,
|
|
||||||
),
|
|
||||||
child: const AspectRatio(
|
|
||||||
aspectRatio: 3 / 4,
|
|
||||||
child: ColoredBox(color: Colors.black),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -27,3 +27,9 @@ class NdChangedEvent extends MeteringEvent {
|
||||||
class MeasureEvent extends MeteringEvent {
|
class MeasureEvent extends MeteringEvent {
|
||||||
const MeasureEvent();
|
const MeasureEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MeasuredEvent extends MeteringEvent {
|
||||||
|
final double ev100;
|
||||||
|
|
||||||
|
const MeasuredEvent(this.ev100);
|
||||||
|
}
|
29
lib/screens/metering/flow_metering.dart
Normal file
29
lib/screens/metering/flow_metering.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/camera/bloc_camera.dart';
|
||||||
|
import 'package:lightmeter/models/photography_value.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
|
|
||||||
|
import 'screen_metering.dart';
|
||||||
|
|
||||||
|
class MeteringFlow extends StatelessWidget {
|
||||||
|
const MeteringFlow({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider(create: (_) => MeteringCommunicationBloc()),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) => MeteringBloc(
|
||||||
|
context.read<MeteringCommunicationBloc>(),
|
||||||
|
context.read<StopType>(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
BlocProvider(create: (context) => CameraBloc(context.read<MeteringCommunicationBloc>())),
|
||||||
|
],
|
||||||
|
child: const MeteringScreen(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ import 'package:lightmeter/screens/settings/settings_screen.dart';
|
||||||
import 'components/bottom_controls/bottom_controls.dart';
|
import 'components/bottom_controls/bottom_controls.dart';
|
||||||
import 'components/exposure_pairs_list/exposure_pairs_list.dart';
|
import 'components/exposure_pairs_list/exposure_pairs_list.dart';
|
||||||
import 'components/topbar/topbar.dart';
|
import 'components/topbar/topbar.dart';
|
||||||
import 'metering_bloc.dart';
|
import 'bloc_metering.dart';
|
||||||
import 'metering_event.dart';
|
import 'event_metering.dart';
|
||||||
import 'metering_state.dart';
|
import 'state_metering.dart';
|
||||||
|
|
||||||
class MeteringScreen extends StatefulWidget {
|
class MeteringScreen extends StatefulWidget {
|
||||||
const MeteringScreen({super.key});
|
const MeteringScreen({super.key});
|
|
@ -1,12 +1,14 @@
|
||||||
name: lightmeter
|
name: lightmeter
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
publish_to: 'none'
|
publish_to: "none"
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.0 <3.0.0'
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
camera: 0.10.0+4
|
||||||
|
exif: 3.1.2
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_bloc: ^8.1.1
|
flutter_bloc: ^8.1.1
|
||||||
|
@ -61,4 +63,4 @@ flutter:
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
|
|
||||||
flutter_intl:
|
flutter_intl:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
Loading…
Reference in a new issue