mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
added MeteringBloc
This commit is contained in:
parent
888a9a6a76
commit
b6e377959f
9 changed files with 199 additions and 84 deletions
|
@ -1,6 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'models/photography_value.dart';
|
||||
import 'res/theme.dart';
|
||||
import 'screens/metering/metering_bloc.dart';
|
||||
import 'screens/metering/metering_screen.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -12,13 +15,16 @@ class MyApp extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: lightColorScheme,
|
||||
return BlocProvider(
|
||||
create: (context) => MeteringBloc(Stop.third),
|
||||
child: MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: lightColorScheme,
|
||||
),
|
||||
home: const MeteringScreen(),
|
||||
),
|
||||
home: const MeteringScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
8
lib/models/exposure_pair.dart
Normal file
8
lib/models/exposure_pair.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
import 'photography_value.dart';
|
||||
|
||||
class ExposurePair {
|
||||
final ApertureValue aperture;
|
||||
final ShutterSpeedValue shutterSpeed;
|
||||
|
||||
const ExposurePair(this.aperture, this.shutterSpeed);
|
||||
}
|
|
@ -1,56 +1,12 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/models/photography_value.dart';
|
||||
import 'package:lightmeter/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart';
|
||||
|
||||
class ExposurePairsList extends StatelessWidget {
|
||||
final double ev;
|
||||
final Stop stopType;
|
||||
final List<ApertureValue> _apertureValuesList;
|
||||
final List<ShutterSpeedValue> _shutterSpeedValuesList;
|
||||
late final int _apertureOffset;
|
||||
late final int _shutterSpeedOffset;
|
||||
late final int _itemsCount;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
||||
ExposurePairsList({
|
||||
required this.ev,
|
||||
required this.stopType,
|
||||
super.key,
|
||||
}) : _apertureValuesList = apertureValues.whereStopType(stopType),
|
||||
_shutterSpeedValuesList = shutterSpeedValues.whereStopType(stopType) {
|
||||
late final int evSteps;
|
||||
switch (stopType) {
|
||||
case Stop.full:
|
||||
evSteps = ev.floor();
|
||||
break;
|
||||
case Stop.half:
|
||||
evSteps = (ev / 0.5).floor();
|
||||
break;
|
||||
case Stop.third:
|
||||
evSteps = (ev / 0.3).floor();
|
||||
break;
|
||||
}
|
||||
|
||||
final evOffset = _shutterSpeedValuesList.indexOf(const ShutterSpeedValue(1, false, Stop.full)) - evSteps;
|
||||
if (evOffset >= 0) {
|
||||
_apertureOffset = 0;
|
||||
_shutterSpeedOffset = evOffset;
|
||||
} else {
|
||||
_apertureOffset = -evOffset;
|
||||
_shutterSpeedOffset = 0;
|
||||
}
|
||||
|
||||
_itemsCount = min(
|
||||
_apertureValuesList.length + _shutterSpeedOffset,
|
||||
_shutterSpeedValuesList.length + _apertureOffset,
|
||||
) -
|
||||
max(
|
||||
_apertureOffset,
|
||||
_shutterSpeedOffset,
|
||||
);
|
||||
}
|
||||
const ExposurePairsList(this.exposurePairs, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -59,7 +15,8 @@ class ExposurePairsList extends StatelessWidget {
|
|||
children: [
|
||||
Positioned.fill(
|
||||
child: ListView.builder(
|
||||
itemCount: _itemsCount,
|
||||
key: ValueKey(exposurePairs.hashCode),
|
||||
itemCount: exposurePairs.length,
|
||||
itemBuilder: (_, index) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -67,7 +24,7 @@ class ExposurePairsList extends StatelessWidget {
|
|||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ExposurePaitListItem(
|
||||
_apertureValuesList[index + _apertureOffset],
|
||||
exposurePairs[index].aperture,
|
||||
tickOnTheLeft: false,
|
||||
),
|
||||
),
|
||||
|
@ -77,7 +34,7 @@ class ExposurePairsList extends StatelessWidget {
|
|||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ExposurePaitListItem(
|
||||
_shutterSpeedValuesList[index + _shutterSpeedOffset],
|
||||
exposurePairs[index].shutterSpeed,
|
||||
tickOnTheLeft: true,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/topbar/components/reading_container.dart';
|
||||
import 'package:lightmeter/screens/metering/components/topbar/topbar_shape.dart';
|
||||
import 'package:lightmeter/utils/text_line_height.dart';
|
||||
|
||||
import 'components/reading_container.dart';
|
||||
|
||||
class MeteringTopBar extends StatelessWidget {
|
||||
static const _columnsCount = 3;
|
||||
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final double ev;
|
||||
final int iso;
|
||||
final double nd;
|
||||
|
||||
const MeteringTopBar({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.ev,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
|
@ -43,14 +47,18 @@ class MeteringTopBar extends StatelessWidget {
|
|||
SizedBox(
|
||||
height: columnWidth / 3 * 4,
|
||||
child: ReadingContainer(
|
||||
values: const [
|
||||
values: [
|
||||
ReadingValue(
|
||||
label: 'Fastest',
|
||||
value: 'f/5.6 - 1/2000',
|
||||
value: fastest != null
|
||||
? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}'
|
||||
: 'N/A',
|
||||
),
|
||||
ReadingValue(
|
||||
label: 'Slowest',
|
||||
value: 'f/45 - 1/30',
|
||||
value: fastest != null
|
||||
? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}'
|
||||
: 'N/A',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -63,7 +71,7 @@ class MeteringTopBar extends StatelessWidget {
|
|||
child: ReadingContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: 'EV',
|
||||
value: ev.toString(),
|
||||
value: ev.toStringAsFixed(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
90
lib/screens/metering/metering_bloc.dart
Normal file
90
lib/screens/metering/metering_bloc.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/models/photography_value.dart';
|
||||
|
||||
import 'metering_event.dart';
|
||||
import 'metering_state.dart';
|
||||
|
||||
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||
final Stop stopType;
|
||||
late final _apertureValues = apertureValues.whereStopType(stopType);
|
||||
late final _shutterSpeedValues = shutterSpeedValues.whereStopType(stopType);
|
||||
late final _isoValues = isoValues.whereStopType(stopType);
|
||||
final _random = Random();
|
||||
|
||||
MeteringBloc(this.stopType)
|
||||
: super(
|
||||
const MeteringState(
|
||||
iso: 100,
|
||||
ev: 21.3,
|
||||
evCompensation: 0.0,
|
||||
nd: 0.0,
|
||||
exposurePairs: [],
|
||||
),
|
||||
) {
|
||||
on<MeasureEvent>(_onMeasure);
|
||||
|
||||
add(const MeasureEvent());
|
||||
}
|
||||
|
||||
/// https://stackoverflow.com/questions/5401738/how-to-convert-between-lux-and-exposure-value
|
||||
void _onMeasure(_, Emitter emit) {
|
||||
double log2(double x) => log(x) / log(2);
|
||||
|
||||
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()) - log2(shutterSpeed.value);
|
||||
final ev = evAtSystemIso - log2(iso.value / state.iso);
|
||||
final exposurePairs = _buildExposureValues(ev);
|
||||
|
||||
emit(MeteringState(
|
||||
iso: state.iso,
|
||||
ev: ev,
|
||||
evCompensation: state.evCompensation,
|
||||
nd: state.nd,
|
||||
exposurePairs: exposurePairs,
|
||||
));
|
||||
}
|
||||
|
||||
List<ExposurePair> _buildExposureValues(double ev) {
|
||||
late final int evSteps;
|
||||
switch (stopType) {
|
||||
case Stop.full:
|
||||
evSteps = ev.floor();
|
||||
break;
|
||||
case Stop.half:
|
||||
evSteps = (ev / 0.5).floor();
|
||||
break;
|
||||
case Stop.third:
|
||||
evSteps = (ev / 0.3).floor();
|
||||
break;
|
||||
}
|
||||
final evOffset = _shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, Stop.full)) - evSteps;
|
||||
|
||||
late final int apertureOffset;
|
||||
late final int shutterSpeedOffset;
|
||||
if (evOffset >= 0) {
|
||||
apertureOffset = 0;
|
||||
shutterSpeedOffset = evOffset;
|
||||
} else {
|
||||
apertureOffset = -evOffset;
|
||||
shutterSpeedOffset = 0;
|
||||
}
|
||||
|
||||
int itemsCount = min(_apertureValues.length + shutterSpeedOffset, _shutterSpeedValues.length + apertureOffset) -
|
||||
max(apertureOffset, shutterSpeedOffset);
|
||||
|
||||
return List.generate(
|
||||
itemsCount,
|
||||
(index) => ExposurePair(
|
||||
_apertureValues[index + apertureOffset],
|
||||
_shutterSpeedValues[index + shutterSpeedOffset],
|
||||
),
|
||||
growable: false,
|
||||
);
|
||||
}
|
||||
}
|
7
lib/screens/metering/metering_event.dart
Normal file
7
lib/screens/metering/metering_event.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
abstract class MeteringEvent {
|
||||
const MeteringEvent();
|
||||
}
|
||||
|
||||
class MeasureEvent extends MeteringEvent {
|
||||
const MeasureEvent();
|
||||
}
|
|
@ -1,37 +1,55 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/models/photography_value.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/metering_event.dart';
|
||||
|
||||
import 'components/bottom_controls/bottom_controls.dart';
|
||||
import 'components/exposure_pairs_list/exposure_pairs_list.dart';
|
||||
import 'components/topbar/topbar.dart';
|
||||
import 'metering_bloc.dart';
|
||||
import 'metering_state.dart';
|
||||
|
||||
class MeteringScreen extends StatelessWidget {
|
||||
const MeteringScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const ev = 0.3;
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
body: Column(
|
||||
children: [
|
||||
const MeteringTopBar(
|
||||
ev: ev,
|
||||
iso: 6400,
|
||||
nd: 0,
|
||||
),
|
||||
Expanded(
|
||||
child: ExposurePairsList(
|
||||
ev: ev,
|
||||
stopType: Stop.third,
|
||||
),
|
||||
),
|
||||
MeteringBottomControls(
|
||||
onSourceChanged: () {},
|
||||
onMeasure: () {},
|
||||
onSettings: () {},
|
||||
),
|
||||
],
|
||||
body: BlocBuilder<MeteringBloc, MeteringState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
MeteringTopBar(
|
||||
fastest: state.fastest,
|
||||
slowest: state.slowest,
|
||||
ev: state.ev,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: ExposurePairsList(state.exposurePairs),
|
||||
),
|
||||
const SizedBox(width: Dimens.grid16),
|
||||
const Spacer()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
MeteringBottomControls(
|
||||
onSourceChanged: () {},
|
||||
onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
|
||||
onSettings: () {},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
20
lib/screens/metering/metering_state.dart
Normal file
20
lib/screens/metering/metering_state.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'package:lightmeter/models/exposure_pair.dart';
|
||||
|
||||
class MeteringState {
|
||||
final double ev;
|
||||
final double evCompensation;
|
||||
final int iso;
|
||||
final double nd;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
||||
const MeteringState({
|
||||
required this.ev,
|
||||
required this.evCompensation,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.exposurePairs,
|
||||
});
|
||||
|
||||
ExposurePair? get fastest => exposurePairs.isEmpty ? null : exposurePairs.first;
|
||||
ExposurePair? get slowest => exposurePairs.isEmpty ? null : exposurePairs.last;
|
||||
}
|
|
@ -9,6 +9,7 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_bloc: ^8.1.1
|
||||
material_color_utilities: ^0.2.0
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in a new issue