8000 Added timer controller by shrijanRegmi · Pull Request #244 · appinioGmbH/flutter_packages · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Added timer controller #244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions packages/flutter_timer_countdown/example/lib/timer_basic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import 'package:flutter/cupertino.dart';

import 'package:example/timer_frame.dart';
import 'package:flutter_timer_countdown/flutter_timer_countdown.dart';
import 'package:flutter_timer_countdown/timer_countdown_controller.dart';

class TimerBasic extends StatelessWidget {
class TimerBasic extends StatefulWidget {
final CountDownTimerFormat format;
final bool inverted;

Expand All @@ -15,39 +16,59 @@ class TimerBasic extends StatelessWidget {
Key? key,
}) : super(key: key);

@override
State<TimerBasic> createState() => _TimerBasicState();
}

class _TimerBasicState extends State<TimerBasic> {
late TimerCountdownController _controller;

@override
void initState() {
super.initState();
_controller = TimerCountdownController(
duration: const Duration(
days: 15,
hours: 20,
minutes: 47,
seconds: 45,
),
);
_controller.start();
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return TimerCountdown(
format: format,
endTime: DateTime.now().add(
Duration(
days: 15,
hours: 20,
minutes: 47,
seconds: 45,
),
),
format: widget.format,
controller: _controller,
onEnd: () {
print("Timer finished");
},
timeTextStyle: TextStyle(
color: (inverted) ? purple : CupertinoColors.white,
color: (widget.inverted) ? purple : CupertinoColors.white,
fontWeight: FontWeight.w300,
fontSize: 40,
fontFeatures: <FontFeature>[
FontFeature.tabularFigures(),
],
),
colonsTextStyle: TextStyle(
color: (inverted) ? purple : CupertinoColors.white,
color: (widget.inverted) ? purple : CupertinoColors.white,
fontWeight: FontWeight.w300,
fontSize: 40,
fontFeatures: <FontFeature>[
FontFeature.tabularFigures(),
],
),
descriptionTextStyle: TextStyle(
color: (inverted) ? purple : CupertinoColors.white,
color: (widget.inverted) ? purple : CupertinoColors.white,
fontSize: 10,
fontFeatures: <FontFeature>[
FontFeature.tabularFigures(),
Expand Down
157 changes: 115 additions & 42 deletions packages/flutter_timer_countdown/lib/flutter_timer_countdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library flutter_timer_countdown;

import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_timer_countdown/timer_countdown_controller.dart';

enum CountDownTimerFormat {
daysHoursMinutesSeconds,
Expand All @@ -19,12 +20,14 @@ enum CountDownTimerFormat {
typedef Function(Duration remainingTime);

class TimerCountdown extends StatefulWidget {
/// The controller used to control the timer.
///
/// Eg. to start, stop, reset the timer.
final TimerCountdownController controller;

/// Format for the timer coundtown, choose between different `CountDownTimerFormat`s
final CountDownTimerFormat format;

/// Defines the time when the timer is over.
final DateTime endTime;

/// Gives you remaining time after every tick.
final OnTickCallBack? onTick;

Expand Down Expand Up @@ -59,7 +62,7 @@ class TimerCountdown extends StatefulWidget {
final double spacerWidth;

TimerCountdown({
required this.endTime,
required this.controller,
this.format = CountDownTimerFormat.daysHoursMinutesSeconds,
this.enableDescriptions = true,
this.onEnd,
Expand All @@ -84,53 +87,71 @@ class _TimerCountdownState extends State<TimerCountdown> {
late String countdownHours;
late String countdownMinutes;
late String countdownSeconds;
late Duration difference;

late Duration durationRemaining;

@override
void initState() {
_startTimer();
super.initState();

// initialize the UI values with the total duration
durationRemaining = widget.controller.duration;
countdownDays = _durationToStringDays(widget.controller.duration);
countdownHours = _durationToStringHours(widget.controller.duration);
countdownMinutes = _durationToStringMinutes(widget.controller.duration);
countdownSeconds = _durationToStringSeconds(widget.controller.duration);

if (widget.controller.value == TimerState.start) _startTimer();
widget.controller.addListener(() {
if (widget.controller.value == TimerState.start) {
_startTimer();
} else if (widget.controller.value == TimerState.stop) {
_stopTimer();
} else if (widget.controller.value == TimerState.pause) {
_pauseTimer();
} else if (widget.controller.value == TimerState.resume) {
_resumeTimer();
}
});
}

@override
void dispose() {
if (timer != null) {
timer!.cancel();
}
_stopTimer();
super.dispose();
}

/// Calculate the time difference between now end the given [endTime] and initialize all UI timer values.
///
/// Then create a periodic `Timer` which updates all fields every second depending on the time difference which is getting smaller.
/// Creates a periodic `Timer` which updates all fields every second depending on the time difference which is getting smaller.
/// When this difference reached `Duration.zero` the `Timer` is stopped and the [onEnd] callback is invoked.
void _startTimer() {
if (widget.endTime.isBefore(DateTime.now())) {
difference = Duration.zero;
} else {
difference = widget.endTime.difference(DateTime.now());
}

countdownDays = _durationToStringDays(difference);
countdownHours = _durationToStringHours(difference);
countdownMinutes = _durationToStringMinutes(difference);
countdownSeconds = _durationToStringSeconds(difference);
///
/// If resume is `true`, the timer will continue from the [durationRemaining] without resetting it to total duration.
void _startTimer({
final bool resume = false,
}) {
final totalDuration = widget.controller.duration;

if (difference == Duration.zero) {
if (totalDuration == Duration.zero) {
if (widget.onEnd != null) {
widget.onEnd!();
}
} else {
if (!resume) {
// only resetting the remaining time to totalDuration if the timer is not resumed
durationRemaining = totalDuration;
}

timer = Timer.periodic(Duration(seconds: 1), (timer) {
difference = widget.endTime.difference(DateTime.now());
widget.onTick?.call(difference);
durationRemaining = durationRemaining - Duration(seconds: 1);
widget.onTick?.call(durationRemaining);

setState(() {
countdownDays = _durationToStringDays(difference);
countdownHours = _durationToStringHours(difference);
countdownMinutes = _durationToStringMinutes(difference);
countdownSeconds = _durationToStringSeconds(difference);
countdownDays = _durationToStringDays(durationRemaining);
countdownHours = _durationToStringHours(durationRemaining);
countdownMinutes = _durationToStringMinutes(durationRemaining);
countdownSeconds = _durationToStringSeconds(durationRemaining);
});
if (difference <= Duration.zero) {

if (durationRemaining <= Duration.zero) {
timer.cancel();
if (widget.onEnd != null) {
widget.onEnd!();
Expand All @@ -140,6 +161,30 @@ class _TimerCountdownState extends State<TimerCountdown> {
}
}

/// Stops the timer and resets the [durationRemaining] to the total duration.
void _stopTimer() {
timer?.cancel();
timer = null;

durationRemaining = widget.controller.duration;
}

/// Pauses the timer.
///
/// Pause is just a stop function but without resetting the remaining time to the total duration.
/// i.e the [durationRemaining] is preserved.
void _pauseTimer() {
timer?.cancel();
timer = null;
}

/// Resumes the timer.
///
/// Resume is just a start function but with out resetting [durationRemaining] to the total duration.
void _resumeTimer() {
_startTimer(resume: true);
}

@override
Widget build(BuildContext context) {
return _countDownTimerFormat();
Expand Down Expand Up @@ -273,13 +318,13 @@ class _TimerCountdownState extends State<TimerCountdown> {
/// When the selected [CountDownTimerFormat] is leaving out the last unit, this function puts the UI value of the unit before up by one.
///
/// This is done to show the currently running time unit.
String _twoDigits(int n, String unitType) {
String _twoDigits(final Duration duration, int n, String unitType) {
switch (unitType) {
case "minutes":
if (widget.format == CountDownTimerFormat.daysHoursMinutes ||
widget.format == CountDownTimerFormat.hoursMinutes ||
widget.format == CountDownTimerFormat.minutesOnly) {
if (difference > Duration.zero) {
if (duration > Duration.zero) {
n++;
}
}
Expand All @@ -288,15 +333,15 @@ class _TimerCountdownState extends State<TimerCountdown> {
case "hours":
if (widget.format == CountDownTimerFormat.daysHours ||
widget.format == CountDownTimerFormat.hoursOnly) {
if (difference > Duration.zero) {
if (duration > Duration.zero) {
n++;
}
}
if (n >= 10) return "$n";
return "0$n";
case "days":
if (widget.format == CountDownTimerFormat.daysOnly) {
if (difference > Duration.zero) {
if (duration > Duration.zero) {
n++;
}
}
Expand All @@ -310,34 +355,62 @@ class _TimerCountdownState extends State<TimerCountdown> {

/// Convert [Duration] in days to String for UI.
String _durationToStringDays(Duration duration) {
return _twoDigits(duration.inDays, "days").toString();
return _twoDigits(
duration,
duration.inDays,
"days",
).toString();
}

/// Convert [Duration] in hours to String for UI.
String _durationToStringHours(Duration duration) {
if (widget.format == CountDownTimerFormat.hoursMinutesSeconds ||
widget.format == CountDownTimerFormat.hoursMinutes ||
widget.format == CountDownTimerFormat.hoursOnly) {
return _twoDigits(duration.inHours, "hours");
return _twoDigits(
duration,
duration.inHours,
"hours",
);
} else
return _twoDigits(duration.inHours.remainder(24), "hours").toString();
return _twoDigits(
duration,
duration.inHours.remainder(24),
"hours",
).toString();
}

/// Convert [Duration] in minutes to String for UI.
String _durationToStringMinutes(Duration duration) {
if (widget.format == CountDownTimerFormat.minutesSeconds ||
widget.format == CountDownTimerFormat.minutesOnly) {
return _twoDigits(duration.inMinutes, "minutes");
return _twoDigits(
duration,
duration.inMinutes,
"minutes",
);
} else
return _twoDigits(duration.inMinutes.remainder(60), "minutes");
return _twoDigits(
duration,
duration.inMinutes.remainder(60),
"minutes",
);
}

/// Convert [Duration] in seconds to String for UI.
String _durationToStringSeconds(Duration duration) {
if (widget.format == CountDownTimerFormat.secondsOnly) {
return _twoDigits(duration.inSeconds, "seconds");
return _twoDigits(
duration,
duration.inSeconds,
"seconds",
);
} else
return _twoDigits(duration.inSeconds.remainder(60), "seconds");
return _twoDigits(
duration,
duration.inSeconds.remainder(60),
"seconds",
);
}

/// Switches the UI to be displayed based on [CountDownTimerFormat].
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';

enum TimerState {
idle,
start,
pause,
resume,
stop,
}

class TimerCountdownController extends ValueNotifier<TimerState> {
/// Handle the timer countdown functionality.
/// Eg. start, pause, resume, stop, reset.
///
/// The [duration] is the total time of the countdown.
TimerCountdownController({
required final Duration duration,
}) : _duration = duration,
super(TimerState.idle);

final Duration _duration;
Duration get duration => _duration;

/// Starts the timer.
///
/// Restarts the timer if it was already started.
void start() {
value = TimerState.start;
}

/// Stops the timer.
void stop() {
value = TimerState.stop;
}

/// Pauses the timer.
void pause() {
value = TimerState.pause;
}

/// Resumes the timer.
void resume() {
value = TimerState.resume;
}
}
0