mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
Added mock ev bloc
This commit is contained in:
parent
a054a55f15
commit
07dc5d8f57
13 changed files with 176 additions and 92 deletions
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
|
@ -12,6 +12,17 @@
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"dev",
|
"dev",
|
||||||
],
|
],
|
||||||
|
"program": "${workspaceFolder}/lib/main.dart",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dev (mock)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"dev",
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/lib/main_mock.dart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "prod",
|
"name": "prod",
|
||||||
|
@ -21,6 +32,7 @@
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"prod",
|
"prod",
|
||||||
],
|
],
|
||||||
|
"program": "${workspaceFolder}/lib/main.dart",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
72
lib/application.dart
Normal file
72
lib/application.dart
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/ev_source_type.dart';
|
||||||
|
import 'package:lightmeter/data/permissions_service.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/settings_screen.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
import 'res/theme.dart';
|
||||||
|
import 'screens/metering/flow_metering.dart';
|
||||||
|
import 'utils/stop_type_provider.dart';
|
||||||
|
|
||||||
|
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
||||||
|
|
||||||
|
class Application extends StatefulWidget {
|
||||||
|
final EvSourceType evSource;
|
||||||
|
|
||||||
|
const Application(this.evSource, {super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Application> createState() => _ApplicationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ApplicationState extends State<Application> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final mySystemTheme = SystemUiOverlayStyle.light.copyWith(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
statusBarBrightness: Brightness.light,
|
||||||
|
statusBarIconBrightness: Brightness.dark,
|
||||||
|
systemNavigationBarColor: Colors.transparent,
|
||||||
|
systemNavigationBarIconBrightness: Brightness.dark,
|
||||||
|
);
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(mySystemTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Provider.value(
|
||||||
|
value: widget.evSource,
|
||||||
|
child: Provider(
|
||||||
|
create: (context) => PermissionsService(),
|
||||||
|
child: StopTypeProvider(
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
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 MeteringFlow(),
|
||||||
|
routes: {
|
||||||
|
"metering": (context) => const MeteringFlow(),
|
||||||
|
"settings": (context) => const SettingsScreen(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import 'package:camera/camera.dart';
|
||||||
import 'package:exif/exif.dart';
|
import 'package:exif/exif.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/ev_source_bloc.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.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/event_communication_metering.dart' as communication_event;
|
||||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
||||||
|
@ -14,21 +15,20 @@ import 'package:lightmeter/utils/log_2.dart';
|
||||||
import 'event_camera.dart';
|
import 'event_camera.dart';
|
||||||
import 'state_camera.dart';
|
import 'state_camera.dart';
|
||||||
|
|
||||||
class CameraBloc extends Bloc<CameraEvent, CameraState> {
|
class CameraBloc extends EvSourceBloc<CameraEvent, CameraState> {
|
||||||
final MeteringCommunicationBloc _communicationBloc;
|
|
||||||
late final StreamSubscription<communication_states.MeteringCommunicationState> _communicationSubscription;
|
|
||||||
|
|
||||||
late final _WidgetsBindingObserver _observer;
|
late final _WidgetsBindingObserver _observer;
|
||||||
CameraController? _cameraController;
|
CameraController? _cameraController;
|
||||||
CameraController? get cameraController => _cameraController;
|
CameraController? get cameraController => _cameraController;
|
||||||
|
|
||||||
CameraBloc(this._communicationBloc) : super(const CameraInitState()) {
|
CameraBloc(MeteringCommunicationBloc communicationBloc)
|
||||||
_communicationSubscription = _communicationBloc.stream.listen(_onCommunicationState);
|
: super(
|
||||||
|
communicationBloc,
|
||||||
|
const CameraInitState(),
|
||||||
|
) {
|
||||||
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
||||||
WidgetsBinding.instance.addObserver(_observer);
|
WidgetsBinding.instance.addObserver(_observer);
|
||||||
|
|
||||||
on<InitializeEvent>(_onInitialized);
|
on<InitializeEvent>(_onInitialize);
|
||||||
|
|
||||||
add(const InitializeEvent());
|
add(const InitializeEvent());
|
||||||
}
|
}
|
||||||
|
@ -37,21 +37,21 @@ class CameraBloc extends Bloc<CameraEvent, CameraState> {
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
WidgetsBinding.instance.removeObserver(_observer);
|
WidgetsBinding.instance.removeObserver(_observer);
|
||||||
_cameraController?.dispose();
|
_cameraController?.dispose();
|
||||||
await _communicationSubscription.cancel();
|
|
||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCommunicationState(communication_states.MeteringCommunicationState communicationState) {
|
@override
|
||||||
|
void onCommunicationState(communication_states.SourceState communicationState) {
|
||||||
if (communicationState is communication_states.MeasureState) {
|
if (communicationState is communication_states.MeasureState) {
|
||||||
_takePhoto().then((ev100) {
|
_takePhoto().then((ev100) {
|
||||||
if (ev100 != null) {
|
if (ev100 != null) {
|
||||||
_communicationBloc.add(communication_event.MeasuredEvent(ev100));
|
communicationBloc.add(communication_event.MeasuredEvent(ev100));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onInitialized(_, Emitter emit) async {
|
Future<void> _onInitialize(_, Emitter emit) async {
|
||||||
emit(const CameraLoadingState());
|
emit(const CameraLoadingState());
|
||||||
try {
|
try {
|
||||||
final cameras = await availableCameras();
|
final cameras = await availableCameras();
|
||||||
|
@ -69,7 +69,7 @@ class CameraBloc extends Bloc<CameraEvent, CameraState> {
|
||||||
emit(CameraReadyState(_cameraController!));
|
emit(CameraReadyState(_cameraController!));
|
||||||
_takePhoto().then((ev100) {
|
_takePhoto().then((ev100) {
|
||||||
if (ev100 != null) {
|
if (ev100 != null) {
|
||||||
_communicationBloc.add(communication_event.MeasuredEvent(ev100));
|
communicationBloc.add(communication_event.MeasuredEvent(ev100));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
24
lib/data/ev_source/ev_source_bloc.dart
Normal file
24
lib/data/ev_source/ev_source_bloc.dart
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states;
|
||||||
|
|
||||||
|
abstract class EvSourceBloc<E, S> extends Bloc<E, S> {
|
||||||
|
final MeteringCommunicationBloc communicationBloc;
|
||||||
|
late final StreamSubscription<communication_states.SourceState> _communicationSubscription;
|
||||||
|
|
||||||
|
EvSourceBloc(this.communicationBloc, super.initialState) {
|
||||||
|
_communicationSubscription = communicationBloc.stream
|
||||||
|
.where((event) => event is communication_states.SourceState)
|
||||||
|
.map((event) => event as communication_states.SourceState)
|
||||||
|
.listen(onCommunicationState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() async {
|
||||||
|
await _communicationSubscription.cancel();
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCommunicationState(communication_states.SourceState communicationState);
|
||||||
|
}
|
1
lib/data/ev_source/ev_source_type.dart
Normal file
1
lib/data/ev_source/ev_source_type.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
enum EvSourceType { camera, mock }
|
25
lib/data/ev_source/random_ev/bloc_random_ev.dart
Normal file
25
lib/data/ev_source/random_ev/bloc_random_ev.dart
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:lightmeter/data/ev_source/ev_source_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 'event_random_ev.dart';
|
||||||
|
import 'state_random_ev.dart';
|
||||||
|
|
||||||
|
class RandomEvBloc extends EvSourceBloc<RandomEvEvent, RandomEvState> {
|
||||||
|
final random = Random();
|
||||||
|
|
||||||
|
RandomEvBloc(MeteringCommunicationBloc communicationBloc)
|
||||||
|
: super(
|
||||||
|
communicationBloc,
|
||||||
|
RandomEvState(Random().nextDouble() * 15),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onCommunicationState(communication_states.SourceState communicationState) {
|
||||||
|
if (communicationState is communication_states.MeasureState) {
|
||||||
|
communicationBloc.add(communication_event.MeasuredEvent(random.nextDouble() * 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
lib/data/ev_source/random_ev/event_random_ev.dart
Normal file
3
lib/data/ev_source/random_ev/event_random_ev.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
abstract class RandomEvEvent {
|
||||||
|
const RandomEvEvent();
|
||||||
|
}
|
5
lib/data/ev_source/random_ev/state_random_ev.dart
Normal file
5
lib/data/ev_source/random_ev/state_random_ev.dart
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class RandomEvState {
|
||||||
|
final double ev;
|
||||||
|
|
||||||
|
const RandomEvState(this.ev);
|
||||||
|
}
|
|
@ -1,70 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:lightmeter/data/ev_source/ev_source_type.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
|
||||||
import 'package:lightmeter/screens/settings/settings_screen.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
import 'generated/l10n.dart';
|
import 'application.dart';
|
||||||
import 'res/theme.dart';
|
|
||||||
import 'screens/metering/flow_metering.dart';
|
|
||||||
import 'utils/stop_type_provider.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const Application());
|
runApp(const Application(EvSourceType.camera));
|
||||||
}
|
|
||||||
|
|
||||||
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
|
||||||
|
|
||||||
class Application extends StatefulWidget {
|
|
||||||
const Application({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<Application> createState() => _ApplicationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ApplicationState extends State<Application> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
final mySystemTheme = SystemUiOverlayStyle.light.copyWith(
|
|
||||||
statusBarColor: Colors.transparent,
|
|
||||||
statusBarBrightness: Brightness.light,
|
|
||||||
statusBarIconBrightness: Brightness.dark,
|
|
||||||
systemNavigationBarColor: Colors.transparent,
|
|
||||||
systemNavigationBarIconBrightness: Brightness.dark,
|
|
||||||
);
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(mySystemTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Provider(
|
|
||||||
create: (context) => PermissionsService(),
|
|
||||||
child: StopTypeProvider(
|
|
||||||
child: MaterialApp(
|
|
||||||
theme: ThemeData(
|
|
||||||
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 MeteringFlow(),
|
|
||||||
routes: {
|
|
||||||
"metering": (context) => const MeteringFlow(),
|
|
||||||
"settings": (context) => const SettingsScreen(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
8
lib/main_mock.dart
Normal file
8
lib/main_mock.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/ev_source_type.dart';
|
||||||
|
|
||||||
|
import 'application.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const Application(EvSourceType.mock));
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
StopType stopType;
|
StopType stopType;
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
: super(
|
: super(
|
||||||
MeteringState(
|
MeteringState(
|
||||||
iso: isoValues.where((element) => element.value == 100).first,
|
iso: isoValues.where((element) => element.value == 100).first,
|
||||||
ev: 21.3,
|
ev: 0.0,
|
||||||
evCompensation: 0.0,
|
evCompensation: 0.0,
|
||||||
nd: ndValues.first,
|
nd: ndValues.first,
|
||||||
exposurePairs: [],
|
exposurePairs: [],
|
||||||
|
@ -62,16 +61,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(
|
||||||
|
|
|
@ -63,13 +63,13 @@ class MeteringTopBar extends StatelessWidget {
|
||||||
label: S.of(context).fastestExposurePair,
|
label: S.of(context).fastestExposurePair,
|
||||||
value: fastest != null
|
value: fastest != null
|
||||||
? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}'
|
? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}'
|
||||||
: 'N/A',
|
: '-',
|
||||||
),
|
),
|
||||||
ReadingValue(
|
ReadingValue(
|
||||||
label: S.of(context).slowestExposurePair,
|
label: S.of(context).slowestExposurePair,
|
||||||
value: fastest != null
|
value: fastest != null
|
||||||
? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}'
|
? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}'
|
||||||
: 'N/A',
|
: '-',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/bloc_camera.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/ev_source_type.dart';
|
||||||
|
import 'package:lightmeter/data/ev_source/random_ev/bloc_random_ev.dart';
|
||||||
import 'package:lightmeter/models/photography_value.dart';
|
import 'package:lightmeter/models/photography_value.dart';
|
||||||
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
|
@ -22,6 +24,11 @@ class MeteringFlow extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => CameraBloc(context.read<MeteringCommunicationBloc>())),
|
BlocProvider(create: (context) => CameraBloc(context.read<MeteringCommunicationBloc>())),
|
||||||
|
if (context.read<EvSourceType>() == EvSourceType.mock)
|
||||||
|
BlocProvider(
|
||||||
|
lazy: false,
|
||||||
|
create: (context) => RandomEvBloc(context.read<MeteringCommunicationBloc>()),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MeteringScreen(),
|
child: const MeteringScreen(),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue