diff --git a/lib/models/photography_value.dart b/lib/models/photography_value.dart index 2e319bb..f800255 100644 --- a/lib/models/photography_value.dart +++ b/lib/models/photography_value.dart @@ -13,10 +13,21 @@ abstract class PhotographyValue { T get value; } -extension PhotographyValues on List> { - List> fullStops() => where((e) => e.stopType == Stop.full).toList(); +extension PhotographyValues on List { + List whereStopType(Stop stopType) { + switch (stopType) { + case Stop.full: + return where((e) => e.stopType == Stop.full).toList(); + case Stop.half: + return where((e) => e.stopType == Stop.full || e.stopType == Stop.half).toList(); + case Stop.third: + return where((e) => e.stopType == Stop.full || e.stopType == Stop.third).toList(); + } + } - List> halfStops() => where((e) => e.stopType == Stop.full || e.stopType == Stop.half).toList(); + List fullStops() => whereStopType(Stop.full); - List> thirdStops() => where((e) => e.stopType == Stop.full || e.stopType == Stop.third).toList(); + List halfStops() => whereStopType(Stop.half); + + List thirdStops() => whereStopType(Stop.third); } diff --git a/lib/res/dimens.dart b/lib/res/dimens.dart index 39c568e..6eb3607 100644 --- a/lib/res/dimens.dart +++ b/lib/res/dimens.dart @@ -7,6 +7,7 @@ class Dimens { static const double grid4 = 4; static const double grid8 = 8; static const double grid16 = 16; + static const double grid24 = 24; static const double paddingM = 16; } diff --git a/lib/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart b/lib/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart new file mode 100644 index 0000000..d60211a --- /dev/null +++ b/lib/screens/metering/components/exposure_pairs_list/components/exposure_pair_item.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/models/photography_value.dart'; +import 'package:lightmeter/res/dimens.dart'; + +class ExposurePaitListItem extends StatelessWidget { + final T value; + final bool tickOnTheLeft; + + const ExposurePaitListItem(this.value, {required this.tickOnTheLeft, super.key}); + + @override + Widget build(BuildContext context) { + final List rowChildren = [ + Text( + value.toString(), + style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onBackground), + ), + const SizedBox(width: Dimens.grid8), + ColoredBox( + color: Theme.of(context).colorScheme.onBackground, + child: SizedBox( + height: 1, + width: tickLength(), + ), + ), + ]; + return Row( + mainAxisAlignment: tickOnTheLeft ? MainAxisAlignment.start : MainAxisAlignment.end, + children: tickOnTheLeft ? rowChildren.reversed.toList() : rowChildren, + ); + } + + TextStyle labelTextStyle(BuildContext context) { + switch (value.stopType) { + case Stop.full: + return Theme.of(context).textTheme.bodyLarge!; + case Stop.half: + return Theme.of(context).textTheme.bodyMedium!; + case Stop.third: + return Theme.of(context).textTheme.bodySmall!; + } + } + + double tickLength() { + switch (value.stopType) { + case Stop.full: + return Dimens.grid24; + case Stop.half: + return Dimens.grid16; + case Stop.third: + return Dimens.grid8; + } + } +} diff --git a/lib/screens/metering/components/exposure_pairs_list/exposure_pairs_list.dart b/lib/screens/metering/components/exposure_pairs_list/exposure_pairs_list.dart new file mode 100644 index 0000000..22b9609 --- /dev/null +++ b/lib/screens/metering/components/exposure_pairs_list/exposure_pairs_list.dart @@ -0,0 +1,100 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:lightmeter/models/photography_value.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 _apertureValuesList; + final List _shutterSpeedValuesList; + late final int _apertureOffset; + late final int _shutterSpeedOffset; + late final int _itemsCount; + + 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, + ); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + Positioned.fill( + child: ListView.builder( + itemCount: _itemsCount, + itemBuilder: (_, index) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: ExposurePaitListItem( + _apertureValuesList[index + _apertureOffset], + tickOnTheLeft: false, + ), + ), + ), + const SizedBox(width: Dimens.grid16), + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: ExposurePaitListItem( + _shutterSpeedValuesList[index + _shutterSpeedOffset], + tickOnTheLeft: true, + ), + ), + ), + ], + ), + ), + ), + Positioned( + top: 0, + bottom: 0, + child: ColoredBox( + color: Theme.of(context).colorScheme.onBackground, + child: const SizedBox(width: 1), + ), + ), + ], + ); + } +} diff --git a/lib/screens/metering/metering_screen.dart b/lib/screens/metering/metering_screen.dart index 803eab0..2a0dfa9 100644 --- a/lib/screens/metering/metering_screen.dart +++ b/lib/screens/metering/metering_screen.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/models/photography_value.dart'; +import 'components/exposure_pairs_list/exposure_pairs_list.dart'; import 'components/topbar/topbar.dart'; class MeteringScreen extends StatelessWidget { @@ -7,17 +9,23 @@ class MeteringScreen extends StatelessWidget { @override Widget build(BuildContext context) { + const ev = 0.3; return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, body: Column( - children: const [ - MeteringTopBar( + children: [ + const MeteringTopBar( lux: 283, - ev: 2.3, + ev: ev, iso: 6400, nd: 0, ), - Spacer() + Expanded( + child: ExposurePairsList( + ev: ev, + stopType: Stop.third, + ), + ), ], ), );