add circular progress indicator
parent
c1cf3cce30
commit
9eff2ad177
|
@ -4,169 +4,11 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:smoke_cess_app/providers/timer_provider.dart';
|
import 'package:smoke_cess_app/providers/timer_provider.dart';
|
||||||
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/popup_for_start_and_stop.dart';
|
import 'package:smoke_cess_app/widgets/mute_button.dart';
|
||||||
import 'package:smoke_cess_app/widgets/timer_widget.dart';
|
|
||||||
import 'package:smoke_cess_app/widgets/workout_timer_widget.dart';
|
import 'package:smoke_cess_app/widgets/workout_timer_widget.dart';
|
||||||
|
|
||||||
import '../providers/input_provider.dart';
|
class IntervalTimerPage extends StatelessWidget {
|
||||||
|
const IntervalTimerPage({super.key});
|
||||||
class IntervalTimerPage extends StatefulWidget {
|
|
||||||
const IntervalTimerPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_IntervalTimerPageState createState() => _IntervalTimerPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _IntervalTimerPageState extends State<IntervalTimerPage> {
|
|
||||||
final Duration _warmupDuration = const Duration(seconds: 5);
|
|
||||||
final Duration _cooldownDuration = const Duration(seconds: 5);
|
|
||||||
final Duration _highIntensityDuration = const Duration(seconds: 4);
|
|
||||||
final Duration _lowIntensityDuration = const Duration(seconds: 3);
|
|
||||||
late Duration _totalDuration = const Duration(minutes: 35);
|
|
||||||
AudioPlayer warmUpPlayer = AudioPlayer();
|
|
||||||
AudioPlayer workoutPlayer = AudioPlayer();
|
|
||||||
AudioPlayer coolDownPlayer = AudioPlayer();
|
|
||||||
final AudioCache _audioCache = AudioCache();
|
|
||||||
final int _numHighIntensityBlocks = 4;
|
|
||||||
final int _numLowIntensityBlocks = 3;
|
|
||||||
|
|
||||||
Timer? _timer;
|
|
||||||
int _currentBlock = 0;
|
|
||||||
Duration _currentDuration = const Duration();
|
|
||||||
bool _isPaused = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_currentDuration = _warmupDuration;
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startTimer() async {
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return ChangeNotifierProvider(
|
|
||||||
create: (context) => InputProvider(),
|
|
||||||
child: const TimerStartStopPopup(
|
|
||||||
title: 'Motivation vor dem Training',
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
_isPaused = false;
|
|
||||||
Source source = AssetSource('go.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) => _tick());
|
|
||||||
Future.delayed(const Duration(seconds: 1)).then((value) {
|
|
||||||
_playWarmUpMusic();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _resetTimer() {
|
|
||||||
() async {
|
|
||||||
await coolDownPlayer.stop();
|
|
||||||
await warmUpPlayer.stop();
|
|
||||||
await workoutPlayer.stop();
|
|
||||||
}();
|
|
||||||
_isPaused = true;
|
|
||||||
_timer?.cancel();
|
|
||||||
_currentBlock = 0;
|
|
||||||
_currentDuration = _warmupDuration;
|
|
||||||
_totalDuration = const Duration(minutes: 35);
|
|
||||||
setState(() {});
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const TimerStartStopPopup(
|
|
||||||
title: 'Motivation nach dem Training',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _playWarmUpMusic() async {
|
|
||||||
Source source = AssetSource('warmUp.mp3');
|
|
||||||
await warmUpPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await warmUpPlayer.play(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _playWorkoutMusic() async {
|
|
||||||
await warmUpPlayer.stop();
|
|
||||||
Future.delayed(const Duration(microseconds: 600)).then((value) async {
|
|
||||||
Source source = AssetSource('workout.mp3');
|
|
||||||
await workoutPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await workoutPlayer.play(source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _intervalChange() async {
|
|
||||||
Source source = AssetSource('beep.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _tick() {
|
|
||||||
setState(() {
|
|
||||||
_currentDuration = Duration(
|
|
||||||
seconds: _currentDuration.inSeconds - 1,
|
|
||||||
);
|
|
||||||
_totalDuration = Duration(
|
|
||||||
seconds: _totalDuration.inSeconds - 1,
|
|
||||||
);
|
|
||||||
if (_currentDuration.inSeconds < 1) {
|
|
||||||
if (_currentBlock < _numHighIntensityBlocks + _numLowIntensityBlocks) {
|
|
||||||
_intervalChange();
|
|
||||||
if (_currentBlock == 0) {
|
|
||||||
_playWorkoutMusic();
|
|
||||||
}
|
|
||||||
_currentBlock++;
|
|
||||||
|
|
||||||
if (_currentBlock % 2 == 1) {
|
|
||||||
_currentDuration = _highIntensityDuration;
|
|
||||||
} else {
|
|
||||||
_currentDuration = _lowIntensityDuration;
|
|
||||||
}
|
|
||||||
} else if (_currentBlock < _numHighIntensityBlocks * 2) {
|
|
||||||
_intervalChange();
|
|
||||||
_currentBlock++;
|
|
||||||
_currentDuration = _cooldownDuration;
|
|
||||||
() async {
|
|
||||||
await workoutPlayer.stop();
|
|
||||||
Source source = AssetSource('cool_down.mp3');
|
|
||||||
await coolDownPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await coolDownPlayer.play(source);
|
|
||||||
}();
|
|
||||||
} else {
|
|
||||||
() async {
|
|
||||||
Future.delayed(const Duration(microseconds: 900))
|
|
||||||
.then((value) async {
|
|
||||||
Source source = AssetSource('finish.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
});
|
|
||||||
}();
|
|
||||||
_resetTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatDuration(Duration duration) {
|
|
||||||
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
|
||||||
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
|
||||||
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
|
||||||
return '$minutes:$seconds';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatTotalDuration(Duration duration) {
|
|
||||||
final minutes = duration.inMinutes;
|
|
||||||
final seconds = duration.inSeconds.remainder(60);
|
|
||||||
return _formatDuration(Duration(minutes: minutes, seconds: seconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -177,60 +19,16 @@ class _IntervalTimerPageState extends State<IntervalTimerPage> {
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (context) => WorkoutProvider(timerProvider))
|
create: (context) => WorkoutProvider(timerProvider))
|
||||||
],
|
],
|
||||||
child: WorkoutTimerWidget(),
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: const [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: MuteButton(),
|
||||||
|
),
|
||||||
|
WorkoutTimerWidget()
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
ChangeNotifierProvider(
|
|
||||||
create: (context) => TimerProvider(),
|
|
||||||
child: TimerWidget(
|
|
||||||
duration: Duration(seconds: 5),
|
|
||||||
));
|
|
||||||
return Center(
|
|
||||||
child: ChangeNotifierProvider(
|
|
||||||
create: (context) => InputProvider(),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
_currentBlock == 0
|
|
||||||
? 'Warm-up'
|
|
||||||
: _currentBlock % 2 == 1
|
|
||||||
? 'High Intensity'
|
|
||||||
: _currentBlock < _numHighIntensityBlocks * 2
|
|
||||||
? 'Low Intensity'
|
|
||||||
: 'Cool-down',
|
|
||||||
style: const TextStyle(fontSize: 32.0),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Text(
|
|
||||||
_formatDuration(_currentDuration),
|
|
||||||
style: const TextStyle(fontSize: 80.0),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32.0),
|
|
||||||
Text(
|
|
||||||
'Total: ${_formatTotalDuration(_totalDuration)}',
|
|
||||||
style: const TextStyle(fontSize: 24.0),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32.0),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(_isPaused
|
|
||||||
? Icons.play_arrow_rounded
|
|
||||||
: Icons.stop_rounded),
|
|
||||||
iconSize: 48.0,
|
|
||||||
onPressed: () {
|
|
||||||
if (_isPaused) {
|
|
||||||
_startTimer();
|
|
||||||
} else {
|
|
||||||
_resetTimer();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,19 @@ class WorkoutProvider extends ChangeNotifier {
|
||||||
final Source _finishedSoundSource = AssetSource('finish.mp3');
|
final Source _finishedSoundSource = AssetSource('finish.mp3');
|
||||||
final Source _beepSoundSource = AssetSource('beep.mp3');
|
final Source _beepSoundSource = AssetSource('beep.mp3');
|
||||||
bool isWorkoutStarted = false;
|
bool isWorkoutStarted = false;
|
||||||
|
bool isMuted = false;
|
||||||
|
|
||||||
|
void mutePlayer() {
|
||||||
|
isMuted = true;
|
||||||
|
_audioPlayer.setVolume(0);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unMutePlayer() {
|
||||||
|
isMuted = false;
|
||||||
|
_audioPlayer.setVolume(1);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
WorkoutProvider(this.timerProvider);
|
WorkoutProvider(this.timerProvider);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
||||||
|
|
||||||
|
class MuteButton extends StatelessWidget {
|
||||||
|
const MuteButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
onPressed: workoutProvider.isMuted
|
||||||
|
? workoutProvider.unMutePlayer
|
||||||
|
: workoutProvider.mutePlayer,
|
||||||
|
icon: Icon(workoutProvider.isMuted
|
||||||
|
? Icons.volume_off_outlined
|
||||||
|
: Icons.volume_up_outlined),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,9 @@ class WorkoutTimerWidget extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(workoutProvider.currentPhase),
|
Text(workoutProvider.currentPhase),
|
||||||
|
CircularProgressIndicator(
|
||||||
|
value: timerProvider.elapsedSeconds /
|
||||||
|
workoutProvider.currentPhaseDuration.inSeconds),
|
||||||
TimerWidget(duration: workoutProvider.currentPhaseDuration),
|
TimerWidget(duration: workoutProvider.currentPhaseDuration),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: !workoutProvider.isWorkoutStarted
|
onPressed: !workoutProvider.isWorkoutStarted
|
||||||
|
|
Loading…
Reference in New Issue