mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
synchronized timeline with actual timer
This commit is contained in:
parent
f77ef321d0
commit
7eb0ecde7f
4 changed files with 36 additions and 94 deletions
|
@ -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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue