mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-23 07:50:42 +00:00
ML-107 Films filter (#118)
* added stub `FilmsProvider` * moved dialogs to the shared folder * typo * separated `EquipmentSettingsSection` * copy * `IAPBuilder` -> `IAPListTile` * moved `Film` to resources repo * fixed films selection * untied iso and selected film * removed film from exposure pairs building * indicate push/pull * copy * Update .gitignore * fixed extreme exposure pairs reciprocity display * sync with iap changes * sync iap stub with iap changes * added reciprocity description * added workspace file * Update .gitignore
This commit is contained in:
parent
1be7c3be48
commit
cc9f162933
58 changed files with 592 additions and 978 deletions
9
.github/workflows/pr_check.yml
vendored
9
.github/workflows/pr_check.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
analyze_and_test:
|
||||
name: Analyze & test
|
||||
runs-on: macos-11
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
|
@ -47,3 +47,10 @@ jobs:
|
|||
|
||||
- name: Run tests
|
||||
run: flutter test
|
||||
|
||||
- name: Analyze project source with stub
|
||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
run: |
|
||||
bash ./.github/scripts/stub_iap.sh
|
||||
flutter pub get
|
||||
flutter analyze lib --fatal-infos
|
||||
|
|
25
README.md
25
README.md
|
@ -33,11 +33,7 @@ Without further delay behold my new Lightmeter app inspired by Material You (a.k
|
|||
|
||||
To build this app you need to install Flutter 3.10.0 stable. [How to install](https://docs.flutter.dev/get-started/install).
|
||||
|
||||
### 2. (Optional) Install Firebase
|
||||
|
||||
Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics to your local build please follow [this guide](https://firebase.google.com/docs/flutter/setup).
|
||||
|
||||
### 3. Get packages
|
||||
### 3. Project setup
|
||||
|
||||
As part of the app's functionallity is in the private repo, you have to replace these lines in _pubspec.yaml_:
|
||||
|
||||
|
@ -47,24 +43,39 @@ m3_lightmeter_iap:
|
|||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
||||
ref: main
|
||||
```
|
||||
|
||||
with these:
|
||||
|
||||
```yaml
|
||||
m3_lightmeter_iap:
|
||||
path: iap
|
||||
```
|
||||
and run `flutter pub get` from the _iap/_ folder.
|
||||
|
||||
You can do it simply by running the script:
|
||||
|
||||
```console
|
||||
sh .github/scripts/stub_iap.sh
|
||||
```
|
||||
|
||||
> If you are using VSCode, you can open the workspace like so: _File -> Open Workspace from File -> m3_lightmeter.code-workspace_. Otherwise you have to run `flutter pub get` command from the iap folder.
|
||||
|
||||
Then you can fetch all the neccessary dependencies and generate translation files by running the following commands:
|
||||
|
||||
```console
|
||||
flutter pub get
|
||||
flutter pub run intl_utils:generate
|
||||
```
|
||||
|
||||
### 4. Build
|
||||
### 4. (Optional) Install Firebase
|
||||
|
||||
Out of the box Firebase Crashlytics won't work. If you want to add Crashlytics to your local build please follow [this guide](https://firebase.google.com/docs/flutter/setup).
|
||||
|
||||
### 5. Build
|
||||
|
||||
#### Android
|
||||
|
||||
You can build an apk by running the following command from the root of the repository:
|
||||
|
||||
```console
|
||||
flutter build apk --release --flavor dev --dart-define cameraPreviewAspectRatio=240/320 -t lib/main_dev.dart
|
||||
```
|
||||
|
|
|
@ -2,11 +2,13 @@ library m3_lightmeter_iap;
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_iap/src/providers/equipment_profile_provider.dart';
|
||||
import 'package:m3_lightmeter_iap/src/providers/films_provider.dart';
|
||||
import 'package:m3_lightmeter_iap/src/providers/iap_products_provider.dart';
|
||||
|
||||
export 'src/data/models/iap_product.dart';
|
||||
|
||||
export 'src/providers/equipment_profile_provider.dart' hide EquipmentProfilesAspect;
|
||||
export 'src/providers/equipment_profile_provider.dart';
|
||||
export 'src/providers/films_provider.dart';
|
||||
export 'src/providers/iap_products_provider.dart';
|
||||
|
||||
class IAPProviders extends StatelessWidget {
|
||||
|
@ -22,8 +24,10 @@ class IAPProviders extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IAPProductsProvider(
|
||||
child: EquipmentProfileProvider(
|
||||
child: child,
|
||||
child: FilmsProvider(
|
||||
child: EquipmentProfileProvider(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_iap/src/providers/selectable_provider.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfileProvider extends StatefulWidget {
|
||||
|
@ -27,7 +28,7 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EquipmentProfiles(
|
||||
profiles: const [_defaultProfile],
|
||||
values: const [_defaultProfile],
|
||||
selected: _defaultProfile,
|
||||
child: widget.child,
|
||||
);
|
||||
|
@ -42,38 +43,19 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
|
|||
void deleteProfile(EquipmentProfile data) {}
|
||||
}
|
||||
|
||||
enum EquipmentProfilesAspect { list, selected }
|
||||
|
||||
class EquipmentProfiles extends InheritedModel<EquipmentProfilesAspect> {
|
||||
class EquipmentProfiles extends SelectableInheritedModel<EquipmentProfile> {
|
||||
const EquipmentProfiles({
|
||||
super.key,
|
||||
required this.profiles,
|
||||
required this.selected,
|
||||
required super.values,
|
||||
required super.selected,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final List<EquipmentProfile> profiles;
|
||||
final EquipmentProfile selected;
|
||||
|
||||
static List<EquipmentProfile> of(BuildContext context) {
|
||||
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
||||
context,
|
||||
aspect: EquipmentProfilesAspect.list,
|
||||
)!
|
||||
.profiles;
|
||||
return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: SelectableAspect.list)!.values;
|
||||
}
|
||||
|
||||
static EquipmentProfile selectedOf(BuildContext context) {
|
||||
return InheritedModel.inheritFrom<EquipmentProfiles>(
|
||||
context,
|
||||
aspect: EquipmentProfilesAspect.selected,
|
||||
)!
|
||||
.selected;
|
||||
return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: SelectableAspect.selected)!.selected;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(EquipmentProfiles oldWidget) => false;
|
||||
|
||||
@override
|
||||
bool updateShouldNotifyDependent(EquipmentProfiles oldWidget, Set<EquipmentProfilesAspect> dependencies) => false;
|
||||
}
|
||||
|
|
65
iap/lib/src/providers/films_provider.dart
Normal file
65
iap/lib/src/providers/films_provider.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_iap/src/providers/selectable_provider.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class FilmsProvider extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const FilmsProvider({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
static FilmsProviderState of(BuildContext context) {
|
||||
return context.findAncestorStateOfType<FilmsProviderState>()!;
|
||||
}
|
||||
|
||||
@override
|
||||
State<FilmsProvider> createState() => FilmsProviderState();
|
||||
}
|
||||
|
||||
class FilmsProviderState extends State<FilmsProvider> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Films(
|
||||
values: const [Film.other()],
|
||||
filmsInUse: const [Film.other()],
|
||||
selected: const Film.other(),
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
||||
void setFilm(Film film) {}
|
||||
|
||||
void saveFilms(List<Film> films) {}
|
||||
}
|
||||
|
||||
class Films extends SelectableInheritedModel<Film> {
|
||||
final List<Film> filmsInUse;
|
||||
|
||||
const Films({
|
||||
super.key,
|
||||
required super.values,
|
||||
required this.filmsInUse,
|
||||
required super.selected,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
/// [Film.other()] + all the custom fields with actual reciprocity formulas
|
||||
static List<Film> of(BuildContext context) {
|
||||
return InheritedModel.inheritFrom<Films>(context)!.values;
|
||||
}
|
||||
|
||||
/// [Film.other()] + films in use selected by user
|
||||
static List<Film> inUseOf<T>(BuildContext context) {
|
||||
return InheritedModel.inheritFrom<Films>(
|
||||
context,
|
||||
aspect: SelectableAspect.list,
|
||||
)!
|
||||
.filmsInUse;
|
||||
}
|
||||
|
||||
static Film selectedOf(BuildContext context) {
|
||||
return InheritedModel.inheritFrom<Films>(context, aspect: SelectableAspect.selected)!.selected;
|
||||
}
|
||||
}
|
29
iap/lib/src/providers/selectable_provider.dart
Normal file
29
iap/lib/src/providers/selectable_provider.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
enum SelectableAspect { list, selected }
|
||||
|
||||
class SelectableInheritedModel<T> extends InheritedModel<SelectableAspect> {
|
||||
const SelectableInheritedModel({
|
||||
super.key,
|
||||
required this.values,
|
||||
required this.selected,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final List<T> values;
|
||||
final T selected;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(SelectableInheritedModel oldWidget) => true;
|
||||
|
||||
@override
|
||||
bool updateShouldNotifyDependent(SelectableInheritedModel oldWidget, Set<SelectableAspect> dependencies) {
|
||||
if (dependencies.contains(SelectableAspect.list)) {
|
||||
return true;
|
||||
} else if (dependencies.contains(SelectableAspect.selected)) {
|
||||
return selected != oldWidget.selected;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
double log10(double x) => log(x) / log(10);
|
||||
|
||||
double log10polynomian(
|
||||
double x,
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
) =>
|
||||
a * pow(log10(x), 2) + b * log10(x) + c;
|
||||
|
||||
typedef ReciprocityFailureBuilder = ShutterSpeedValue Function(ShutterSpeedValue shutterSpeed);
|
||||
|
||||
/// Only Ilford films have reciprocity formulas provided by the manufacturer:
|
||||
/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf
|
||||
///
|
||||
/// Reciprocity formulas for Fomapan films and Kodak films are from here:
|
||||
/// https://www.flickr.com/groups/86738082@N00/discuss/72157626050157470/
|
||||
///
|
||||
/// Cinema films like Kodak 5222/7222 Double-X and respective CineStill films (cause they are basically a modification of Kodak)
|
||||
/// do not have any reciprocity failure information, as these films are ment to be used in cinema
|
||||
/// with appropriate light and pretty short shutter speeds.
|
||||
///
|
||||
/// Because of this: https://github.com/dart-lang/sdk/issues/38934#issuecomment-803938315
|
||||
/// `super` calls are ignored in test coverage
|
||||
class Film {
|
||||
final String name;
|
||||
final int iso;
|
||||
|
||||
const Film(this.name, this.iso);
|
||||
|
||||
const Film.other()
|
||||
: name = '',
|
||||
iso = 0;
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
|
||||
ShutterSpeedValue reciprocityFailure(ShutterSpeedValue shutterSpeed) {
|
||||
if (shutterSpeed.isFraction) {
|
||||
return shutterSpeed;
|
||||
} else {
|
||||
return ShutterSpeedValue(
|
||||
reciprocityFormula(shutterSpeed.rawValue),
|
||||
shutterSpeed.isFraction,
|
||||
shutterSpeed.stopType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
double reciprocityFormula(double t) => t;
|
||||
|
||||
static const List<Film> values = [
|
||||
Film.other(),
|
||||
FomapanFilm.creative100(),
|
||||
FomapanFilm.creative200(),
|
||||
FomapanFilm.action400(),
|
||||
IlfordFilm.ortho(),
|
||||
IlfordFilm.delta100(),
|
||||
IlfordFilm.delta400(),
|
||||
IlfordFilm.delta3200(),
|
||||
IlfordFilm.fp4(),
|
||||
IlfordFilm.hp5(),
|
||||
IlfordFilm.panf(),
|
||||
IlfordFilm.sfx200(),
|
||||
IlfordFilm.xp2super(),
|
||||
IlfordFilm.pan100(),
|
||||
IlfordFilm.pan400(),
|
||||
KodakFilm.tmax100(),
|
||||
KodakFilm.tmax400(),
|
||||
KodakFilm.tmax3200(),
|
||||
KodakFilm.trix320(),
|
||||
KodakFilm.trix400(),
|
||||
];
|
||||
}
|
||||
|
||||
/// https://www.tate.org.uk/documents/598/page_6_7_agfa_stocks_0.pdf
|
||||
/// https://www.filmwasters.com/forum/index.php?topic=5298.0
|
||||
// {{1,1.87},{2,3.73},{3,8.06},{4,13.93},{5,21.28},{6,23.00},{7,30.12},{8,38.05},{9,44.75},{10,50.12},{20,117},{30,202},{40,293},{50,413},{60,547},{70,694},{80,853},{90,1022},{100,1202}};
|
||||
// class AgfaFilm extends Film {
|
||||
// final double a;
|
||||
// final double b;
|
||||
// final double c;
|
||||
|
||||
// const AgfaFilm.apx100()
|
||||
// : a = 1,
|
||||
// b = 5,
|
||||
// c = 2,
|
||||
// super('Agfa APX 100', 100); // coverage:ignore-line
|
||||
|
||||
// const AgfaFilm.apx400()
|
||||
// : a = 1.5,
|
||||
// b = 4.5,
|
||||
// c = 3,
|
||||
// super('Agfa APX 400', 400); // coverage:ignore-line
|
||||
|
||||
// @override
|
||||
// double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c);
|
||||
// }
|
||||
|
||||
class FomapanFilm extends Film {
|
||||
final double a;
|
||||
final double b;
|
||||
final double c;
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-100
|
||||
const FomapanFilm.creative100()
|
||||
: a = 1,
|
||||
b = 5,
|
||||
c = 2,
|
||||
super('Fomapan CREATIVE 100', 100); // coverage:ignore-line
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-200
|
||||
const FomapanFilm.creative200()
|
||||
: a = 1.5,
|
||||
b = 4.5,
|
||||
c = 3,
|
||||
super('Fomapan CREATIVE 200', 200); // coverage:ignore-line
|
||||
|
||||
/// https://www.foma.cz/en/fomapan-100
|
||||
const FomapanFilm.action400()
|
||||
: a = -1.25, // coverage:ignore-line
|
||||
b = 5.75,
|
||||
c = 1.5,
|
||||
super('Fomapan ACTION 400', 400); // coverage:ignore-line
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c);
|
||||
}
|
||||
|
||||
class IlfordFilm extends Film {
|
||||
final double reciprocityPower;
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1948/product/1650/
|
||||
const IlfordFilm.ortho()
|
||||
: reciprocityPower = 1.25,
|
||||
super('Ilford ORTHO+', 80); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1919/product/686/
|
||||
const IlfordFilm.fp4()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Ilford FP4+', 125); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1903/product/691/
|
||||
const IlfordFilm.hp5()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford HP5+', 400); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/3/product/679/
|
||||
const IlfordFilm.delta100()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Ilford DELTA 100', 100); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1915/product/684/
|
||||
const IlfordFilm.delta400()
|
||||
: reciprocityPower = 1.41,
|
||||
super('Ilford DELTA 400', 400); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1913/product/682/
|
||||
const IlfordFilm.delta3200()
|
||||
: reciprocityPower = 1.33,
|
||||
super('Ilford DELTA 3200', 3200); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1905/product/699/
|
||||
const IlfordFilm.panf()
|
||||
: reciprocityPower = 1.33,
|
||||
super('Ilford Pan F+', 50); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1907/product/701/
|
||||
const IlfordFilm.sfx200()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford SFX 200', 200); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1909/product/703/
|
||||
const IlfordFilm.xp2super()
|
||||
: reciprocityPower = 1.31,
|
||||
super('Ilford XP2 SUPER', 400); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1958/product/696/
|
||||
const IlfordFilm.pan100()
|
||||
: reciprocityPower = 1.26,
|
||||
super('Kentemere 100', 100); // coverage:ignore-line
|
||||
|
||||
/// https://www.ilfordphoto.com/amfile/file/download/file/1959/product/697/
|
||||
const IlfordFilm.pan400()
|
||||
: reciprocityPower = 1.30,
|
||||
super('Kentemere 400', 400); // coverage:ignore-line
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => pow(t, reciprocityPower).toDouble();
|
||||
}
|
||||
|
||||
class KodakFilm extends Film {
|
||||
final double a;
|
||||
final double b;
|
||||
final double c;
|
||||
|
||||
const KodakFilm.tmax100()
|
||||
: a = 1 / 6, // coverage:ignore-line
|
||||
b = 0, // coverage:ignore-line
|
||||
c = 4 / 3, // coverage:ignore-line
|
||||
super('Kodak T-MAX 100', 100); // coverage:ignore-line
|
||||
|
||||
const KodakFilm.tmax400()
|
||||
: a = 2 / 3, // coverage:ignore-line
|
||||
b = -1 / 2, // coverage:ignore-line
|
||||
c = 4 / 3, // coverage:ignore-line
|
||||
super('Kodak T-MAX 400', 400); // coverage:ignore-line
|
||||
|
||||
const KodakFilm.tmax3200()
|
||||
: a = 7 / 6, // coverage:ignore-line
|
||||
b = -1, // coverage:ignore-line
|
||||
c = 4 / 3, // coverage:ignore-line
|
||||
super('Kodak T-MAX 3200', 3200); // coverage:ignore-line
|
||||
|
||||
const KodakFilm.trix320()
|
||||
: a = 2,
|
||||
b = 1,
|
||||
c = 2,
|
||||
super('Kodak TRI-X 320', 320); // coverage:ignore-line
|
||||
|
||||
const KodakFilm.trix400()
|
||||
: a = 2,
|
||||
b = 1,
|
||||
c = 2,
|
||||
super('Kodak TRI-X 400', 400); // coverage:ignore-line
|
||||
|
||||
@override
|
||||
double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c);
|
||||
}
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/data/models/supported_locale.dart';
|
||||
import 'package:lightmeter/data/models/theme_type.dart';
|
||||
|
@ -19,7 +18,6 @@ class UserPreferencesService {
|
|||
static const cameraEvCalibrationKey = "cameraEvCalibration";
|
||||
static const lightSensorEvCalibrationKey = "lightSensorEvCalibration";
|
||||
static const meteringScreenLayoutKey = "meteringScreenLayout";
|
||||
static const filmKey = "film";
|
||||
|
||||
static const caffeineKey = "caffeine";
|
||||
static const hapticsKey = "haptics";
|
||||
|
@ -142,10 +140,4 @@ class UserPreferencesService {
|
|||
|
||||
bool get dynamicColor => _sharedPreferences.getBool(dynamicColorKey) ?? false;
|
||||
set dynamicColor(bool value) => _sharedPreferences.setBool(dynamicColorKey, value);
|
||||
|
||||
Film get film => Film.values.firstWhere(
|
||||
(e) => e.name == _sharedPreferences.getString(filmKey),
|
||||
orElse: () => Film.values.first,
|
||||
);
|
||||
set film(Film value) => _sharedPreferences.setString(filmKey, value.name);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'package:app_settings/app_settings.dart';
|
|||
import 'package:lightmeter/data/caffeine_service.dart';
|
||||
import 'package:lightmeter/data/haptics_service.dart';
|
||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/volume_action.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
|
@ -45,9 +44,6 @@ class MeteringInteractor {
|
|||
NdValue get ndFilter => _userPreferencesService.ndFilter;
|
||||
set ndFilter(NdValue value) => _userPreferencesService.ndFilter = value;
|
||||
|
||||
Film get film => _userPreferencesService.film;
|
||||
set film(Film value) => _userPreferencesService.film = value;
|
||||
|
||||
VolumeAction get volumeAction => _userPreferencesService.volumeAction;
|
||||
|
||||
/// Executes vibration if haptics are enabled in settings
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
"meteringScreenFeatureFilmPicker": "Film picker",
|
||||
"meteringScreenFeatureHistogram": "Histogram",
|
||||
"film": "Film",
|
||||
"filmPush": "Film (push)",
|
||||
"filmPull": "Film (pull)",
|
||||
"filmReciprocityHint": "Applies correction for shutter speeds grater than 1 second",
|
||||
"equipment": "Equipment",
|
||||
"equipmentProfileName": "Equipment profile name",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
@ -56,6 +59,8 @@
|
|||
"equipmentProfile": "Equipment profile",
|
||||
"equipmentProfiles": "Equipment profiles",
|
||||
"tapToAdd": "Tap to add",
|
||||
"filmsInUse": "Films in use",
|
||||
"filmsInUseDescription": "Select films which you use.",
|
||||
"general": "General",
|
||||
"keepScreenOn": "Keep screen on",
|
||||
"haptics": "Haptics",
|
||||
|
|
|
@ -41,9 +41,11 @@
|
|||
"meteringScreenFeatureFilmPicker": "Sélecteur de film",
|
||||
"meteringScreenFeatureHistogram": "Histogramme",
|
||||
"film": "Pellicule",
|
||||
"filmPush": "Pellicule (push)",
|
||||
"filmPull": "Pellicule (pull)",
|
||||
"filmReciprocityHint": "La correction s'applique aux vitesses d'obturation supérieures à 1 seconde",
|
||||
"equipment": "Équipement",
|
||||
"equipmentProfileName": "Nom du profil de l'équipement",
|
||||
"tapToAdd": "Appuie pour ajouter",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
"equipmentProfileAllValues": "Tout",
|
||||
"apertureValues": "Valeurs Aperture",
|
||||
|
@ -56,6 +58,9 @@
|
|||
"isoValuesFilterDescription": "Sélectionnez les valeurs ISO à afficher. Ce sont peut-être vos valeurs les plus couramment utilisées ou celles prises en charge par votre caméra.",
|
||||
"equipmentProfile": "Profil de l'équipement",
|
||||
"equipmentProfiles": "Profils de l'équipement",
|
||||
"tapToAdd": "Appuie pour ajouter",
|
||||
"filmsInUse": "Films en usage",
|
||||
"filmsInUseDescription": "Sélectionnez les films que vous utilisez.",
|
||||
"general": "Général",
|
||||
"keepScreenOn": "Garder l'écran allumé",
|
||||
"haptics": "Haptiques",
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
"meteringScreenFeatureFilmPicker": "Выбор пленки",
|
||||
"meteringScreenFeatureHistogram": "Гистограмма",
|
||||
"film": "Пленка",
|
||||
"filmPush": "Пленка (push)",
|
||||
"filmPull": "Пленка (pull)",
|
||||
"filmReciprocityHint": "Применяет коррекцию для выдержек длиннее 1 секунды",
|
||||
"equipment": "Оборудование",
|
||||
"equipmentProfileName": "Название профиля",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
@ -56,6 +59,8 @@
|
|||
"equipmentProfile": "Оборудование",
|
||||
"equipmentProfiles": "Профили оборудования",
|
||||
"tapToAdd": "Нажмите, чтобы добавить",
|
||||
"filmsInUse": "Используемые пленки",
|
||||
"filmsInUseDescription": "Выберите пленки, которыми вы пользуетесь.",
|
||||
"general": "Общие",
|
||||
"keepScreenOn": "Запрет блокировки",
|
||||
"haptics": "Вибрация",
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"thirdStops": "1/3",
|
||||
"calibration": "校准",
|
||||
"calibrationMessage": "此应用测量读数的准确性完全取决于设备的硬件。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
|
||||
"calibrationMessageCameraOnly": "此应用程序测量读数的准确性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
|
||||
"calibrationMessageCameraOnly": "此应用程序测量读数的准确s性完全取决于设备的后置摄像头。因此,请考虑测试此应用并手动设置 EV 校准,以获得准确的测量结果。",
|
||||
"camera": "摄像头",
|
||||
"lightSensor": "光传感器",
|
||||
"meteringScreenLayout": "布局",
|
||||
|
@ -41,6 +41,9 @@
|
|||
"meteringScreenFeatureFilmPicker": "胶片选择",
|
||||
"meteringScreenFeatureHistogram": "直方图",
|
||||
"film": "胶片",
|
||||
"filmPush": "胶片 (push)",
|
||||
"filmPull": "胶片 (pull)",
|
||||
"filmReciprocityHint": "Applies correction for shutter speeds grater than 1 second",
|
||||
"equipment": "设备",
|
||||
"equipmentProfileName": "设备配置名称",
|
||||
"equipmentProfileNameHint": "Praktica MTL5B",
|
||||
|
@ -56,6 +59,8 @@
|
|||
"equipmentProfile": "设备配置",
|
||||
"equipmentProfiles": "设备配置",
|
||||
"tapToAdd": "點擊添加",
|
||||
"filmsInUse": "Films in use",
|
||||
"filmsInUseDescription": "Select films which you use.",
|
||||
"general": "通用",
|
||||
"keepScreenOn": "保持屏幕常亮",
|
||||
"haptics": "震动",
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:async';
|
|||
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/volume_action.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
|
@ -29,7 +28,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
) : super(
|
||||
MeteringDataState(
|
||||
ev100: null,
|
||||
film: _meteringInteractor.film,
|
||||
iso: _meteringInteractor.iso,
|
||||
nd: _meteringInteractor.ndFilter,
|
||||
isMetering: false,
|
||||
|
@ -42,7 +40,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
.listen(onCommunicationState);
|
||||
|
||||
on<EquipmentProfileChangedEvent>(_onEquipmentProfileChanged);
|
||||
on<FilmChangedEvent>(_onFilmChanged);
|
||||
on<IsoChangedEvent>(_onIsoChanged);
|
||||
on<NdChangedEvent>(_onNdChanged);
|
||||
on<MeasureEvent>(_onMeasure, transformer: droppable());
|
||||
|
@ -92,12 +89,9 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
/// Update selected ISO value and discard selected film, if selected equipment profile
|
||||
/// doesn't contain currently selected value
|
||||
IsoValue iso = state.iso;
|
||||
Film film = state.film;
|
||||
if (!event.equipmentProfileData.isoValues.any((v) => state.iso.value == v.value)) {
|
||||
_meteringInteractor.iso = event.equipmentProfileData.isoValues.first;
|
||||
iso = event.equipmentProfileData.isoValues.first;
|
||||
_meteringInteractor.film = Film.values.first;
|
||||
film = Film.values.first;
|
||||
willUpdateMeasurements = true;
|
||||
}
|
||||
|
||||
|
@ -113,7 +107,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
isMetering: state.isMetering,
|
||||
|
@ -122,46 +115,12 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
}
|
||||
}
|
||||
|
||||
void _onFilmChanged(FilmChangedEvent event, Emitter emit) {
|
||||
if (state.film.name != event.film.name) {
|
||||
_meteringInteractor.film = event.film;
|
||||
|
||||
/// Find `IsoValue` with matching value
|
||||
IsoValue iso = state.iso;
|
||||
if (state.iso.value != event.film.iso && event.film != const Film.other()) {
|
||||
iso = IsoValue.values.firstWhere(
|
||||
(e) => e.value == event.film.iso,
|
||||
orElse: () => state.iso,
|
||||
);
|
||||
_meteringInteractor.iso = iso;
|
||||
}
|
||||
|
||||
/// If user selects 'Other' film we preserve currently selected ISO
|
||||
/// and therefore only discard reciprocity formula
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
film: event.film,
|
||||
iso: iso,
|
||||
nd: state.nd,
|
||||
isMetering: state.isMetering,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onIsoChanged(IsoChangedEvent event, Emitter emit) {
|
||||
/// Discard currently selected film even if ISO is the same,
|
||||
/// because, for example, Fomapan 400 and any Ilford 400
|
||||
/// have different reciprocity formulas
|
||||
_meteringInteractor.film = Film.values.first;
|
||||
|
||||
if (state.iso != event.isoValue) {
|
||||
_meteringInteractor.iso = event.isoValue;
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
film: Film.values.first,
|
||||
iso: event.isoValue,
|
||||
nd: state.nd,
|
||||
isMetering: state.isMetering,
|
||||
|
@ -176,7 +135,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: event.ndValue,
|
||||
isMetering: state.isMetering,
|
||||
|
@ -190,7 +148,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
_communicationBloc.add(const communication_events.MeasureEvent());
|
||||
emit(
|
||||
LoadingState(
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
),
|
||||
|
@ -209,7 +166,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
emit(
|
||||
MeteringDataState(
|
||||
ev100: event.ev100,
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
isMetering: event.isMetering,
|
||||
|
@ -221,7 +177,6 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
|||
emit(
|
||||
MeteringDataState(
|
||||
ev100: null,
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
isMetering: event.isMetering,
|
||||
|
|
|
@ -31,27 +31,7 @@ class _CameraPreviewState extends State<CameraPreview> {
|
|||
AnimatedSwitcher(
|
||||
duration: Dimens.switchDuration,
|
||||
child: widget.controller != null
|
||||
? ValueListenableBuilder<CameraValue>(
|
||||
valueListenable: widget.controller!,
|
||||
builder: (_, __, ___) => widget.controller!.value.isInitialized
|
||||
? Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
CameraView(controller: widget.controller!),
|
||||
if (UserPreferencesProvider.meteringScreenFeatureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.histogram,
|
||||
))
|
||||
Positioned(
|
||||
left: Dimens.grid8,
|
||||
right: Dimens.grid8,
|
||||
bottom: Dimens.grid16,
|
||||
child: CameraHistogram(controller: widget.controller!),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
)
|
||||
? _CameraPreviewBuilder(controller: widget.controller!)
|
||||
: CameraViewPlaceholder(error: widget.error),
|
||||
),
|
||||
],
|
||||
|
@ -60,3 +40,59 @@ class _CameraPreviewState extends State<CameraPreview> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CameraPreviewBuilder extends StatefulWidget {
|
||||
final CameraController controller;
|
||||
|
||||
const _CameraPreviewBuilder({required this.controller});
|
||||
|
||||
@override
|
||||
State<_CameraPreviewBuilder> createState() => _CameraPreviewBuilderState();
|
||||
}
|
||||
|
||||
class _CameraPreviewBuilderState extends State<_CameraPreviewBuilder> {
|
||||
late final ValueNotifier<bool> _initializedNotifier =
|
||||
ValueNotifier<bool>(widget.controller.value.isInitialized);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.controller.addListener(_update);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.controller.removeListener(_update);
|
||||
_initializedNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _initializedNotifier,
|
||||
builder: (context, value, child) => value
|
||||
? Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
CameraView(controller: widget.controller),
|
||||
if (UserPreferencesProvider.meteringScreenFeatureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.histogram,
|
||||
))
|
||||
Positioned(
|
||||
left: Dimens.grid8,
|
||||
right: Dimens.grid8,
|
||||
bottom: Dimens.grid16,
|
||||
child: CameraHistogram(controller: widget.controller),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
|
||||
void _update() {
|
||||
_initializedNotifier.value = widget.controller.value.isInitialized;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart';
|
||||
import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart';
|
||||
|
@ -12,10 +11,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
class CameraContainerProvider extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -23,10 +20,8 @@ class CameraContainerProvider extends StatelessWidget {
|
|||
const CameraContainerProvider({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -44,10 +39,8 @@ class CameraContainerProvider extends StatelessWidget {
|
|||
child: CameraContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:math';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/platform_config.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
|
@ -23,10 +22,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
class CameraContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -34,10 +31,8 @@ class CameraContainer extends StatelessWidget {
|
|||
const CameraContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -60,10 +55,8 @@ class CameraContainer extends StatelessWidget {
|
|||
readingsContainer: ReadingsContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart';
|
||||
import 'package:lightmeter/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart';
|
||||
|
@ -11,10 +10,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
class LightSensorContainerProvider extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -22,10 +19,8 @@ class LightSensorContainerProvider extends StatelessWidget {
|
|||
const LightSensorContainerProvider({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -43,10 +38,8 @@ class LightSensorContainerProvider extends StatelessWidget {
|
|||
child: LightSensorContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
||||
|
@ -10,10 +9,8 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|||
class LightSensorContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -21,10 +18,8 @@ class LightSensorContainer extends StatelessWidget {
|
|||
const LightSensorContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
required this.exposurePairs,
|
||||
|
@ -39,10 +34,8 @@ class LightSensorContainer extends StatelessWidget {
|
|||
readingsContainer: ReadingsContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:lightmeter/res/dimens.dart';
|
|||
|
||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/components/exposure_pairs_list_item/widget_item_list_exposure_pairs.dart';
|
||||
import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
|
||||
class ExposurePairsList extends StatelessWidget {
|
||||
final List<ExposurePair> exposurePairs;
|
||||
|
@ -47,7 +48,8 @@ class ExposurePairsList extends StatelessWidget {
|
|||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ExposurePairsListItem(
|
||||
exposurePairs[index].shutterSpeed,
|
||||
Films.selectedOf(context)
|
||||
.reciprocityFailure(exposurePairs[index].shutterSpeed),
|
||||
tickOnTheLeft: true,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfilePicker extends StatelessWidget {
|
||||
const EquipmentProfilePicker();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<EquipmentProfile>(
|
||||
icon: Icons.camera,
|
||||
title: S.of(context).equipmentProfile,
|
||||
selectedValue: EquipmentProfiles.selectedOf(context),
|
||||
values: EquipmentProfiles.of(context),
|
||||
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: EquipmentProfileProvider.of(context).setProfile,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).equipmentProfile,
|
||||
value: EquipmentProfiles.selectedOf(context).id.isEmpty
|
||||
? S.of(context).none
|
||||
: EquipmentProfiles.selectedOf(context).name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
|
||||
class ExtremeExposurePairsContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
|
||||
const ExtremeExposurePairsContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ReadingValueContainer(
|
||||
values: [
|
||||
ReadingValue(
|
||||
label: S.of(context).fastestExposurePair,
|
||||
value: _exposurePairToString(context, fastest),
|
||||
),
|
||||
ReadingValue(
|
||||
label: S.of(context).slowestExposurePair,
|
||||
value: _exposurePairToString(context, slowest),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _exposurePairToString(BuildContext context, ExposurePair? pair) {
|
||||
if (pair == null) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return '${pair.aperture} - ${Films.selectedOf(context).reciprocityFailure(pair.shutterSpeed)}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class FilmPicker extends StatelessWidget {
|
||||
final IsoValue selectedIso;
|
||||
|
||||
const FilmPicker({required this.selectedIso});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<Film>(
|
||||
icon: Icons.camera_roll,
|
||||
title: S.of(context).film,
|
||||
subtitle: S.of(context).filmReciprocityHint,
|
||||
selectedValue: Films.selectedOf(context),
|
||||
values: Films.inUseOf(context),
|
||||
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: FilmsProvider.of(context).setFilm,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: _label(context),
|
||||
value: Films.selectedOf(context).name.isEmpty
|
||||
? S.of(context).none
|
||||
: Films.selectedOf(context).name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _label(BuildContext context) {
|
||||
if (Films.selectedOf(context) == const Film.other() ||
|
||||
Films.selectedOf(context).iso == selectedIso.value) {
|
||||
return S.of(context).film;
|
||||
}
|
||||
|
||||
final evDiff = IsoValue(
|
||||
Films.selectedOf(context).iso,
|
||||
StopType.full,
|
||||
).difference(selectedIso);
|
||||
|
||||
if (evDiff > 0) {
|
||||
return S.of(context).filmPush;
|
||||
} else if (evDiff < 0) {
|
||||
return S.of(context).filmPull;
|
||||
} else {
|
||||
return S.of(context).film;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class IsoValuePicker extends StatelessWidget {
|
||||
final List<IsoValue> values;
|
||||
final IsoValue selectedValue;
|
||||
final ValueChanged<IsoValue> onChanged;
|
||||
|
||||
const IsoValuePicker({
|
||||
required this.selectedValue,
|
||||
required this.values,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<IsoValue>(
|
||||
icon: Icons.iso,
|
||||
title: S.of(context).iso,
|
||||
subtitle: S.of(context).filmSpeed,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(value.value.toString()),
|
||||
// using ascending order, because increase in film speed rises EV
|
||||
itemTrailingBuilder: (selected, value) => value.value != selected.value
|
||||
? Text(S.of(context).evValue(selected.toStringDifference(value)))
|
||||
: null,
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).iso,
|
||||
value: selectedValue.value.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class NdValuePicker extends StatelessWidget {
|
||||
final List<NdValue> values;
|
||||
final NdValue selectedValue;
|
||||
final ValueChanged<NdValue> onChanged;
|
||||
|
||||
const NdValuePicker({
|
||||
required this.selectedValue,
|
||||
required this.values,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<NdValue>(
|
||||
icon: Icons.filter_b_and_w,
|
||||
title: S.of(context).nd,
|
||||
subtitle: S.of(context).ndFilterFactor,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(
|
||||
value.value == 0 ? S.of(context).none : value.value.toString(),
|
||||
),
|
||||
// using descending order, because ND filter darkens image & lowers EV
|
||||
itemTrailingBuilder: (selected, value) => value.value != selected.value
|
||||
? Text(S.of(context).evValue(value.toStringDifference(selected)))
|
||||
: null,
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).nd,
|
||||
value: selectedValue.value.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
|
||||
|
||||
// Has to be stateful, so that [GlobalKey] is not recreated.
|
||||
// Otherwise use will no be able to close the dialog after EV value has changed.
|
|
@ -1,32 +1,29 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
|
||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class ReadingsContainer extends StatelessWidget {
|
||||
final ExposurePair? fastest;
|
||||
final ExposurePair? slowest;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
|
||||
const ReadingsContainer({
|
||||
required this.fastest,
|
||||
required this.slowest,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
super.key,
|
||||
|
@ -41,24 +38,16 @@ class ReadingsContainer extends StatelessWidget {
|
|||
context,
|
||||
MeteringScreenLayoutFeature.equipmentProfiles,
|
||||
)) ...[
|
||||
const _EquipmentProfilePicker(),
|
||||
const EquipmentProfilePicker(),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
if (UserPreferencesProvider.meteringScreenFeatureOf(
|
||||
context,
|
||||
MeteringScreenLayoutFeature.extremeExposurePairs,
|
||||
)) ...[
|
||||
ReadingValueContainer(
|
||||
values: [
|
||||
ReadingValue(
|
||||
label: S.of(context).fastestExposurePair,
|
||||
value: fastest != null ? fastest!.toString() : '-',
|
||||
),
|
||||
ReadingValue(
|
||||
label: S.of(context).slowestExposurePair,
|
||||
value: fastest != null ? slowest!.toString() : '-',
|
||||
),
|
||||
],
|
||||
ExtremeExposurePairsContainer(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
|
@ -66,17 +55,13 @@ class ReadingsContainer extends StatelessWidget {
|
|||
context,
|
||||
MeteringScreenLayoutFeature.filmPicker,
|
||||
)) ...[
|
||||
_FilmPicker(
|
||||
values: Film.values,
|
||||
selectedValue: film,
|
||||
onChanged: onFilmChanged,
|
||||
),
|
||||
FilmPicker(selectedIso: iso),
|
||||
const _InnerPadding(),
|
||||
],
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _IsoValuePicker(
|
||||
child: IsoValuePicker(
|
||||
selectedValue: iso,
|
||||
values: EquipmentProfiles.selectedOf(context).isoValues,
|
||||
onChanged: onIsoChanged,
|
||||
|
@ -84,7 +69,7 @@ class ReadingsContainer extends StatelessWidget {
|
|||
),
|
||||
const _InnerPadding(),
|
||||
Expanded(
|
||||
child: _NdValuePicker(
|
||||
child: NdValuePicker(
|
||||
selectedValue: nd,
|
||||
values: EquipmentProfiles.selectedOf(context).ndValues,
|
||||
onChanged: onNdChanged,
|
||||
|
@ -100,129 +85,3 @@ class ReadingsContainer extends StatelessWidget {
|
|||
class _InnerPadding extends SizedBox {
|
||||
const _InnerPadding() : super(height: Dimens.grid8, width: Dimens.grid8);
|
||||
}
|
||||
|
||||
class _EquipmentProfilePicker extends StatelessWidget {
|
||||
const _EquipmentProfilePicker();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<EquipmentProfile>(
|
||||
icon: Icons.camera,
|
||||
title: S.of(context).equipmentProfile,
|
||||
selectedValue: EquipmentProfiles.selectedOf(context),
|
||||
values: EquipmentProfiles.of(context),
|
||||
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: EquipmentProfileProvider.of(context).setProfile,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).equipmentProfile,
|
||||
value: EquipmentProfiles.selectedOf(context).id.isEmpty
|
||||
? S.of(context).none
|
||||
: EquipmentProfiles.selectedOf(context).name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FilmPicker extends StatelessWidget {
|
||||
final List<Film> values;
|
||||
final Film selectedValue;
|
||||
final ValueChanged<Film> onChanged;
|
||||
|
||||
const _FilmPicker({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<Film>(
|
||||
icon: Icons.camera_roll,
|
||||
title: S.of(context).film,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name),
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).film,
|
||||
value: selectedValue.name.isEmpty ? S.of(context).none : selectedValue.name,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IsoValuePicker extends StatelessWidget {
|
||||
final List<IsoValue> values;
|
||||
final IsoValue selectedValue;
|
||||
final ValueChanged<IsoValue> onChanged;
|
||||
|
||||
const _IsoValuePicker({
|
||||
required this.selectedValue,
|
||||
required this.values,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<IsoValue>(
|
||||
icon: Icons.iso,
|
||||
title: S.of(context).iso,
|
||||
subtitle: S.of(context).filmSpeed,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(value.value.toString()),
|
||||
// using ascending order, because increase in film speed rises EV
|
||||
itemTrailingBuilder: (selected, value) => value.value != selected.value
|
||||
? Text(S.of(context).evValue(selected.toStringDifference(value)))
|
||||
: null,
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).iso,
|
||||
value: selectedValue.value.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NdValuePicker extends StatelessWidget {
|
||||
final List<NdValue> values;
|
||||
final NdValue selectedValue;
|
||||
final ValueChanged<NdValue> onChanged;
|
||||
|
||||
const _NdValuePicker({
|
||||
required this.selectedValue,
|
||||
required this.values,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogPicker<NdValue>(
|
||||
icon: Icons.filter_b_and_w,
|
||||
title: S.of(context).nd,
|
||||
subtitle: S.of(context).ndFilterFactor,
|
||||
selectedValue: selectedValue,
|
||||
values: values,
|
||||
itemTitleBuilder: (_, value) => Text(
|
||||
value.value == 0 ? S.of(context).none : value.value.toString(),
|
||||
),
|
||||
// using descending order, because ND filter darkens image & lowers EV
|
||||
itemTrailingBuilder: (selected, value) => value.value != selected.value
|
||||
? Text(S.of(context).evValue(value.toStringDifference(selected)))
|
||||
: null,
|
||||
onChanged: onChanged,
|
||||
closedChild: ReadingValueContainer.singleValue(
|
||||
value: ReadingValue(
|
||||
label: S.of(context).nd,
|
||||
value: selectedValue.value.toString(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
sealed class MeteringEvent {
|
||||
|
@ -11,12 +10,6 @@ class EquipmentProfileChangedEvent extends MeteringEvent {
|
|||
const EquipmentProfileChangedEvent(this.equipmentProfileData);
|
||||
}
|
||||
|
||||
class FilmChangedEvent extends MeteringEvent {
|
||||
final Film film;
|
||||
|
||||
const FilmChangedEvent(this.film);
|
||||
}
|
||||
|
||||
class IsoChangedEvent extends MeteringEvent {
|
||||
final IsoValue isoValue;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/providers/services_provider.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
|
@ -33,11 +32,8 @@ class MeteringScreen extends StatelessWidget {
|
|||
child: BlocBuilder<MeteringBloc, MeteringState>(
|
||||
builder: (_, state) => MeteringContainerBuidler(
|
||||
ev: state is MeteringDataState ? state.ev : null,
|
||||
film: state.film,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
onFilmChanged: (value) =>
|
||||
context.read<MeteringBloc>().add(FilmChangedEvent(value)),
|
||||
onIsoChanged: (value) => context.read<MeteringBloc>().add(IsoChangedEvent(value)),
|
||||
onNdChanged: (value) => context.read<MeteringBloc>().add(NdChangedEvent(value)),
|
||||
),
|
||||
|
@ -81,7 +77,7 @@ class _InheritedListeners extends StatelessWidget {
|
|||
feature: MeteringScreenLayoutFeature.filmPicker,
|
||||
onDidChangeDependencies: (value) {
|
||||
if (!value) {
|
||||
context.read<MeteringBloc>().add(const FilmChangedEvent(Film.other()));
|
||||
FilmsProvider.of(context).setFilm(const Film.other());
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
|
@ -92,19 +88,15 @@ class _InheritedListeners extends StatelessWidget {
|
|||
|
||||
class MeteringContainerBuidler extends StatelessWidget {
|
||||
final double? ev;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final ValueChanged<Film> onFilmChanged;
|
||||
final ValueChanged<IsoValue> onIsoChanged;
|
||||
final ValueChanged<NdValue> onNdChanged;
|
||||
|
||||
const MeteringContainerBuidler({
|
||||
required this.ev,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.onFilmChanged,
|
||||
required this.onIsoChanged,
|
||||
required this.onNdChanged,
|
||||
});
|
||||
|
@ -116,7 +108,6 @@ class MeteringContainerBuidler extends StatelessWidget {
|
|||
ev!,
|
||||
UserPreferencesProvider.stopTypeOf(context),
|
||||
EquipmentProfiles.selectedOf(context),
|
||||
film,
|
||||
)
|
||||
: <ExposurePair>[];
|
||||
final fastest = exposurePairs.isNotEmpty ? exposurePairs.first : null;
|
||||
|
@ -126,10 +117,8 @@ class MeteringContainerBuidler extends StatelessWidget {
|
|||
? CameraContainerProvider(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
@ -137,10 +126,8 @@ class MeteringContainerBuidler extends StatelessWidget {
|
|||
: LightSensorContainerProvider(
|
||||
fastest: fastest,
|
||||
slowest: slowest,
|
||||
film: film,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
onFilmChanged: onFilmChanged,
|
||||
onIsoChanged: onIsoChanged,
|
||||
onNdChanged: onNdChanged,
|
||||
exposurePairs: exposurePairs,
|
||||
|
@ -152,7 +139,6 @@ class MeteringContainerBuidler extends StatelessWidget {
|
|||
double ev,
|
||||
StopType stopType,
|
||||
EquipmentProfile equipmentProfile,
|
||||
Film film,
|
||||
) {
|
||||
if (ev.isNaN || ev.isInfinite) {
|
||||
return List.empty();
|
||||
|
@ -195,7 +181,7 @@ class MeteringContainerBuidler extends StatelessWidget {
|
|||
itemsCount,
|
||||
(index) => ExposurePair(
|
||||
apertureValues[index + apertureOffset],
|
||||
film.reciprocityFailure(shutterSpeedValues[index + shutterSpeedOffset]),
|
||||
shutterSpeedValues[index + shutterSpeedOffset],
|
||||
),
|
||||
growable: false,
|
||||
);
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
@immutable
|
||||
abstract class MeteringState {
|
||||
final double? ev100;
|
||||
final Film film;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final bool isMetering;
|
||||
|
||||
const MeteringState({
|
||||
this.ev100,
|
||||
required this.film,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.isMetering,
|
||||
|
@ -21,7 +18,6 @@ abstract class MeteringState {
|
|||
|
||||
class LoadingState extends MeteringState {
|
||||
const LoadingState({
|
||||
required super.film,
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
}) : super(isMetering: true);
|
||||
|
@ -30,7 +26,6 @@ class LoadingState extends MeteringState {
|
|||
class MeteringDataState extends MeteringState {
|
||||
const MeteringDataState({
|
||||
required super.ev100,
|
||||
required super.film,
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
required super.isMetering,
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfileListener extends StatefulWidget {
|
||||
final ValueChanged<EquipmentProfile> onDidChangeDependencies;
|
||||
final Widget child;
|
||||
|
||||
const EquipmentProfileListener({
|
||||
required this.onDidChangeDependencies,
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EquipmentProfileListener> createState() => _EquipmentProfileListenerState();
|
||||
}
|
||||
|
||||
class _EquipmentProfileListenerState extends State<EquipmentProfileListener> {
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
widget.onDidChangeDependencies(EquipmentProfiles.selectedOf(context));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_filter/widget_dialog_filter.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/components/dialog_range_picker/widget_dialog_picker_range.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_range_picker/widget_dialog_picker_range.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentListTiles extends StatelessWidget {
|
|
@ -3,8 +3,8 @@ import 'dart:math';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/components/equipment_list_tiles/widget_list_tiles_equipments.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfileContainer extends StatefulWidget {
|
|
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:lightmeter/generated/l10n.dart';
|
||||
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_container/widget_container_equipment_profile.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/components/equipment_profile_name_dialog/widget_dialog_equipment_profile_name.dart';
|
||||
import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart';
|
||||
import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
@ -0,0 +1,22 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfilesListTile extends StatelessWidget {
|
||||
const EquipmentProfilesListTile({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IAPListTile(
|
||||
leading: const Icon(Icons.camera),
|
||||
title: Text(S.of(context).equipmentProfiles),
|
||||
onTap: () {
|
||||
Navigator.of(context).push<EquipmentProfile>(
|
||||
MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_filter/widget_dialog_filter.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/iap_list_tile/widget_list_tile_iap.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class FilmsListTile extends StatelessWidget {
|
||||
const FilmsListTile({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IAPListTile(
|
||||
leading: const Icon(Icons.camera_roll),
|
||||
title: Text(S.of(context).filmsInUse),
|
||||
onTap: () {
|
||||
showDialog<List<Film>>(
|
||||
context: context,
|
||||
builder: (_) => DialogFilter<Film>(
|
||||
icon: const Icon(Icons.camera_roll),
|
||||
title: S.of(context).filmsInUse,
|
||||
description: S.of(context).filmsInUseDescription,
|
||||
values: Films.of(context).sublist(1),
|
||||
selectedValues: Films.inUseOf(context),
|
||||
titleAdapter: (_, value) => value.name,
|
||||
),
|
||||
).then((values) {
|
||||
if (values != null) {
|
||||
FilmsProvider.of(context).saveFilms(values);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/equipment_profiles/widget_list_tile_equipment_profiles.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/components/films/widget_list_tile_films.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||
|
||||
class EquipmentSettingsSection extends StatelessWidget {
|
||||
const EquipmentSettingsSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsSection(
|
||||
title: S.of(context).equipment,
|
||||
children: const [
|
||||
EquipmentProfilesListTile(),
|
||||
FilmsListTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:lightmeter/data/models/supported_locale.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart';
|
||||
|
||||
class LanguageListTile extends StatelessWidget {
|
||||
const LanguageListTile({super.key});
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/components/equipment_profile_screen/screen_equipment_profile.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class EquipmentProfilesListTile extends StatelessWidget {
|
||||
const EquipmentProfilesListTile({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final paidStatus = IAPProducts.productOf(context, IAPProductType.paidFeatures)?.status ??
|
||||
IAPProductStatus.pending;
|
||||
log(paidStatus.toString());
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.camera),
|
||||
title: Text(S.of(context).equipmentProfiles),
|
||||
onTap: switch (paidStatus) {
|
||||
IAPProductStatus.purchased => () {
|
||||
Navigator.of(context).push<EquipmentProfile>(
|
||||
MaterialPageRoute(builder: (_) => const EquipmentProfilesScreen()),
|
||||
);
|
||||
},
|
||||
IAPProductStatus.pending => null,
|
||||
_ => () {
|
||||
IAPProductsProvider.of(context).buy(IAPProductType.paidFeatures);
|
||||
},
|
||||
},
|
||||
trailing: switch (paidStatus) {
|
||||
IAPProductStatus.purchasable => const Icon(Icons.lock),
|
||||
IAPProductStatus.pending => const SizedBox(
|
||||
height: Dimens.grid24,
|
||||
width: Dimens.grid24,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
_ => null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class StopTypeListTile extends StatelessWidget {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/calibration/widget_list_tile_calibration.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/equipment_profiles/widget_list_tile_equipment_profiles.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/components/metering_screen_layout/widget_list_tile_metering_screen_layout.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart';
|
||||
|
@ -17,7 +16,6 @@ class MeteringSettingsSection extends StatelessWidget {
|
|||
StopTypeListTile(),
|
||||
CalibrationListTile(),
|
||||
MeteringScreenLayoutListTile(),
|
||||
EquipmentProfilesListTile(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/res/dimens.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
class DialogFilter<T extends PhotographyValue> extends StatefulWidget {
|
||||
class DialogFilter<T> extends StatefulWidget {
|
||||
final Icon icon;
|
||||
final String title;
|
||||
final String description;
|
||||
|
@ -25,10 +24,10 @@ class DialogFilter<T extends PhotographyValue> extends StatefulWidget {
|
|||
State<DialogFilter<T>> createState() => _DialogFilterState<T>();
|
||||
}
|
||||
|
||||
class _DialogFilterState<T extends PhotographyValue> extends State<DialogFilter<T>> {
|
||||
class _DialogFilterState<T> extends State<DialogFilter<T>> {
|
||||
late final List<bool> checkboxValues = List.generate(
|
||||
widget.values.length,
|
||||
(index) => widget.selectedValues.any((element) => element.value == widget.values[index].value),
|
||||
(index) => widget.selectedValues.any((element) => element == widget.values[index]),
|
||||
growable: false,
|
||||
);
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||
|
||||
/// Depends on the product status and replaces [onTap] with purchase callback
|
||||
/// if the product is purchasable.
|
||||
class IAPListTile extends StatelessWidget {
|
||||
final IAPProductType product;
|
||||
final Icon leading;
|
||||
final Text title;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const IAPListTile({
|
||||
this.product = IAPProductType.paidFeatures,
|
||||
required this.leading,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: leading,
|
||||
title: title,
|
||||
onTap: switch (IAPProducts.productOf(context, product)?.status) {
|
||||
IAPProductStatus.purchasable => () => IAPProductsProvider.of(context).buy(product),
|
||||
IAPProductStatus.pending => null,
|
||||
IAPProductStatus.purchased => onTap,
|
||||
null => null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,10 +4,12 @@ import 'package:lightmeter/res/dimens.dart';
|
|||
class SettingsSection extends StatelessWidget {
|
||||
final String title;
|
||||
final List<Widget> children;
|
||||
final bool enabled;
|
||||
|
||||
const SettingsSection({
|
||||
required this.title,
|
||||
required this.children,
|
||||
this.enabled = true,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -23,22 +25,25 @@ class SettingsSection extends StatelessWidget {
|
|||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||
child: Text(
|
||||
title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(color: Theme.of(context).colorScheme.onSurface),
|
||||
child: Opacity(
|
||||
opacity: enabled ? Dimens.enabledOpacity : Dimens.disabledOpacity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: Dimens.paddingM),
|
||||
child: Text(
|
||||
title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
...children,
|
||||
],
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:lightmeter/data/models/theme_type.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/providers/user_preferences_provider.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker.dart/widget_dialog_picker.dart';
|
||||
import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart';
|
||||
|
||||
class ThemeTypeListTile extends StatelessWidget {
|
||||
const ThemeTypeListTile({super.key});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lightmeter/generated/l10n.dart';
|
||||
import 'package:lightmeter/screens/settings/components/about/widget_settings_section_about.dart';
|
||||
import 'package:lightmeter/screens/settings/components/equipment/widget_settings_section_equipment.dart';
|
||||
import 'package:lightmeter/screens/settings/components/general/widget_settings_section_general.dart';
|
||||
import 'package:lightmeter/screens/settings/components/metering/widget_settings_section_metering.dart';
|
||||
import 'package:lightmeter/screens/settings/components/theme/widget_settings_section_theme.dart';
|
||||
|
@ -43,6 +44,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||
delegate: SliverChildListDelegate(
|
||||
<Widget>[
|
||||
const MeteringSettingsSection(),
|
||||
const EquipmentSettingsSection(),
|
||||
const GeneralSettingsSection(),
|
||||
const ThemeSettingsSection(),
|
||||
const AboutSettingsSection(),
|
||||
|
|
11
m3_lightmeter.code-workspace
Normal file
11
m3_lightmeter.code-workspace
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "iap"
|
||||
},
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('iso', () {
|
||||
expect(const Film.other().iso, 0);
|
||||
expect(const FomapanFilm.creative100().iso, 100);
|
||||
expect(const FomapanFilm.creative200().iso, 200);
|
||||
expect(const FomapanFilm.action400().iso, 400);
|
||||
expect(const IlfordFilm.ortho().iso, 80);
|
||||
expect(const IlfordFilm.delta100().iso, 100);
|
||||
expect(const IlfordFilm.delta400().iso, 400);
|
||||
expect(const IlfordFilm.delta3200().iso, 3200);
|
||||
expect(const IlfordFilm.fp4().iso, 125);
|
||||
expect(const IlfordFilm.hp5().iso, 400);
|
||||
expect(const IlfordFilm.panf().iso, 50);
|
||||
expect(const IlfordFilm.sfx200().iso, 200);
|
||||
expect(const IlfordFilm.xp2super().iso, 400);
|
||||
expect(const IlfordFilm.pan100().iso, 100);
|
||||
expect(const IlfordFilm.pan400().iso, 400);
|
||||
expect(const KodakFilm.tmax100().iso, 100);
|
||||
expect(const KodakFilm.tmax400().iso, 400);
|
||||
expect(const KodakFilm.tmax3200().iso, 3200);
|
||||
expect(const KodakFilm.trix320().iso, 320);
|
||||
expect(const KodakFilm.trix400().iso, 400);
|
||||
});
|
||||
|
||||
test('toString()', () {
|
||||
expect(const Film.other().toString(), "");
|
||||
expect(const FomapanFilm.creative100().toString(), "Fomapan CREATIVE 100");
|
||||
expect(const FomapanFilm.creative200().toString(), "Fomapan CREATIVE 200");
|
||||
expect(const FomapanFilm.action400().toString(), "Fomapan ACTION 400");
|
||||
expect(const IlfordFilm.ortho().toString(), "Ilford ORTHO+");
|
||||
expect(const IlfordFilm.delta100().toString(), "Ilford DELTA 100");
|
||||
expect(const IlfordFilm.delta400().toString(), "Ilford DELTA 400");
|
||||
expect(const IlfordFilm.delta3200().toString(), "Ilford DELTA 3200");
|
||||
expect(const IlfordFilm.fp4().toString(), "Ilford FP4+");
|
||||
expect(const IlfordFilm.hp5().toString(), "Ilford HP5+");
|
||||
expect(const IlfordFilm.panf().toString(), "Ilford Pan F+");
|
||||
expect(const IlfordFilm.sfx200().toString(), "Ilford SFX 200");
|
||||
expect(const IlfordFilm.xp2super().toString(), "Ilford XP2 SUPER");
|
||||
expect(const IlfordFilm.pan100().toString(), "Kentemere 100");
|
||||
expect(const IlfordFilm.pan400().toString(), "Kentemere 400");
|
||||
expect(const KodakFilm.tmax100().toString(), "Kodak T-MAX 100");
|
||||
expect(const KodakFilm.tmax400().toString(), "Kodak T-MAX 400");
|
||||
expect(const KodakFilm.tmax3200().toString(), "Kodak T-MAX 3200");
|
||||
expect(const KodakFilm.trix320().toString(), "Kodak TRI-X 320");
|
||||
expect(const KodakFilm.trix400().toString(), "Kodak TRI-X 400");
|
||||
});
|
||||
|
||||
group(
|
||||
'reciprocityFailure',
|
||||
() {
|
||||
const inputSpeeds = [
|
||||
ShutterSpeedValue(1000, true, StopType.full),
|
||||
ShutterSpeedValue(1, false, StopType.full),
|
||||
ShutterSpeedValue(16, false, StopType.full)
|
||||
];
|
||||
test('No change `Film.other()`', () {
|
||||
expect(
|
||||
const Film.other().reciprocityFailure(inputSpeeds[0]),
|
||||
const ShutterSpeedValue(1000, true, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const Film.other().reciprocityFailure(inputSpeeds[1]),
|
||||
const ShutterSpeedValue(1, false, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const Film.other().reciprocityFailure(inputSpeeds[2]),
|
||||
const ShutterSpeedValue(16, false, StopType.full),
|
||||
);
|
||||
});
|
||||
|
||||
test('pow `IlfordFilm.delta100()`', () {
|
||||
expect(
|
||||
const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[0]),
|
||||
const ShutterSpeedValue(1000, true, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[1]),
|
||||
const ShutterSpeedValue(1, false, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const IlfordFilm.delta100().reciprocityFailure(inputSpeeds[2]),
|
||||
const ShutterSpeedValue(32.899642452994128, false, StopType.full),
|
||||
);
|
||||
});
|
||||
|
||||
test('log10polynomian `FomapanFilm.creative100()`', () {
|
||||
expect(
|
||||
const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[0]),
|
||||
const ShutterSpeedValue(1000, true, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[1]),
|
||||
const ShutterSpeedValue(2, false, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const FomapanFilm.creative100().reciprocityFailure(inputSpeeds[2]),
|
||||
const ShutterSpeedValue(151.52807753457483, false, StopType.full),
|
||||
);
|
||||
});
|
||||
|
||||
test('log10polynomian `Kodak.tmax400()`', () {
|
||||
expect(
|
||||
const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[0]),
|
||||
const ShutterSpeedValue(1000, true, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[1]),
|
||||
const ShutterSpeedValue(1.3333333333333333, false, StopType.full),
|
||||
);
|
||||
expect(
|
||||
const KodakFilm.tmax400().reciprocityFailure(inputSpeeds[2]),
|
||||
const ShutterSpeedValue(27.166026086819844, false, StopType.full),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lightmeter/data/models/ev_source_type.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
||||
import 'package:lightmeter/data/models/supported_locale.dart';
|
||||
import 'package:lightmeter/data/models/theme_type.dart';
|
||||
|
@ -392,26 +391,4 @@ void main() {
|
|||
.called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('film', () {
|
||||
test('get default', () {
|
||||
when(() => sharedPreferences.getString(UserPreferencesService.filmKey)).thenReturn(null);
|
||||
expect(service.film, Film.values.first);
|
||||
});
|
||||
|
||||
test('get', () {
|
||||
when(() => sharedPreferences.getString(UserPreferencesService.filmKey))
|
||||
.thenReturn('Fomapan ACTION 400');
|
||||
expect(service.film, const FomapanFilm.action400());
|
||||
});
|
||||
|
||||
test('set', () {
|
||||
when(() => sharedPreferences.setString(UserPreferencesService.filmKey, 'Fomapan ACTION 400'))
|
||||
.thenAnswer((_) => Future.value(true));
|
||||
service.film = const FomapanFilm.action400();
|
||||
verify(
|
||||
() => sharedPreferences.setString(UserPreferencesService.filmKey, 'Fomapan ACTION 400'),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:lightmeter/data/caffeine_service.dart';
|
||||
import 'package:lightmeter/data/haptics_service.dart';
|
||||
import 'package:lightmeter/data/light_sensor_service.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/volume_action.dart';
|
||||
import 'package:lightmeter/data/permissions_service.dart';
|
||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||
|
@ -124,19 +123,6 @@ void main() {
|
|||
interactor.ndFilter = NdValue.values.first;
|
||||
verify(() => mockUserPreferencesService.ndFilter = NdValue.values.first).called(1);
|
||||
});
|
||||
|
||||
test('film - get', () async {
|
||||
when(() => mockUserPreferencesService.film).thenReturn(Film.values.first);
|
||||
expect(interactor.film, Film.values.first);
|
||||
verify(() => mockUserPreferencesService.film).called(1);
|
||||
});
|
||||
|
||||
test('film - set', () async {
|
||||
when(() => mockUserPreferencesService.film = Film.values.first)
|
||||
.thenReturn(Film.values.first);
|
||||
interactor.film = Film.values.first;
|
||||
verify(() => mockUserPreferencesService.film = Film.values.first).called(1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/data/models/volume_action.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||
|
@ -34,7 +33,6 @@ void main() {
|
|||
meteringInteractor = _MockMeteringInteractor();
|
||||
when<IsoValue>(() => meteringInteractor.iso).thenReturn(iso100);
|
||||
when<NdValue>(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first);
|
||||
when<Film>(() => meteringInteractor.film).thenReturn(Film.values.first);
|
||||
when(meteringInteractor.quickVibration).thenAnswer((_) async {});
|
||||
when(meteringInteractor.responseVibration).thenAnswer((_) async {});
|
||||
when(meteringInteractor.errorVibration).thenAnswer((_) async {});
|
||||
|
@ -157,7 +155,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -166,14 +163,12 @@ void main() {
|
|||
bloc.add(const IsoChangedEvent(IsoValue(200, StopType.full)));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = Film.values.first).called(1);
|
||||
verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1);
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 2.0)
|
||||
.having((state) => state.film, 'film', Film.values.first)
|
||||
.having((state) => state.iso, 'iso', const IsoValue(200, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -185,7 +180,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: null,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -194,14 +188,12 @@ void main() {
|
|||
bloc.add(const IsoChangedEvent(IsoValue(200, StopType.full)));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = Film.values.first).called(1);
|
||||
verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1);
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', null)
|
||||
.having((state) => state.ev, 'ev', null)
|
||||
.having((state) => state.film, 'film', Film.values.first)
|
||||
.having((state) => state.iso, 'iso', const IsoValue(200, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -213,7 +205,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -222,7 +213,6 @@ void main() {
|
|||
bloc.add(const IsoChangedEvent(IsoValue(100, StopType.full)));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = Film.values.first).called(1);
|
||||
verifyNever(() => meteringInteractor.iso = const IsoValue(100, StopType.full));
|
||||
},
|
||||
expect: () => [],
|
||||
|
@ -233,7 +223,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -244,14 +233,12 @@ void main() {
|
|||
bloc.onCommunicationState(const communication_states.MeteringEndedState(2));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = Film.values.first).called(1);
|
||||
verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1);
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 2.0)
|
||||
.having((state) => state.film, 'film', Film.values.first)
|
||||
.having((state) => state.iso, 'iso', const IsoValue(200, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -259,7 +246,6 @@ void main() {
|
|||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 2.0)
|
||||
.having((state) => state.ev, 'ev', 3.0)
|
||||
.having((state) => state.film, 'film', Film.values.first)
|
||||
.having((state) => state.iso, 'iso', const IsoValue(200, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -276,7 +262,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -291,7 +276,6 @@ void main() {
|
|||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 0.0)
|
||||
.having((state) => state.film, 'film', Film.values[1])
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', const NdValue(2))
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -303,7 +287,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: null,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -318,7 +301,6 @@ void main() {
|
|||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', null)
|
||||
.having((state) => state.ev, 'ev', null)
|
||||
.having((state) => state.film, 'film', Film.values[1])
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', const NdValue(2))
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -330,7 +312,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -349,7 +330,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -366,7 +346,6 @@ void main() {
|
|||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 0.0)
|
||||
.having((state) => state.film, 'film', Film.values[1])
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', const NdValue(2))
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -374,7 +353,6 @@ void main() {
|
|||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 2.0)
|
||||
.having((state) => state.ev, 'ev', 1.0)
|
||||
.having((state) => state.film, 'film', Film.values[1])
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', const NdValue(2))
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -383,115 +361,6 @@ void main() {
|
|||
},
|
||||
);
|
||||
|
||||
group(
|
||||
'`FilmChangedEvent`',
|
||||
() {
|
||||
blocTest<MeteringBloc, MeteringState>(
|
||||
'Pick different film with different ISO',
|
||||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: const FomapanFilm.creative100(),
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
),
|
||||
act: (bloc) async {
|
||||
bloc.add(const FilmChangedEvent(FomapanFilm.creative200()));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = const FomapanFilm.creative200()).called(1);
|
||||
verify(() => meteringInteractor.iso = const IsoValue(200, StopType.full)).called(1);
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 2.0)
|
||||
.having((state) => state.film, 'film', const FomapanFilm.creative200())
|
||||
.having((state) => state.iso, 'iso', const IsoValue(200, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<MeteringBloc, MeteringState>(
|
||||
'Pick different film with same ISO',
|
||||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: const FomapanFilm.creative100(),
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
),
|
||||
act: (bloc) async {
|
||||
bloc.add(const FilmChangedEvent(IlfordFilm.delta100()));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = const IlfordFilm.delta100()).called(1);
|
||||
verifyNever(() => meteringInteractor.iso = const IsoValue(100, StopType.full));
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 1.0)
|
||||
.having((state) => state.film, 'film', const IlfordFilm.delta100())
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<MeteringBloc, MeteringState>(
|
||||
'Pick same film',
|
||||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: const FomapanFilm.creative100(),
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
),
|
||||
act: (bloc) async {
|
||||
bloc.add(const FilmChangedEvent(FomapanFilm.creative100()));
|
||||
},
|
||||
verify: (_) {
|
||||
verifyNever(() => meteringInteractor.film = const FomapanFilm.creative100());
|
||||
},
|
||||
expect: () => [],
|
||||
);
|
||||
|
||||
blocTest<MeteringBloc, MeteringState>(
|
||||
'Pick `Film.other()`',
|
||||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: const FomapanFilm.creative100(),
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
),
|
||||
act: (bloc) async {
|
||||
bloc.add(const FilmChangedEvent(Film.other()));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = const Film.other()).called(1);
|
||||
verifyNever(() => meteringInteractor.iso = const IsoValue(0, StopType.full));
|
||||
verifyNever(() => meteringInteractor.responseVibration());
|
||||
},
|
||||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.ev, 'ev', 1.0)
|
||||
.having((state) => state.film, 'film', const Film.other())
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
group(
|
||||
'`EquipmentProfileChangedEvent`',
|
||||
() {
|
||||
|
@ -509,7 +378,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -518,7 +386,6 @@ void main() {
|
|||
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||
},
|
||||
verify: (_) {
|
||||
verifyNever(() => meteringInteractor.film = const Film.other());
|
||||
verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first);
|
||||
verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first);
|
||||
verifyNever(() => meteringInteractor.responseVibration());
|
||||
|
@ -531,7 +398,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: IsoValue.values[2],
|
||||
nd: NdValue.values.first,
|
||||
isMetering: false,
|
||||
|
@ -540,7 +406,6 @@ void main() {
|
|||
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = const Film.other()).called(1);
|
||||
verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1);
|
||||
verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first);
|
||||
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||
|
@ -548,7 +413,6 @@ void main() {
|
|||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.film, 'film', const Film.other())
|
||||
.having((state) => state.iso, 'iso', reducedProfile.isoValues.first)
|
||||
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -560,7 +424,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: const IsoValue(100, StopType.full),
|
||||
nd: NdValue.values[4],
|
||||
isMetering: false,
|
||||
|
@ -569,7 +432,6 @@ void main() {
|
|||
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||
},
|
||||
verify: (_) {
|
||||
verifyNever(() => meteringInteractor.film = const Film.other());
|
||||
verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first);
|
||||
verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1);
|
||||
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||
|
@ -577,7 +439,6 @@ void main() {
|
|||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.film, 'film', Film.values[1])
|
||||
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||
.having((state) => state.nd, 'nd', reducedProfile.ndValues.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
@ -589,7 +450,6 @@ void main() {
|
|||
build: () => bloc,
|
||||
seed: () => MeteringDataState(
|
||||
ev100: 1.0,
|
||||
film: Film.values[1],
|
||||
iso: IsoValue.values[2],
|
||||
nd: NdValue.values[4],
|
||||
isMetering: false,
|
||||
|
@ -598,7 +458,6 @@ void main() {
|
|||
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||
},
|
||||
verify: (_) {
|
||||
verify(() => meteringInteractor.film = const Film.other()).called(1);
|
||||
verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1);
|
||||
verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1);
|
||||
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||
|
@ -606,7 +465,6 @@ void main() {
|
|||
expect: () => [
|
||||
isA<MeteringDataState>()
|
||||
.having((state) => state.ev100, 'ev100', 1.0)
|
||||
.having((state) => state.film, 'film', const Film.other())
|
||||
.having((state) => state.iso, 'iso', reducedProfile.isoValues.first)
|
||||
.having((state) => state.nd, 'nd', reducedProfile.ndValues.first)
|
||||
.having((state) => state.isMetering, 'isMetering', false),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:lightmeter/data/models/exposure_pair.dart';
|
||||
import 'package:lightmeter/data/models/film.dart';
|
||||
import 'package:lightmeter/screens/metering/screen_metering.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
|
@ -19,7 +18,6 @@ void main() {
|
|||
ev,
|
||||
StopType.full,
|
||||
defaultEquipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('isNan', () {
|
||||
|
@ -42,7 +40,6 @@ void main() {
|
|||
ev,
|
||||
StopType.full,
|
||||
defaultEquipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -142,7 +139,6 @@ void main() {
|
|||
ev,
|
||||
StopType.half,
|
||||
defaultEquipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -242,7 +238,6 @@ void main() {
|
|||
ev,
|
||||
StopType.third,
|
||||
defaultEquipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -356,7 +351,6 @@ void main() {
|
|||
ev,
|
||||
StopType.full,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -456,7 +450,6 @@ void main() {
|
|||
ev,
|
||||
StopType.half,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -556,7 +549,6 @@ void main() {
|
|||
ev,
|
||||
StopType.third,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -669,7 +661,6 @@ void main() {
|
|||
ev,
|
||||
StopType.full,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -769,7 +760,6 @@ void main() {
|
|||
ev,
|
||||
StopType.half,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
@ -869,7 +859,6 @@ void main() {
|
|||
ev,
|
||||
StopType.third,
|
||||
equipmentProfile,
|
||||
const Film.other(),
|
||||
);
|
||||
|
||||
test('EV 1', () {
|
||||
|
|
Loading…
Reference in a new issue