synchronized timeline with actual timer

This commit is contained in:
Vadim 2024-05-03 11:16:29 +02:00
parent f77ef321d0
commit 7eb0ecde7f
4 changed files with 36 additions and 94 deletions

View file

@ -5,75 +5,30 @@ import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/timer/event_timer.dart'; import 'package:lightmeter/screens/timer/event_timer.dart';
import 'package:lightmeter/screens/timer/state_timer.dart'; import 'package:lightmeter/screens/timer/state_timer.dart';
const _kTimerStep = Duration(milliseconds: 1);
class TimerBloc extends Bloc<TimerEvent, TimerState> { class TimerBloc extends Bloc<TimerEvent, TimerState> {
final MeteringInteractor _meteringInteractor; final MeteringInteractor _meteringInteractor;
late Timer? _timer;
final Duration duration; final Duration duration;
TimerBloc(this._meteringInteractor, this.duration) TimerBloc(this._meteringInteractor, this.duration) : super(const TimerStoppedState()) {
: super(
TimerStoppedState(
duration: duration,
timeLeft: duration,
),
) {
on<StartTimerEvent>(_onStartTimer); on<StartTimerEvent>(_onStartTimer);
on<SetTimeLeftEvent>(_onSetTimeLeft); on<SetTimeLeftEvent>(_onSetTimeLeft);
on<StopTimerEvent>(_onStopTimer); on<StopTimerEvent>(_onStopTimer);
on<ResetTimerEvent>(_onResetTimer); on<ResetTimerEvent>(_onResetTimer);
} }
@override
Future<void> close() async {
_timer?.cancel();
return super.close();
}
Future<void> _onStartTimer(StartTimerEvent _, Emitter emit) async { Future<void> _onStartTimer(StartTimerEvent _, Emitter emit) async {
emit( emit(const TimerResumedState());
TimerResumedState(
duration: state.duration,
timeLeft: state.timeLeft,
),
);
_timer = Timer.periodic(_kTimerStep, (_) {
if (state.timeLeft.inMilliseconds == 0) {
add(const StopTimerEvent());
} else {
add(SetTimeLeftEvent(state.timeLeft - _kTimerStep));
}
});
} }
Future<void> _onSetTimeLeft(SetTimeLeftEvent event, Emitter emit) async { Future<void> _onSetTimeLeft(SetTimeLeftEvent event, Emitter emit) async {
emit( emit(const TimerResumedState());
TimerResumedState(
duration: state.duration,
timeLeft: event.timeLeft,
),
);
} }
Future<void> _onStopTimer(StopTimerEvent _, Emitter emit) async { Future<void> _onStopTimer(StopTimerEvent _, Emitter emit) async {
_timer?.cancel(); emit(const TimerStoppedState());
emit(
TimerStoppedState(
duration: state.duration,
timeLeft: state.timeLeft,
),
);
} }
Future<void> _onResetTimer(ResetTimerEvent _, Emitter emit) async { Future<void> _onResetTimer(ResetTimerEvent _, Emitter emit) async {
_timer?.cancel(); emit(const TimerResetState());
emit(
TimerResetState(
duration: state.duration,
timeLeft: state.duration,
),
);
} }
} }

View file

@ -42,7 +42,10 @@ class TimerFlow extends StatelessWidget {
MeteringInteractorProvider.of(context), MeteringInteractorProvider.of(context),
_duration, _duration,
), ),
child: TimerScreen(exposurePair: exposurePair), child: TimerScreen(
exposurePair: exposurePair,
duration: _duration,
),
), ),
), ),
); );

View file

@ -1,21 +1,22 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/exposure_pair.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/timer/bloc_timer.dart'; import 'package:lightmeter/screens/timer/bloc_timer.dart';
import 'package:lightmeter/screens/timer/components/text/widget_text_timer.dart'; import 'package:lightmeter/screens/timer/components/text/widget_text_timer.dart';
import 'package:lightmeter/screens/timer/components/timeline/widget_timeline_timer.dart'; import 'package:lightmeter/screens/timer/components/timeline/widget_timeline_timer.dart';
import 'package:lightmeter/screens/timer/event_timer.dart'; import 'package:lightmeter/screens/timer/event_timer.dart';
import 'package:lightmeter/screens/timer/state_timer.dart'; import 'package:lightmeter/screens/timer/state_timer.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
class TimerScreen extends StatefulWidget { class TimerScreen extends StatefulWidget {
final ExposurePair exposurePair; final ExposurePair exposurePair;
final Duration duration;
const TimerScreen({required this.exposurePair, super.key}); const TimerScreen({
required this.exposurePair,
required this.duration,
super.key,
});
@override @override
State<TimerScreen> createState() => _TimerScreenState(); State<TimerScreen> createState() => _TimerScreenState();
@ -31,11 +32,13 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
void initState() { void initState() {
super.initState(); super.initState();
timelineController = AnimationController( timelineController = AnimationController(vsync: this, duration: widget.duration);
vsync: this,
duration: Duration(seconds: widget.exposurePair.shutterSpeed.value.toInt()),
);
timelineAnimation = Tween<double>(begin: 1, end: 0).animate(timelineController); timelineAnimation = Tween<double>(begin: 1, end: 0).animate(timelineController);
timelineController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
context.read<TimerBloc>().add(const StopTimerEvent());
}
});
startStopIconController = AnimationController(vsync: this, duration: Dimens.durationS); startStopIconController = AnimationController(vsync: this, duration: Dimens.durationS);
startStopIconAnimation = Tween<double>(begin: 0, end: 1).animate(startStopIconController); startStopIconAnimation = Tween<double>(begin: 0, end: 1).animate(startStopIconController);
@ -56,7 +59,6 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// TODO (@vodemn): split build in timer/components folder
return BlocListener<TimerBloc, TimerState>( return BlocListener<TimerBloc, TimerState>(
listenWhen: (previous, current) => previous.runtimeType != current.runtimeType, listenWhen: (previous, current) => previous.runtimeType != current.runtimeType,
listener: (context, state) => _updateAnimations(state), listener: (context, state) => _updateAnimations(state),
@ -88,13 +90,9 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
valueListenable: timelineAnimation, valueListenable: timelineAnimation,
builder: (_, value, child) => TimerTimeline( builder: (_, value, child) => TimerTimeline(
progress: value, progress: value,
child: child!, child: TimerText(
), timeLeft: Duration(milliseconds: (widget.duration.inMilliseconds * value).toInt()),
child: BlocBuilder<TimerBloc, TimerState>( duration: widget.duration,
buildWhen: (previous, current) => previous.timeLeft != current.timeLeft,
builder: (_, state) => TimerText(
timeLeft: state.timeLeft,
duration: state.duration,
), ),
), ),
), ),
@ -117,13 +115,14 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
child: BlocBuilder<TimerBloc, TimerState>( child: BlocBuilder<TimerBloc, TimerState>(
builder: (_, state) => FloatingActionButton( builder: (_, state) => FloatingActionButton(
shape: state is TimerResumedState ? null : const CircleBorder(), shape: state is TimerResumedState ? null : const CircleBorder(),
onPressed: state.timeLeft.inSeconds == 0 onPressed: () {
? null if (timelineAnimation.value == 0) {
: () { return;
final event = }
state is TimerStoppedState ? const StartTimerEvent() : const StopTimerEvent(); final event =
context.read<TimerBloc>().add(event); state is TimerStoppedState ? const StartTimerEvent() : const StopTimerEvent();
}, context.read<TimerBloc>().add(event);
},
child: AnimatedIcon( child: AnimatedIcon(
icon: AnimatedIcons.play_pause, icon: AnimatedIcons.play_pause,
progress: startStopIconAnimation, progress: startStopIconAnimation,

View file

@ -2,32 +2,17 @@ import 'package:flutter/material.dart';
@immutable @immutable
sealed class TimerState { sealed class TimerState {
final Duration duration; const TimerState();
final Duration timeLeft;
const TimerState({
required this.duration,
required this.timeLeft,
});
} }
class TimerStoppedState extends TimerState { class TimerStoppedState extends TimerState {
const TimerStoppedState({ const TimerStoppedState();
required super.duration,
required super.timeLeft,
});
} }
class TimerResumedState extends TimerState { class TimerResumedState extends TimerState {
const TimerResumedState({ const TimerResumedState();
required super.duration,
required super.timeLeft,
});
} }
class TimerResetState extends TimerStoppedState { class TimerResetState extends TimerStoppedState {
const TimerResetState({ const TimerResetState();
required super.duration,
required super.timeLeft,
});
} }