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/state_timer.dart';
const _kTimerStep = Duration(milliseconds: 1);
class TimerBloc extends Bloc<TimerEvent, TimerState> {
final MeteringInteractor _meteringInteractor;
late Timer? _timer;
final Duration duration;
TimerBloc(this._meteringInteractor, this.duration)
: super(
TimerStoppedState(
duration: duration,
timeLeft: duration,
),
) {
TimerBloc(this._meteringInteractor, this.duration) : super(const TimerStoppedState()) {
on<StartTimerEvent>(_onStartTimer);
on<SetTimeLeftEvent>(_onSetTimeLeft);
on<StopTimerEvent>(_onStopTimer);
on<ResetTimerEvent>(_onResetTimer);
}
@override
Future<void> close() async {
_timer?.cancel();
return super.close();
}
Future<void> _onStartTimer(StartTimerEvent _, Emitter emit) async {
emit(
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));
}
});
emit(const TimerResumedState());
}
Future<void> _onSetTimeLeft(SetTimeLeftEvent event, Emitter emit) async {
emit(
TimerResumedState(
duration: state.duration,
timeLeft: event.timeLeft,
),
);
emit(const TimerResumedState());
}
Future<void> _onStopTimer(StopTimerEvent _, Emitter emit) async {
_timer?.cancel();
emit(
TimerStoppedState(
duration: state.duration,
timeLeft: state.timeLeft,
),
);
emit(const TimerStoppedState());
}
Future<void> _onResetTimer(ResetTimerEvent _, Emitter emit) async {
_timer?.cancel();
emit(
TimerResetState(
duration: state.duration,
timeLeft: state.duration,
),
);
emit(const TimerResetState());
}
}

View file

@ -42,7 +42,10 @@ class TimerFlow extends StatelessWidget {
MeteringInteractorProvider.of(context),
_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_bloc/flutter_bloc.dart';
import 'package:lightmeter/data/models/exposure_pair.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/components/text/widget_text_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/state_timer.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
class TimerScreen extends StatefulWidget {
final ExposurePair exposurePair;
final Duration duration;
const TimerScreen({required this.exposurePair, super.key});
const TimerScreen({
required this.exposurePair,
required this.duration,
super.key,
});
@override
State<TimerScreen> createState() => _TimerScreenState();
@ -31,11 +32,13 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
void initState() {
super.initState();
timelineController = AnimationController(
vsync: this,
duration: Duration(seconds: widget.exposurePair.shutterSpeed.value.toInt()),
);
timelineController = AnimationController(vsync: this, duration: widget.duration);
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);
startStopIconAnimation = Tween<double>(begin: 0, end: 1).animate(startStopIconController);
@ -56,7 +59,6 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
@override
Widget build(BuildContext context) {
// TODO (@vodemn): split build in timer/components folder
return BlocListener<TimerBloc, TimerState>(
listenWhen: (previous, current) => previous.runtimeType != current.runtimeType,
listener: (context, state) => _updateAnimations(state),
@ -88,13 +90,9 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
valueListenable: timelineAnimation,
builder: (_, value, child) => TimerTimeline(
progress: value,
child: child!,
),
child: BlocBuilder<TimerBloc, TimerState>(
buildWhen: (previous, current) => previous.timeLeft != current.timeLeft,
builder: (_, state) => TimerText(
timeLeft: state.timeLeft,
duration: state.duration,
child: TimerText(
timeLeft: Duration(milliseconds: (widget.duration.inMilliseconds * value).toInt()),
duration: widget.duration,
),
),
),
@ -117,9 +115,10 @@ class _TimerScreenState extends State<TimerScreen> with TickerProviderStateMixin
child: BlocBuilder<TimerBloc, TimerState>(
builder: (_, state) => FloatingActionButton(
shape: state is TimerResumedState ? null : const CircleBorder(),
onPressed: state.timeLeft.inSeconds == 0
? null
: () {
onPressed: () {
if (timelineAnimation.value == 0) {
return;
}
final event =
state is TimerStoppedState ? const StartTimerEvent() : const StopTimerEvent();
context.read<TimerBloc>().add(event);

View file

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