mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
added start/stop button
This commit is contained in:
parent
378ab45f45
commit
c532801358
5 changed files with 223 additions and 303 deletions
|
@ -1,201 +1,76 @@
|
|||
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/volume_action.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
|
||||
as communication_events;
|
||||
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
|
||||
as communication_states;
|
||||
import 'package:lightmeter/screens/metering/event_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/state_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/utils/notifier_volume_keys.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
import 'package:lightmeter/screens/timer/event_timer.dart';
|
||||
import 'package:lightmeter/screens/timer/state_timer.dart';
|
||||
|
||||
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||
class TimerBloc extends Bloc<TimerEvent, TimerState> {
|
||||
final MeteringInteractor _meteringInteractor;
|
||||
final VolumeKeysNotifier _volumeKeysNotifier;
|
||||
final MeteringCommunicationBloc _communicationBloc;
|
||||
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
|
||||
late Timer? _timer;
|
||||
final int timerLength;
|
||||
|
||||
MeteringBloc(
|
||||
this._meteringInteractor,
|
||||
this._volumeKeysNotifier,
|
||||
this._communicationBloc,
|
||||
) : super(
|
||||
MeteringDataState(
|
||||
ev100: null,
|
||||
iso: _meteringInteractor.iso,
|
||||
nd: _meteringInteractor.ndFilter,
|
||||
isMetering: false,
|
||||
TimerBloc(this._meteringInteractor, this.timerLength)
|
||||
: super(
|
||||
TimerStoppedState(
|
||||
duration: Duration(seconds: timerLength),
|
||||
timeLeft: Duration(seconds: timerLength),
|
||||
),
|
||||
) {
|
||||
_volumeKeysNotifier.addListener(onVolumeKey);
|
||||
_communicationSubscription = _communicationBloc.stream
|
||||
.where((state) => state is communication_states.ScreenState)
|
||||
.map((state) => state as communication_states.ScreenState)
|
||||
.listen(onCommunicationState);
|
||||
|
||||
on<EquipmentProfileChangedEvent>(_onEquipmentProfileChanged);
|
||||
on<IsoChangedEvent>(_onIsoChanged);
|
||||
on<NdChangedEvent>(_onNdChanged);
|
||||
on<MeasureEvent>(_onMeasure, transformer: droppable());
|
||||
on<MeasuredEvent>(_onMeasured);
|
||||
on<MeasureErrorEvent>(_onMeasureError);
|
||||
on<SettingsOpenedEvent>(_onSettingsOpened);
|
||||
on<SettingsClosedEvent>(_onSettingsClosed);
|
||||
}
|
||||
|
||||
@override
|
||||
void onTransition(Transition<MeteringEvent, MeteringState> transition) {
|
||||
super.onTransition(transition);
|
||||
if (transition.nextState is MeteringDataState) {
|
||||
final nextState = transition.nextState as MeteringDataState;
|
||||
if (transition.currentState is LoadingState ||
|
||||
transition.currentState is MeteringDataState &&
|
||||
(transition.currentState as MeteringDataState).ev != nextState.ev) {
|
||||
if (nextState.hasError) {
|
||||
_meteringInteractor.errorVibration();
|
||||
} else {
|
||||
_meteringInteractor.responseVibration();
|
||||
}
|
||||
}
|
||||
}
|
||||
on<StartTimerEvent>(_onStartTimer);
|
||||
on<SetTimeLeftEvent>(_onSetTimeLeft);
|
||||
on<StopTimerEvent>(_onStopTimer);
|
||||
on<ResetTimerEvent>(_onResetTimer);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
_volumeKeysNotifier.removeListener(onVolumeKey);
|
||||
await _communicationSubscription.cancel();
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void onCommunicationState(communication_states.ScreenState communicationState) {
|
||||
if (communicationState is communication_states.MeasuredState) {
|
||||
_handleEv100(
|
||||
communicationState.ev100,
|
||||
isMetering: communicationState is communication_states.MeteringInProgressState,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onEquipmentProfileChanged(EquipmentProfileChangedEvent event, Emitter emit) {
|
||||
bool willUpdateMeasurements = false;
|
||||
|
||||
/// Update selected ISO value and discard selected film, if selected equipment profile
|
||||
/// doesn't contain currently selected value
|
||||
IsoValue iso = state.iso;
|
||||
if (!event.equipmentProfileData.isoValues.any((v) => state.iso.value == v.value)) {
|
||||
_meteringInteractor.iso = event.equipmentProfileData.isoValues.first;
|
||||
iso = event.equipmentProfileData.isoValues.first;
|
||||
willUpdateMeasurements = true;
|
||||
}
|
||||
|
||||
/// The same for ND filter
|
||||
NdValue nd = state.nd;
|
||||
if (!event.equipmentProfileData.ndValues.any((v) => state.nd.value == v.value)) {
|
||||
_meteringInteractor.ndFilter = event.equipmentProfileData.ndValues.first;
|
||||
nd = event.equipmentProfileData.ndValues.first;
|
||||
willUpdateMeasurements = true;
|
||||
}
|
||||
|
||||
if (willUpdateMeasurements) {
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
iso: iso,
|
||||
nd: nd,
|
||||
isMetering: state.isMetering,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onIsoChanged(IsoChangedEvent event, Emitter emit) {
|
||||
if (state.iso != event.isoValue) {
|
||||
_meteringInteractor.iso = event.isoValue;
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
iso: event.isoValue,
|
||||
nd: state.nd,
|
||||
isMetering: state.isMetering,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onNdChanged(NdChangedEvent event, Emitter emit) {
|
||||
if (state.nd != event.ndValue) {
|
||||
_meteringInteractor.ndFilter = event.ndValue;
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: state.ev100,
|
||||
iso: state.iso,
|
||||
nd: event.ndValue,
|
||||
isMetering: state.isMetering,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onMeasure(MeasureEvent _, Emitter emit) {
|
||||
_meteringInteractor.quickVibration();
|
||||
_communicationBloc.add(const communication_events.MeasureEvent());
|
||||
Future<void> _onStartTimer(StartTimerEvent _, Emitter emit) async {
|
||||
emit(
|
||||
LoadingState(
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
TimerResumedState(
|
||||
duration: state.duration,
|
||||
timeLeft: state.timeLeft,
|
||||
),
|
||||
);
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
add(SetTimeLeftEvent(state.timeLeft - const Duration(seconds: 1)));
|
||||
if (state.timeLeft.inMilliseconds == 0) {
|
||||
add(const StopTimerEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onSetTimeLeft(SetTimeLeftEvent event, Emitter emit) async {
|
||||
emit(
|
||||
TimerResumedState(
|
||||
duration: state.duration,
|
||||
timeLeft: event.timeLeft,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleEv100(double? ev100, {required bool isMetering}) {
|
||||
if (ev100 == null || ev100.isNaN || ev100.isInfinite) {
|
||||
add(MeasureErrorEvent(isMetering: isMetering));
|
||||
} else {
|
||||
add(MeasuredEvent(ev100, isMetering: isMetering));
|
||||
}
|
||||
}
|
||||
|
||||
void _onMeasured(MeasuredEvent event, Emitter emit) {
|
||||
Future<void> _onStopTimer(StopTimerEvent _, Emitter emit) async {
|
||||
_timer?.cancel();
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: event.ev100,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
isMetering: event.isMetering,
|
||||
TimerStoppedState(
|
||||
duration: state.duration,
|
||||
timeLeft: state.timeLeft,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onMeasureError(MeasureErrorEvent event, Emitter emit) {
|
||||
Future<void> _onResetTimer(ResetTimerEvent _, Emitter emit) async {
|
||||
_timer?.cancel();
|
||||
emit(
|
||||
MeteringDataState(
|
||||
ev100: null,
|
||||
iso: state.iso,
|
||||
nd: state.nd,
|
||||
isMetering: event.isMetering,
|
||||
TimerStoppedState(
|
||||
duration: state.duration,
|
||||
timeLeft: state.duration,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
void onVolumeKey() {
|
||||
if (_meteringInteractor.volumeAction == VolumeAction.shutter) {
|
||||
add(const MeasureEvent());
|
||||
}
|
||||
}
|
||||
|
||||
void _onSettingsOpened(SettingsOpenedEvent _, Emitter __) {
|
||||
_communicationBloc.add(const communication_events.SettingsOpenedEvent());
|
||||
}
|
||||
|
||||
void _onSettingsClosed(SettingsClosedEvent _, Emitter __) {
|
||||
_communicationBloc.add(const communication_events.SettingsClosedEvent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,21 @@
|
|||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
sealed class MeteringEvent {
|
||||
const MeteringEvent();
|
||||
sealed class TimerEvent {
|
||||
const TimerEvent();
|
||||
}
|
||||
|
||||
class EquipmentProfileChangedEvent extends MeteringEvent {
|
||||
final EquipmentProfile equipmentProfileData;
|
||||
|
||||
const EquipmentProfileChangedEvent(this.equipmentProfileData);
|
||||
class StartTimerEvent extends TimerEvent {
|
||||
const StartTimerEvent();
|
||||
}
|
||||
|
||||
class IsoChangedEvent extends MeteringEvent {
|
||||
final IsoValue isoValue;
|
||||
|
||||
const IsoChangedEvent(this.isoValue);
|
||||
class StopTimerEvent extends TimerEvent {
|
||||
const StopTimerEvent();
|
||||
}
|
||||
|
||||
class NdChangedEvent extends MeteringEvent {
|
||||
final NdValue ndValue;
|
||||
class SetTimeLeftEvent extends TimerEvent {
|
||||
final Duration timeLeft;
|
||||
|
||||
const NdChangedEvent(this.ndValue);
|
||||
const SetTimeLeftEvent(this.timeLeft);
|
||||
}
|
||||
|
||||
class MeasureEvent extends MeteringEvent {
|
||||
const MeasureEvent();
|
||||
}
|
||||
|
||||
class MeasuredEvent extends MeteringEvent {
|
||||
final double ev100;
|
||||
final bool isMetering;
|
||||
|
||||
const MeasuredEvent(this.ev100, {required this.isMetering});
|
||||
}
|
||||
|
||||
class MeasureErrorEvent extends MeteringEvent {
|
||||
final bool isMetering;
|
||||
|
||||
const MeasureErrorEvent({required this.isMetering});
|
||||
}
|
||||
|
||||
class SettingsOpenedEvent extends MeteringEvent {
|
||||
const SettingsOpenedEvent();
|
||||
}
|
||||
|
||||
class SettingsClosedEvent extends MeteringEvent {
|
||||
const SettingsClosedEvent();
|
||||
class ResetTimerEvent extends TimerEvent {
|
||||
const ResetTimerEvent();
|
||||
}
|
||||
|
|
|
@ -2,10 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||
import 'package:lightmeter/providers/services_provider.dart';
|
||||
import 'package:lightmeter/screens/metering/bloc_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/screen_metering.dart';
|
||||
import 'package:lightmeter/screens/metering/utils/notifier_volume_keys.dart';
|
||||
import 'package:lightmeter/screens/timer/bloc_timer.dart';
|
||||
import 'package:lightmeter/screens/timer/screen_timer.dart';
|
||||
|
||||
class TimerFlow extends StatelessWidget {
|
||||
|
@ -22,17 +19,11 @@ class TimerFlow extends StatelessWidget {
|
|||
ServicesProvider.of(context).lightSensorService,
|
||||
ServicesProvider.of(context).volumeEventsService,
|
||||
)..initialize(),
|
||||
child: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (_) => MeteringCommunicationBloc()),
|
||||
BlocProvider(
|
||||
create: (context) => MeteringBloc(
|
||||
MeteringInteractorProvider.of(context),
|
||||
VolumeKeysNotifier(ServicesProvider.of(context).volumeEventsService),
|
||||
context.read<MeteringCommunicationBloc>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: BlocProvider(
|
||||
create: (context) => TimerBloc(
|
||||
MeteringInteractorProvider.of(context),
|
||||
124,
|
||||
),
|
||||
child: const TimerScreen(),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,81 +1,145 @@
|
|||
import 'dart:math';
|
||||
|
||||
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/timer/bloc_timer.dart';
|
||||
import 'package:lightmeter/screens/timer/event_timer.dart';
|
||||
import 'package:lightmeter/screens/timer/state_timer.dart';
|
||||
import 'package:material_color_utilities/material_color_utilities.dart';
|
||||
|
||||
class TimerScreen extends StatelessWidget {
|
||||
class TimerScreen extends StatefulWidget {
|
||||
const TimerScreen({super.key});
|
||||
|
||||
@override
|
||||
State<TimerScreen> createState() => _TimerScreenState();
|
||||
}
|
||||
|
||||
class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin {
|
||||
late AnimationController timelineController;
|
||||
late Animation<double> timelineAnimation;
|
||||
late AnimationController startStopIconController;
|
||||
late Animation<double> startStopIconAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
timelineController = AnimationController(vsync: this, duration: Dimens.durationS);
|
||||
timelineAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(timelineController);
|
||||
|
||||
startStopIconController = AnimationController(vsync: this, duration: Dimens.durationS);
|
||||
startStopIconAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(startStopIconController);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
context.read<TimerBloc>().add(const StartTimerEvent());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timelineController.dispose();
|
||||
startStopIconController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
'Test',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: Dimens.grid24,
|
||||
return BlocListener<TimerBloc, TimerState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous is TimerStoppedState && current is TimerResumedState ||
|
||||
previous is TimerResumedState && current is TimerStoppedState,
|
||||
listener: (context, state) => _updateAnimations(state),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
'Test',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontSize: Dimens.grid24,
|
||||
),
|
||||
),
|
||||
actions: [if (Navigator.of(context).canPop()) const CloseButton()],
|
||||
),
|
||||
actions: [const CloseButton()],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox.fromSize(
|
||||
size: Size.square(MediaQuery.sizeOf(context).width - Dimens.paddingL * 4),
|
||||
child: _Timer(
|
||||
remainingSeconds: 5,
|
||||
timerLength: 124,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(Dimens.paddingL),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Spacer(),
|
||||
// SizedBox.fromSize(
|
||||
// size: Size.square(MediaQuery.sizeOf(context).width - Dimens.paddingL * 4),
|
||||
// child: BlocBuilder<TimerBloc, TimerState>(
|
||||
// builder: (context, state) {
|
||||
// return _Timer(
|
||||
// timeLeft: state.timeLeft,
|
||||
// duration: state.duration,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
BlocBuilder<TimerBloc, TimerState>(
|
||||
buildWhen: (previous, current) => previous.timeLeft != current.timeLeft,
|
||||
builder: (_, state) => _Timer(
|
||||
timeLeft: state.timeLeft,
|
||||
duration: state.duration,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
BlocBuilder<TimerBloc, TimerState>(
|
||||
builder: (_, state) => FloatingActionButton(
|
||||
onPressed: () {
|
||||
context.read<TimerBloc>().add(
|
||||
state is TimerStoppedState ? const StartTimerEvent() : const StopTimerEvent(),
|
||||
);
|
||||
},
|
||||
child: AnimatedIcon(
|
||||
icon: AnimatedIcons.play_pause,
|
||||
progress: startStopIconAnimation,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateAnimations(TimerState state) {
|
||||
switch (state) {
|
||||
case TimerResumedState():
|
||||
startStopIconController.forward();
|
||||
case TimerStoppedState():
|
||||
startStopIconController.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _Timer extends StatelessWidget {
|
||||
final int remainingSeconds;
|
||||
final int timerLength;
|
||||
final Duration timeLeft;
|
||||
final Duration duration;
|
||||
|
||||
const _Timer({
|
||||
required this.remainingSeconds,
|
||||
required this.timerLength,
|
||||
required this.timeLeft,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
painter: _TimelinePainter(
|
||||
backgroundColor: ElevationOverlay.applySurfaceTint(
|
||||
Theme.of(context).cardTheme.color!,
|
||||
Theme.of(context).cardTheme.surfaceTintColor,
|
||||
Theme.of(context).cardTheme.elevation!,
|
||||
),
|
||||
progressColor: Theme.of(context).colorScheme.primary,
|
||||
progress: remainingSeconds / timerLength,
|
||||
),
|
||||
willChange: true,
|
||||
child: Center(
|
||||
child: Text(
|
||||
parseSeconds(),
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
),
|
||||
return Text(
|
||||
parseSeconds(),
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -91,15 +155,15 @@ class _Timer extends StatelessWidget {
|
|||
}
|
||||
|
||||
final buffer = StringBuffer();
|
||||
int remainingSeconds = this.remainingSeconds;
|
||||
int remainingSeconds = timeLeft.inSeconds;
|
||||
// longer than 1 hours
|
||||
if (timerLength >= 3600) {
|
||||
if (duration.inSeconds >= 3600) {
|
||||
final hours = remainingSeconds ~/ 3600;
|
||||
buffer.writeAll([addZeroIfNeeded(hours), ':']);
|
||||
remainingSeconds -= hours * 3600;
|
||||
}
|
||||
// longer than 1 minute
|
||||
if (timerLength >= 60 || timerLength == 0) {
|
||||
if (duration.inSeconds >= 60 || duration.inSeconds == 0) {
|
||||
final minutes = remainingSeconds ~/ 60;
|
||||
buffer.writeAll([addZeroIfNeeded(minutes), ':']);
|
||||
remainingSeconds -= minutes * 60;
|
||||
|
@ -110,6 +174,33 @@ class _Timer extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _TimerTimeline extends StatelessWidget {
|
||||
final double progress;
|
||||
final Widget child;
|
||||
|
||||
const _TimerTimeline({
|
||||
required this.progress,
|
||||
required this.child,
|
||||
}) : assert(progress >= 0 && progress <= 1);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomPaint(
|
||||
painter: _TimelinePainter(
|
||||
backgroundColor: ElevationOverlay.applySurfaceTint(
|
||||
Theme.of(context).cardTheme.color!,
|
||||
Theme.of(context).cardTheme.surfaceTintColor,
|
||||
Theme.of(context).cardTheme.elevation!,
|
||||
),
|
||||
progressColor: Theme.of(context).colorScheme.primary,
|
||||
progress: progress,
|
||||
),
|
||||
willChange: true,
|
||||
child: Center(child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TimelinePainter extends CustomPainter {
|
||||
final Color progressColor;
|
||||
final Color backgroundColor;
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
||||
|
||||
@immutable
|
||||
abstract class MeteringState {
|
||||
final double? ev100;
|
||||
final IsoValue iso;
|
||||
final NdValue nd;
|
||||
final bool isMetering;
|
||||
sealed class TimerState {
|
||||
final Duration duration;
|
||||
final Duration timeLeft;
|
||||
|
||||
const MeteringState({
|
||||
this.ev100,
|
||||
required this.iso,
|
||||
required this.nd,
|
||||
required this.isMetering,
|
||||
const TimerState({
|
||||
required this.duration,
|
||||
required this.timeLeft,
|
||||
});
|
||||
}
|
||||
|
||||
class LoadingState extends MeteringState {
|
||||
const LoadingState({
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
}) : super(isMetering: true);
|
||||
}
|
||||
|
||||
class MeteringDataState extends MeteringState {
|
||||
const MeteringDataState({
|
||||
required super.ev100,
|
||||
required super.iso,
|
||||
required super.nd,
|
||||
required super.isMetering,
|
||||
class TimerStoppedState extends TimerState {
|
||||
const TimerStoppedState({
|
||||
required super.duration,
|
||||
required super.timeLeft,
|
||||
});
|
||||
}
|
||||
|
||||
class TimerResumedState extends TimerState {
|
||||
const TimerResumedState({
|
||||
required super.duration,
|
||||
required super.timeLeft,
|
||||
});
|
||||
|
||||
double? get ev => ev100 != null ? ev100! + log2(iso.value / 100) - nd.stopReduction : null;
|
||||
bool get hasError => ev == null;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue