2022-10-24 20:25:38 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2022-12-15 09:25:52 +00:00
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import 'package:lightmeter/data/ev_source/camera/bloc_camera.dart';
|
2022-10-30 18:30:12 +00:00
|
|
|
import 'package:lightmeter/generated/l10n.dart';
|
2022-12-16 08:08:12 +00:00
|
|
|
import 'package:lightmeter/data/models/exposure_pair.dart';
|
|
|
|
import 'package:lightmeter/data/models/iso_value.dart';
|
|
|
|
import 'package:lightmeter/data/models/nd_value.dart';
|
|
|
|
import 'package:lightmeter/data/models/photography_value.dart';
|
2022-10-24 20:25:38 +00:00
|
|
|
import 'package:lightmeter/res/dimens.dart';
|
2022-10-29 18:02:45 +00:00
|
|
|
|
2022-12-14 17:33:38 +00:00
|
|
|
import 'components/camera_preview.dart';
|
2022-12-01 20:49:06 +00:00
|
|
|
import 'components/shared/animated_dialog.dart';
|
|
|
|
import 'components/dialog_picker.dart';
|
2022-10-29 18:02:45 +00:00
|
|
|
import 'components/reading_container.dart';
|
2022-12-01 20:49:06 +00:00
|
|
|
import 'models/reading_value.dart';
|
2022-10-24 20:25:38 +00:00
|
|
|
|
|
|
|
class MeteringTopBar extends StatelessWidget {
|
2022-10-29 18:02:45 +00:00
|
|
|
final ExposurePair? fastest;
|
|
|
|
final ExposurePair? slowest;
|
2022-10-24 20:25:38 +00:00
|
|
|
final double ev;
|
2022-12-01 20:49:06 +00:00
|
|
|
final IsoValue iso;
|
2022-12-04 19:12:52 +00:00
|
|
|
final NdValue nd;
|
2022-12-04 19:00:43 +00:00
|
|
|
final ValueChanged<IsoValue> onIsoChanged;
|
2022-12-04 19:12:52 +00:00
|
|
|
final ValueChanged<NdValue> onNdChanged;
|
2022-10-24 20:25:38 +00:00
|
|
|
|
2022-12-04 20:10:04 +00:00
|
|
|
const MeteringTopBar({
|
2022-10-29 18:02:45 +00:00
|
|
|
required this.fastest,
|
|
|
|
required this.slowest,
|
2022-10-24 20:25:38 +00:00
|
|
|
required this.ev,
|
|
|
|
required this.iso,
|
|
|
|
required this.nd,
|
2022-12-04 19:00:43 +00:00
|
|
|
required this.onIsoChanged,
|
2022-12-04 19:12:52 +00:00
|
|
|
required this.onNdChanged,
|
2022-10-24 20:25:38 +00:00
|
|
|
super.key,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2022-10-29 11:06:39 +00:00
|
|
|
return ClipRRect(
|
|
|
|
borderRadius: const BorderRadius.only(
|
|
|
|
bottomLeft: Radius.circular(Dimens.borderRadiusL),
|
|
|
|
bottomRight: Radius.circular(Dimens.borderRadiusL),
|
|
|
|
),
|
|
|
|
child: ColoredBox(
|
|
|
|
color: Theme.of(context).colorScheme.surface,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(Dimens.paddingM),
|
|
|
|
child: SafeArea(
|
|
|
|
bottom: false,
|
2022-12-01 20:49:06 +00:00
|
|
|
child: MediaQuery(
|
|
|
|
data: MediaQuery.of(context),
|
2022-12-15 09:25:52 +00:00
|
|
|
child: Row(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: [
|
|
|
|
ReadingContainer(
|
|
|
|
values: [
|
|
|
|
ReadingValue(
|
|
|
|
label: S.of(context).fastestExposurePair,
|
|
|
|
value: fastest != null
|
|
|
|
? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}'
|
2022-12-15 11:00:28 +00:00
|
|
|
: '-',
|
2022-12-11 14:09:10 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
ReadingValue(
|
|
|
|
label: S.of(context).slowestExposurePair,
|
|
|
|
value: fastest != null
|
|
|
|
? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}'
|
2022-12-15 11:00:28 +00:00
|
|
|
: '-',
|
2022-12-14 17:33:38 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
/*
|
|
|
|
const _InnerPadding(),
|
|
|
|
ReadingContainer.singleValue(
|
|
|
|
value: ReadingValue(
|
|
|
|
label: 'EV',
|
|
|
|
value: ev.toStringAsFixed(1),
|
2022-12-14 17:33:38 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
),
|
|
|
|
*/
|
|
|
|
const _InnerPadding(),
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: _AnimatedDialogPicker(
|
|
|
|
title: S.of(context).iso,
|
|
|
|
subtitle: S.of(context).filmSpeed,
|
|
|
|
selectedValue: iso,
|
|
|
|
values: isoValues,
|
|
|
|
itemTitleBuilder: (_, value) => Text(value.value.toString()),
|
|
|
|
// using ascending order, because increase in film speed rises EV
|
|
|
|
evDifferenceBuilder: (selected, other) => selected.toStringDifference(other),
|
|
|
|
onChanged: onIsoChanged,
|
2022-10-29 11:06:39 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
),
|
|
|
|
const _InnerPadding(),
|
|
|
|
Expanded(
|
|
|
|
child: _AnimatedDialogPicker(
|
|
|
|
title: S.of(context).nd,
|
|
|
|
subtitle: S.of(context).ndFilterFactor,
|
|
|
|
selectedValue: nd,
|
|
|
|
values: ndValues,
|
|
|
|
itemTitleBuilder: (_, value) => Text(
|
|
|
|
value.value == 0 ? S.of(context).none : value.value.toString(),
|
2022-12-11 14:09:10 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
// using descending order, because ND filter darkens image & lowers EV
|
|
|
|
evDifferenceBuilder: (selected, other) => other.toStringDifference(selected),
|
|
|
|
onChanged: onNdChanged,
|
2022-12-01 20:49:06 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
],
|
2022-12-01 20:49:06 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
),
|
|
|
|
const _InnerPadding(),
|
|
|
|
Expanded(
|
|
|
|
child: AnimatedDialog(
|
|
|
|
openedSize: Size(
|
|
|
|
MediaQuery.of(context).size.width - Dimens.paddingM * 2,
|
|
|
|
(MediaQuery.of(context).size.width - Dimens.paddingM * 2) / 3 * 4,
|
|
|
|
),
|
|
|
|
child: BlocProvider.value(
|
|
|
|
value: context.read<CameraBloc>(),
|
|
|
|
child: const CameraView(),
|
2022-12-11 14:09:10 +00:00
|
|
|
),
|
2022-12-01 20:49:06 +00:00
|
|
|
),
|
2022-12-15 09:25:52 +00:00
|
|
|
),
|
|
|
|
],
|
2022-12-01 20:49:06 +00:00
|
|
|
),
|
2022-10-29 11:06:39 +00:00
|
|
|
),
|
2022-10-24 20:25:38 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _InnerPadding extends SizedBox {
|
2022-12-11 14:09:10 +00:00
|
|
|
const _InnerPadding() : super(height: Dimens.grid8, width: Dimens.grid8);
|
2022-10-24 20:25:38 +00:00
|
|
|
}
|
2022-12-04 19:46:11 +00:00
|
|
|
|
2022-12-04 20:10:04 +00:00
|
|
|
class _AnimatedDialogPicker<T extends PhotographyValue> extends StatelessWidget {
|
|
|
|
final _key = GlobalKey<AnimatedDialogState>();
|
|
|
|
final String title;
|
|
|
|
final String subtitle;
|
|
|
|
final T selectedValue;
|
|
|
|
final List<T> values;
|
|
|
|
final DialogPickerItemBuilder<T> itemTitleBuilder;
|
|
|
|
final DialogPickerEvDifferenceBuilder<T> evDifferenceBuilder;
|
|
|
|
final ValueChanged<T> onChanged;
|
|
|
|
|
2022-12-04 19:46:11 +00:00
|
|
|
_AnimatedDialogPicker({
|
2022-12-04 20:10:04 +00:00
|
|
|
required this.title,
|
|
|
|
required this.subtitle,
|
|
|
|
required this.selectedValue,
|
|
|
|
required this.values,
|
|
|
|
required this.itemTitleBuilder,
|
|
|
|
required this.evDifferenceBuilder,
|
|
|
|
required this.onChanged,
|
|
|
|
}) : super();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return AnimatedDialog(
|
|
|
|
key: _key,
|
|
|
|
closedChild: ReadingContainer.singleValue(
|
|
|
|
value: ReadingValue(
|
|
|
|
label: title,
|
|
|
|
value: selectedValue.value.toString(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
openedChild: MeteringScreenDialogPicker<T>(
|
|
|
|
title: title,
|
|
|
|
subtitle: subtitle,
|
|
|
|
initialValue: selectedValue,
|
|
|
|
values: values,
|
|
|
|
itemTitleBuilder: itemTitleBuilder,
|
|
|
|
evDifferenceBuilder: evDifferenceBuilder,
|
|
|
|
onCancel: () {
|
|
|
|
_key.currentState?.close();
|
|
|
|
},
|
|
|
|
onSelect: (value) {
|
|
|
|
_key.currentState?.close().then((_) => onChanged(value));
|
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
2022-12-04 19:46:11 +00:00
|
|
|
}
|