From 00c0a6134d2feb95e2ccf13cd1e768c3547f6a2c Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Thu, 1 Dec 2022 23:49:06 +0300 Subject: [PATCH] Implemented metering dialog picker typo fixed closed container scale fixed dialog title animation implemented `AnimatedDialog` added open/close children transition clean up close dialog --- lib/generated/intl/messages_en.dart | 3 + lib/generated/l10n.dart | 30 ++ lib/l10n/intl_en.arb | 3 + lib/main.dart | 1 - .../topbar/components/dialog_picker.dart | 95 ++++++ .../topbar/components/reading_container.dart | 208 +------------ .../components/shared/animated_dialog.dart | 283 ++++++++++++++++++ .../topbar/models/reading_value.dart | 9 + .../metering/components/topbar/topbar.dart | 175 ++++++----- lib/screens/metering/metering_bloc.dart | 6 +- lib/screens/metering/metering_state.dart | 3 +- .../screen_permissions_check.dart | 1 - 12 files changed, 528 insertions(+), 289 deletions(-) create mode 100644 lib/screens/metering/components/topbar/components/dialog_picker.dart create mode 100644 lib/screens/metering/components/topbar/components/shared/animated_dialog.dart create mode 100644 lib/screens/metering/components/topbar/models/reading_value.dart diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 434a72a..f7ac216 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -23,8 +23,10 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { "caffeine": MessageLookupByLibrary.simpleMessage("Caffeine"), + "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), "fastestExposurePair": MessageLookupByLibrary.simpleMessage("Fastest"), "haptics": MessageLookupByLibrary.simpleMessage("Haptics"), + "iso": MessageLookupByLibrary.simpleMessage("ISO"), "keepsScreenOn": MessageLookupByLibrary.simpleMessage("Keeps screen on"), "openSettings": MessageLookupByLibrary.simpleMessage("Open settings"), @@ -32,6 +34,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Permission needed"), "permissionNeededMessage": MessageLookupByLibrary.simpleMessage( "To use Lightmeter, turn on Camera permissions."), + "select": MessageLookupByLibrary.simpleMessage("Select"), "settings": MessageLookupByLibrary.simpleMessage("Settings"), "slowestExposurePair": MessageLookupByLibrary.simpleMessage("Slowest") }; diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8e6b760..e6e18a2 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -100,6 +100,36 @@ class S { ); } + /// `ISO` + String get iso { + return Intl.message( + 'ISO', + name: 'iso', + desc: '', + args: [], + ); + } + + /// `Cancel` + String get cancel { + return Intl.message( + 'Cancel', + name: 'cancel', + desc: '', + args: [], + ); + } + + /// `Select` + String get select { + return Intl.message( + 'Select', + name: 'select', + desc: '', + args: [], + ); + } + /// `Settings` String get settings { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 86b8586..7f59ba1 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -5,6 +5,9 @@ "openSettings": "Open settings", "fastestExposurePair": "Fastest", "slowestExposurePair": "Slowest", + "iso": "ISO", + "cancel": "Cancel", + "select": "Select", "settings": "Settings", "caffeine": "Caffeine", "keepsScreenOn": "Keeps screen on", diff --git a/lib/main.dart b/lib/main.dart index 589c262..a8ec20d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,6 @@ import 'res/dimens.dart'; import 'res/theme.dart'; import 'screens/metering/metering_bloc.dart'; import 'screens/metering/metering_screen.dart'; -import 'screens/permissions_check/flow_permissions_check.dart'; import 'utils/stop_type_provider.dart'; void main() { diff --git a/lib/screens/metering/components/topbar/components/dialog_picker.dart b/lib/screens/metering/components/topbar/components/dialog_picker.dart new file mode 100644 index 0000000..fb79eef --- /dev/null +++ b/lib/screens/metering/components/topbar/components/dialog_picker.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; + +class MeteringScreenDialogPicker extends StatefulWidget { + final String title; + final T initialValue; + final List values; + final Widget Function(BuildContext context, T value) itemTitleBuilder; + final VoidCallback onCancel; + final ValueChanged onSelect; + + const MeteringScreenDialogPicker({ + required this.title, + required this.initialValue, + required this.values, + required this.itemTitleBuilder, + required this.onCancel, + required this.onSelect, + super.key, + }); + + @override + State> createState() => _MeteringScreenDialogPickerState(); +} + +class _MeteringScreenDialogPickerState extends State> { + late T _selectedValue = widget.initialValue; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB( + Dimens.paddingL, + Dimens.paddingL, + Dimens.paddingL, + Dimens.paddingM, + ), + child: Text( + widget.title, + style: Theme.of(context).textTheme.headlineSmall!, + ), + ), + Divider( + color: Theme.of(context).colorScheme.onPrimaryContainer, + height: 0, + ), + Expanded( + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: widget.values.length, + itemBuilder: (context, index) => RadioListTile( + value: widget.values[index], + groupValue: _selectedValue, + title: widget.itemTitleBuilder(context, widget.values[index]), + onChanged: (value) { + if (value != null) { + setState(() { + _selectedValue = value; + }); + } + }, + ), + ), + ), + Divider( + color: Theme.of(context).colorScheme.onPrimaryContainer, + height: 0, + ), + Padding( + padding: const EdgeInsets.all(Dimens.paddingL), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + const Spacer(), + TextButton( + onPressed: widget.onCancel, + child: Text(S.of(context).cancel), + ), + const SizedBox(width: Dimens.grid16), + TextButton( + onPressed: () => widget.onSelect(_selectedValue), + child: Text(S.of(context).select), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/screens/metering/components/topbar/components/reading_container.dart b/lib/screens/metering/components/topbar/components/reading_container.dart index 1701be0..b857c30 100644 --- a/lib/screens/metering/components/topbar/components/reading_container.dart +++ b/lib/screens/metering/components/topbar/components/reading_container.dart @@ -1,212 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:lightmeter/res/dimens.dart'; - -class ReadingValue { - final String label; - final String value; - - const ReadingValue({ - required this.label, - required this.value, - }); -} - -class ReadingContainerWithDialog extends StatefulWidget { - final ReadingValue value; - final Widget Function(BuildContext context) dialogBuilder; - - const ReadingContainerWithDialog({ - required this.value, - required this.dialogBuilder, - super.key, - }); - - @override - State createState() => _ReadingContainerWithDialogState(); -} - -class _ReadingContainerWithDialogState extends State with SingleTickerProviderStateMixin { - final GlobalKey _key = GlobalKey(); - final LayerLink _layerLink = LayerLink(); - OverlayEntry? _overlayEntry; - - late final _animationController = AnimationController( - duration: Dimens.durationL, - reverseDuration: Dimens.durationML, - vsync: this, - ); - late final _defaultCurve = CurvedAnimation(parent: _animationController, curve: Curves.linear); - - late final _colorAnimation = ColorTween( - begin: Colors.transparent, - end: Colors.black54, - ).animate(_defaultCurve); - late final _borderRadiusAnimation = Tween( - begin: Dimens.borderRadiusM, - end: Dimens.borderRadiusXL, - ).animate(_defaultCurve); - late final _itemOpacityAnimation = Tween( - begin: 1, - end: 0, - ).animate(CurvedAnimation( - parent: _animationController, - curve: const Interval(0, 0.5, curve: Curves.linear), - )); - late final _dialogOpacityAnimation = Tween( - begin: 0, - end: 1, - ).animate(CurvedAnimation( - parent: _animationController, - curve: const Interval(0.5, 1.0, curve: Curves.linear), - )); - - late final SizeTween _sizeTween; - late final Animation _sizeAnimation; - late final Animation _offsetAnimation; - - @override - void initState() { - super.initState(); - //timeDilation = 5.0; - WidgetsBinding.instance.addPostFrameCallback((_) { - final mediaQuery = MediaQuery.of(context); - final itemWidth = _key.currentContext!.size!.width; - final itemHeight = _key.currentContext!.size!.height; - - _sizeTween = SizeTween( - begin: Size( - itemWidth, - itemHeight, - ), - end: Size( - mediaQuery.size.width - mediaQuery.padding.horizontal - Dimens.paddingL * 2, - mediaQuery.size.height - mediaQuery.padding.vertical - Dimens.paddingL * 2, - ), - ); - _sizeAnimation = _sizeTween.animate(_defaultCurve); - - final renderBox = _key.currentContext!.findRenderObject() as RenderBox; - final offset = renderBox.localToGlobal(Offset.zero); - _offsetAnimation = SizeTween( - begin: Size( - offset.dx + itemWidth / 2, - offset.dy + itemHeight / 2, - ), - end: Size( - mediaQuery.size.width / 2, - mediaQuery.size.height / 2 + mediaQuery.padding.top / 2 - mediaQuery.padding.bottom / 2, - ), - ).animate(_defaultCurve); - }); - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return InkWell( - key: _key, - onTap: _openDialog, - child: CompositedTransformTarget( - link: _layerLink, - child: ClipRRect( - borderRadius: BorderRadius.circular(Dimens.borderRadiusM), - child: ColoredBox( - color: Theme.of(context).colorScheme.primaryContainer, - child: Padding( - padding: const EdgeInsets.all(Dimens.paddingM), - child: _ReadingValueBuilder(widget.value), - ), - ), - ), - ), - ); - } - - void _openDialog() { - final RenderBox renderBox = _key.currentContext!.findRenderObject() as RenderBox; - final offset = renderBox.localToGlobal(Offset.zero); - _overlayEntry = OverlayEntry( - builder: (context) => CompositedTransformFollower( - offset: Offset(-offset.dx, -offset.dy), - link: _layerLink, - showWhenUnlinked: false, - child: SizedBox.fromSize( - size: MediaQuery.of(context).size, - child: AnimatedBuilder( - animation: _animationController, - builder: (context, _) => Stack( - children: [ - Positioned.fill( - child: GestureDetector( - onTap: _closeDialog, - child: ColoredBox(color: _colorAnimation.value!), - ), - ), - Positioned.fromRect( - rect: Rect.fromCenter( - center: Offset( - _offsetAnimation.value!.width, - _offsetAnimation.value!.height, - ), - width: _sizeAnimation.value!.width, - height: _sizeAnimation.value!.height, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(_borderRadiusAnimation.value), - child: ColoredBox( - color: Theme.of(context).colorScheme.primaryContainer, - child: Center( - child: Stack( - children: [ - Opacity( - opacity: _itemOpacityAnimation.value, - child: Transform.scale( - scale: _sizeAnimation.value!.width / _sizeTween.begin!.width, - child: SizedBox( - width: _sizeTween.begin!.width, - child: Padding( - padding: const EdgeInsets.all(Dimens.paddingM), - child: _ReadingValueBuilder(widget.value), - ), - ), - ), - ), - Opacity( - opacity: _dialogOpacityAnimation.value, - child: Transform.scale( - scale: _sizeAnimation.value!.width / _sizeTween.end!.width, - child: widget.dialogBuilder(context), - ), - ), - ], - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ); - Overlay.of(context)?.insert(_overlayEntry!); - _animationController.forward(); - } - - void _closeDialog() { - _animationController.reverse(); - Future.delayed(_animationController.reverseDuration! * timeDilation).then((_) { - _overlayEntry?.remove(); - }); - } -} +import 'package:lightmeter/screens/metering/components/topbar/models/reading_value.dart'; class ReadingContainer extends StatelessWidget { final List<_ReadingValueBuilder> _items; diff --git a/lib/screens/metering/components/topbar/components/shared/animated_dialog.dart b/lib/screens/metering/components/topbar/components/shared/animated_dialog.dart new file mode 100644 index 0000000..a8ae8d9 --- /dev/null +++ b/lib/screens/metering/components/topbar/components/shared/animated_dialog.dart @@ -0,0 +1,283 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:lightmeter/res/dimens.dart'; + +class AnimatedDialog extends StatefulWidget { + final Size? openedSize; + final Widget? closedChild; + final Widget? openedChild; + final Widget? child; + + const AnimatedDialog({ + this.openedSize, + this.closedChild, + this.openedChild, + this.child, + super.key, + }); + + @override + State createState() => AnimatedDialogState(); +} + +class AnimatedDialogState extends State with SingleTickerProviderStateMixin { + final GlobalKey _key = GlobalKey(); + final LayerLink _layerLink = LayerLink(); + + late final Size _closedSize; + late final Offset _closedOffset; + + late final AnimationController _animationController; + late final CurvedAnimation _defaultCurvedAnimation; + late final Animation _barrierColorAnimation; + late final SizeTween _sizeTween; + late final Animation _sizeAnimation; + late final Animation _offsetAnimation; + late final Animation _borderRadiusAnimation; + late final Animation _closedOpacityAnimation; + late final Animation _openedOpacityAnimation; + OverlayEntry? _overlayEntry; + + @override + void initState() { + super.initState(); + //timeDilation = 10.0; + _animationController = AnimationController( + duration: Dimens.durationL, + reverseDuration: Dimens.durationML, + vsync: this, + ); + _defaultCurvedAnimation = CurvedAnimation(parent: _animationController, curve: Curves.easeInOut); + _barrierColorAnimation = ColorTween( + begin: Colors.transparent, + end: Colors.black54, + ).animate(_defaultCurvedAnimation); + _borderRadiusAnimation = Tween( + begin: Dimens.borderRadiusM, + end: Dimens.borderRadiusXL, + ).animate(_defaultCurvedAnimation); + + _closedOpacityAnimation = Tween( + begin: 1, + end: 0, + ).animate(CurvedAnimation( + parent: _animationController, + curve: const Interval( + 0, + 0.8, + curve: Curves.ease, + ), + )); + _openedOpacityAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation( + parent: _animationController, + curve: const Interval( + 0.8, + 1.0, + curve: Curves.easeInOut, + ), + )); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final mediaQuery = MediaQuery.of(context); + + _closedSize = _key.currentContext!.size!; + _sizeTween = SizeTween( + begin: _closedSize, + end: widget.openedSize ?? + Size( + mediaQuery.size.width - mediaQuery.padding.horizontal - Dimens.paddingM * 4, + mediaQuery.size.height - mediaQuery.padding.vertical - Dimens.paddingM * 4, + ), + ); + _sizeAnimation = _sizeTween.animate(_defaultCurvedAnimation); + + final renderBox = _key.currentContext!.findRenderObject() as RenderBox; + _closedOffset = renderBox.localToGlobal(Offset.zero); + _offsetAnimation = SizeTween( + begin: Size( + _closedOffset.dx + _closedSize.width / 2, + _closedOffset.dy + _closedSize.height / 2, + ), + end: Size( + mediaQuery.size.width / 2, + mediaQuery.size.height / 2 + mediaQuery.padding.top / 2 - mediaQuery.padding.bottom / 2, + ), + ).animate(_defaultCurvedAnimation); + }); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return InkWell( + key: _key, + onTap: _openDialog, + child: CompositedTransformTarget( + link: _layerLink, + child: Opacity( + opacity: _overlayEntry != null ? 0 : 1, + child: ClipRRect( + borderRadius: BorderRadius.circular(Dimens.borderRadiusM), + child: ColoredBox( + color: Theme.of(context).colorScheme.primaryContainer, + child: widget.child ?? widget.closedChild, + ), + ), + ), + ), + ); + } + + void _openDialog() { + setState(() { + _overlayEntry = OverlayEntry( + builder: (context) => CompositedTransformFollower( + offset: Offset(-_closedOffset.dx, -_closedOffset.dy), + link: _layerLink, + showWhenUnlinked: false, + child: _AnimatedOverlay( + controller: _animationController, + barrierColorAnimation: _barrierColorAnimation, + sizeAnimation: _sizeAnimation, + offsetAnimation: _offsetAnimation, + borderRadiusAnimation: _borderRadiusAnimation, + onDismiss: close, + builder: widget.closedChild != null && widget.openedChild != null + ? (_) => _AnimatedSwitcher( + sizeAnimation: _sizeAnimation, + closedOpacityAnimation: _closedOpacityAnimation, + openedOpacityAnimation: _openedOpacityAnimation, + closedSize: _sizeTween.begin!, + openedSize: _sizeTween.end!, + closedChild: widget.closedChild!, + openedChild: widget.openedChild!, + ) + : null, + child: widget.child, + ), + ), + ); + }); + Overlay.of(context)?.insert(_overlayEntry!); + _animationController.forward(); + } + + Future close() async { + _animationController.reverse(); + await Future.delayed(_animationController.reverseDuration! * timeDilation); + _overlayEntry?.remove(); + setState(() { + _overlayEntry = null; + }); + } +} + +class _AnimatedOverlay extends StatelessWidget { + final AnimationController controller; + final Animation barrierColorAnimation; + final Animation sizeAnimation; + final Animation offsetAnimation; + final Animation borderRadiusAnimation; + final VoidCallback onDismiss; + final Widget? child; + final Widget Function(BuildContext context)? builder; + + const _AnimatedOverlay({ + required this.controller, + required this.barrierColorAnimation, + required this.sizeAnimation, + required this.offsetAnimation, + required this.borderRadiusAnimation, + required this.onDismiss, + this.child, + this.builder, + }); + + @override + Widget build(BuildContext context) { + return SizedBox.fromSize( + size: MediaQuery.of(context).size, + child: AnimatedBuilder( + animation: controller, + builder: (context, _) => Stack( + children: [ + Positioned.fill( + child: GestureDetector( + onTap: onDismiss, + child: ColoredBox(color: barrierColorAnimation.value!), + ), + ), + Positioned.fromRect( + rect: Rect.fromCenter( + center: Offset( + offsetAnimation.value!.width, + offsetAnimation.value!.height, + ), + width: sizeAnimation.value!.width, + height: sizeAnimation.value!.height, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(borderRadiusAnimation.value), + child: Material( + color: Theme.of(context).colorScheme.primaryContainer, + child: builder?.call(context) ?? child, + ), + ), + ), + ], + ), + ), + ); + } +} + +class _AnimatedSwitcher extends StatelessWidget { + final Animation sizeAnimation; + final Animation closedOpacityAnimation; + final Animation openedOpacityAnimation; + final Size closedSize; + final Size openedSize; + final Widget closedChild; + final Widget openedChild; + + const _AnimatedSwitcher({ + required this.sizeAnimation, + required this.closedOpacityAnimation, + required this.openedOpacityAnimation, + required this.closedSize, + required this.openedSize, + required this.closedChild, + required this.openedChild, + }); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + Opacity( + opacity: closedOpacityAnimation.value, + child: Transform.scale( + scale: sizeAnimation.value!.width / closedSize.width, + child: SizedBox( + width: closedSize.width, + child: closedChild, + ), + ), + ), + Opacity( + opacity: openedOpacityAnimation.value, + child: openedChild, + ), + ], + ); + } +} diff --git a/lib/screens/metering/components/topbar/models/reading_value.dart b/lib/screens/metering/components/topbar/models/reading_value.dart new file mode 100644 index 0000000..be0f3bf --- /dev/null +++ b/lib/screens/metering/components/topbar/models/reading_value.dart @@ -0,0 +1,9 @@ +class ReadingValue { + final String label; + final String value; + + const ReadingValue({ + required this.label, + required this.value, + }); +} diff --git a/lib/screens/metering/components/topbar/topbar.dart b/lib/screens/metering/components/topbar/topbar.dart index 37d0152..006cbf8 100644 --- a/lib/screens/metering/components/topbar/topbar.dart +++ b/lib/screens/metering/components/topbar/topbar.dart @@ -1,20 +1,25 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/models/exposure_pair.dart'; +import 'package:lightmeter/models/photography_value.dart'; import 'package:lightmeter/res/dimens.dart'; +import 'components/shared/animated_dialog.dart'; +import 'components/dialog_picker.dart'; import 'components/reading_container.dart'; +import 'models/reading_value.dart'; class MeteringTopBar extends StatelessWidget { static const _columnsCount = 3; + final _isoDialogKey = GlobalKey(); final ExposurePair? fastest; final ExposurePair? slowest; final double ev; - final int iso; + final IsoValue iso; final double nd; - const MeteringTopBar({ + MeteringTopBar({ required this.fastest, required this.slowest, required this.ev, @@ -38,91 +43,109 @@ class MeteringTopBar extends StatelessWidget { padding: const EdgeInsets.all(Dimens.paddingM), child: SafeArea( bottom: false, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox( - height: columnWidth / 3 * 4, - child: ReadingContainer( - values: [ - ReadingValue( - label: S.of(context).fastestExposurePair, - value: fastest != null - ? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}' - : 'N/A', + child: MediaQuery( + data: MediaQuery.of(context), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + height: columnWidth / 3 * 4, + child: ReadingContainer( + values: [ + ReadingValue( + label: S.of(context).fastestExposurePair, + value: fastest != null + ? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}' + : 'N/A', + ), + ReadingValue( + label: S.of(context).slowestExposurePair, + value: fastest != null + ? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}' + : 'N/A', + ), + ], + ), + ), + const _InnerPadding(), + Row( + children: [ + SizedBox( + width: columnWidth, + child: ReadingContainer.singleValue( + value: ReadingValue( + label: 'EV', + value: ev.toStringAsFixed(1), + ), + ), ), - ReadingValue( - label: S.of(context).slowestExposurePair, - value: fastest != null - ? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}' - : 'N/A', + const _InnerPadding(), + SizedBox( + width: columnWidth, + child: AnimatedDialog( + key: _isoDialogKey, + closedChild: ReadingContainer.singleValue( + value: ReadingValue( + label: S.of(context).iso, + value: iso.value.toString(), + ), + ), + openedChild: MeteringScreenDialogPicker( + title: S.of(context).iso, + initialValue: iso, + values: isoValues, + itemTitleBuilder: (context, value) => Text( + value.value.toString(), + style: value.stopType == StopType.full + ? Theme.of(context).textTheme.bodyLarge + : Theme.of(context).textTheme.bodySmall, + ), + onCancel: () { + _isoDialogKey.currentState?.close(); + }, + onSelect: (value) { + _isoDialogKey.currentState?.close(); + }, + ), + ), ), ], - ), - ), - const _InnerPadding(), - Row( - children: [ - SizedBox( - width: columnWidth, - child: ReadingContainer.singleValue( - value: ReadingValue( - label: 'EV', - value: ev.toStringAsFixed(1), - ), - ), - ), - const _InnerPadding(), - SizedBox( - width: columnWidth, - child: MediaQuery( - data: MediaQuery.of(context), - child: ReadingContainerWithDialog( - value: ReadingValue( - label: 'ISO', - value: iso.toString(), - ), - dialogBuilder: (context) => SizedBox(), - ), - ), - ), - ], - ) - ], + ) + ], + ), ), - ), - const _InnerPadding(), - SizedBox( - width: columnWidth, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(Dimens.borderRadiusM), - child: const AspectRatio( - aspectRatio: 3 / 4, - child: ColoredBox(color: Colors.black), + const _InnerPadding(), + SizedBox( + width: columnWidth, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + AnimatedDialog( + openedSize: Size( + MediaQuery.of(context).size.width - Dimens.paddingM * 2, + (MediaQuery.of(context).size.width - Dimens.paddingM * 2) / 3 * 4, + ), + child: const AspectRatio( + aspectRatio: 3 / 4, + child: ColoredBox(color: Colors.black), + ), ), - ), - const _InnerPadding(), - MediaQuery( - data: MediaQuery.of(context), - child: ReadingContainerWithDialog( + const _InnerPadding(), + ReadingContainer.singleValue( value: ReadingValue( label: 'ND', value: nd.toString(), ), - dialogBuilder: (context) => SizedBox(), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/screens/metering/metering_bloc.dart b/lib/screens/metering/metering_bloc.dart index 9890cda..0a08fa2 100644 --- a/lib/screens/metering/metering_bloc.dart +++ b/lib/screens/metering/metering_bloc.dart @@ -16,8 +16,8 @@ class MeteringBloc extends Bloc { MeteringBloc(this.stopType) : super( - const MeteringState( - iso: 100, + MeteringState( + iso: isoValues.where((element) => element.value == 100).first, ev: 21.3, evCompensation: 0.0, nd: 0.0, @@ -38,7 +38,7 @@ class MeteringBloc extends Bloc { final iso = _isoValues[_random.nextInt(_isoValues.thirdStops().length)]; final evAtSystemIso = log2(pow(aperture.value, 2).toDouble()) - log2(shutterSpeed.value); - final ev = evAtSystemIso - log2(iso.value / state.iso); + final ev = evAtSystemIso - log2(iso.value / state.iso.value); final exposurePairs = _buildExposureValues(ev); emit(MeteringState( diff --git a/lib/screens/metering/metering_state.dart b/lib/screens/metering/metering_state.dart index 331d03d..d180710 100644 --- a/lib/screens/metering/metering_state.dart +++ b/lib/screens/metering/metering_state.dart @@ -1,9 +1,10 @@ import 'package:lightmeter/models/exposure_pair.dart'; +import 'package:lightmeter/models/photography_value.dart'; class MeteringState { final double ev; final double evCompensation; - final int iso; + final IsoValue iso; final double nd; final List exposurePairs; diff --git a/lib/screens/permissions_check/screen_permissions_check.dart b/lib/screens/permissions_check/screen_permissions_check.dart index 61fac5f..18dfca7 100644 --- a/lib/screens/permissions_check/screen_permissions_check.dart +++ b/lib/screens/permissions_check/screen_permissions_check.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; -import 'package:lightmeter/screens/settings/settings_screen.dart'; import 'bloc_permissions_check.dart'; import 'state_permissions_check.dart';