added MeteringBloc

This commit is contained in:
Vadim 2022-10-29 21:02:45 +03:00
parent 888a9a6a76
commit b6e377959f
9 changed files with 199 additions and 84 deletions

View file

@ -1,6 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'models/photography_value.dart';
import 'res/theme.dart'; import 'res/theme.dart';
import 'screens/metering/metering_bloc.dart';
import 'screens/metering/metering_screen.dart'; import 'screens/metering/metering_screen.dart';
void main() { void main() {
@ -12,13 +15,16 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return BlocProvider(
create: (context) => MeteringBloc(Stop.third),
child: MaterialApp(
title: 'Flutter Demo', title: 'Flutter Demo',
theme: ThemeData( theme: ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: lightColorScheme, colorScheme: lightColorScheme,
), ),
home: const MeteringScreen(), home: const MeteringScreen(),
),
); );
} }
} }

View file

@ -0,0 +1,8 @@
import 'photography_value.dart';
class ExposurePair {
final ApertureValue aperture;
final ShutterSpeedValue shutterSpeed;
const ExposurePair(this.aperture, this.shutterSpeed);
}

View file

@ -1,56 +1,12 @@
import 'dart:math';
import 'package:flutter/material.dart'; 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/res/dimens.dart';
import 'package:lightmeter/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart'; import 'package:lightmeter/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart';
class ExposurePairsList extends StatelessWidget { class ExposurePairsList extends StatelessWidget {
final double ev; final List<ExposurePair> exposurePairs;
final Stop stopType;
final List<ApertureValue> _apertureValuesList;
final List<ShutterSpeedValue> _shutterSpeedValuesList;
late final int _apertureOffset;
late final int _shutterSpeedOffset;
late final int _itemsCount;
ExposurePairsList({ const ExposurePairsList(this.exposurePairs, {super.key});
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,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -59,7 +15,8 @@ class ExposurePairsList extends StatelessWidget {
children: [ children: [
Positioned.fill( Positioned.fill(
child: ListView.builder( child: ListView.builder(
itemCount: _itemsCount, key: ValueKey(exposurePairs.hashCode),
itemCount: exposurePairs.length,
itemBuilder: (_, index) => Row( itemBuilder: (_, index) => Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -67,7 +24,7 @@ class ExposurePairsList extends StatelessWidget {
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: ExposurePaitListItem( child: ExposurePaitListItem(
_apertureValuesList[index + _apertureOffset], exposurePairs[index].aperture,
tickOnTheLeft: false, tickOnTheLeft: false,
), ),
), ),
@ -77,7 +34,7 @@ class ExposurePairsList extends StatelessWidget {
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: ExposurePaitListItem( child: ExposurePaitListItem(
_shutterSpeedValuesList[index + _shutterSpeedOffset], exposurePairs[index].shutterSpeed,
tickOnTheLeft: true, tickOnTheLeft: true,
), ),
), ),

View file

@ -1,17 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/models/exposure_pair.dart';
import 'package:lightmeter/res/dimens.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 'components/reading_container.dart';
import 'package:lightmeter/utils/text_line_height.dart';
class MeteringTopBar extends StatelessWidget { class MeteringTopBar extends StatelessWidget {
static const _columnsCount = 3; static const _columnsCount = 3;
final ExposurePair? fastest;
final ExposurePair? slowest;
final double ev; final double ev;
final int iso; final int iso;
final double nd; final double nd;
const MeteringTopBar({ const MeteringTopBar({
required this.fastest,
required this.slowest,
required this.ev, required this.ev,
required this.iso, required this.iso,
required this.nd, required this.nd,
@ -43,14 +47,18 @@ class MeteringTopBar extends StatelessWidget {
SizedBox( SizedBox(
height: columnWidth / 3 * 4, height: columnWidth / 3 * 4,
child: ReadingContainer( child: ReadingContainer(
values: const [ values: [
ReadingValue( ReadingValue(
label: 'Fastest', label: 'Fastest',
value: 'f/5.6 - 1/2000', value: fastest != null
? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}'
: 'N/A',
), ),
ReadingValue( ReadingValue(
label: 'Slowest', 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( child: ReadingContainer.singleValue(
value: ReadingValue( value: ReadingValue(
label: 'EV', label: 'EV',
value: ev.toString(), value: ev.toStringAsFixed(1),
), ),
), ),
), ),

View 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,
);
}
}

View file

@ -0,0 +1,7 @@
abstract class MeteringEvent {
const MeteringEvent();
}
class MeasureEvent extends MeteringEvent {
const MeasureEvent();
}

View file

@ -1,37 +1,55 @@
import 'package:flutter/material.dart'; 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/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 'metering_state.dart';
class MeteringScreen extends StatelessWidget { class MeteringScreen extends StatelessWidget {
const MeteringScreen({super.key}); const MeteringScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const ev = 0.3;
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background, backgroundColor: Theme.of(context).colorScheme.background,
body: Column( body: BlocBuilder<MeteringBloc, MeteringState>(
builder: (context, state) {
return Column(
children: [ children: [
const MeteringTopBar( MeteringTopBar(
ev: ev, fastest: state.fastest,
iso: 6400, slowest: state.slowest,
nd: 0, ev: state.ev,
iso: state.iso,
nd: state.nd,
), ),
Expanded( Expanded(
child: ExposurePairsList( child: Padding(
ev: ev, padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
stopType: Stop.third, child: Row(
children: [
Expanded(
flex: 2,
child: ExposurePairsList(state.exposurePairs),
),
const SizedBox(width: Dimens.grid16),
const Spacer()
],
),
), ),
), ),
MeteringBottomControls( MeteringBottomControls(
onSourceChanged: () {}, onSourceChanged: () {},
onMeasure: () {}, onMeasure: () => context.read<MeteringBloc>().add(const MeasureEvent()),
onSettings: () {}, onSettings: () {},
), ),
], ],
);
},
), ),
); );
} }

View 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;
}

View file

@ -9,6 +9,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_bloc: ^8.1.1
material_color_utilities: ^0.2.0 material_color_utilities: ^0.2.0
dev_dependencies: dev_dependencies: