Flutter Countdown Timer

如何将传递的值放入构造中,使计时器舍入到第一个小数并显示在 RaisedButton 的子文本中?我试过了,但运气不好。我设法使工作的回调函数与一个简单的定时器,但没有定期和没有实时更新的价值在文本..。

import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:async';


class TimerButton extends StatefulWidget {
final Duration timerTastoPremuto;




TimerButton(this.timerTastoPremuto);


@override
_TimerButtonState createState() => _TimerButtonState();
}


class _TimerButtonState extends State<TimerButton> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(5.0),
height: 135.0,
width: 135.0,
child: new RaisedButton(
elevation: 100.0,
color: Colors.white.withOpacity(.8),
highlightElevation: 0.0,
onPressed: () {
int _start = widget.timerTastoPremuto.inMilliseconds;


const oneDecimal = const Duration(milliseconds: 100);
Timer _timer = new Timer.periodic(
oneDecimal,
(Timer timer) =>
setState(() {
if (_start < 100) {
_timer.cancel();
} else {
_start = _start - 100;
}
}));


},
splashColor: Colors.red,
highlightColor: Colors.red,
//shape: RoundedRectangleBorder e tutto il resto uguale
shape: BeveledRectangleBorder(
side: BorderSide(color: Colors.black, width: 2.5),
borderRadius: new BorderRadius.circular(15.0)),
child: new Text(
"$_start",
style: new TextStyle(fontFamily: "Minim", fontSize: 50.0),
),
),
);
}
}
269300 次浏览

下面是一个使用 定时器,周期性的的例子:

倒计时开始从 100按钮点击:

import 'dart:async';


[...]


Timer _timer;
int _start = 10;


void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) {
if (_start == 0) {
setState(() {
timer.cancel();
});
} else {
setState(() {
_start--;
});
}
},
);
}


@override
void dispose() {
_timer.cancel();
super.dispose();
}


Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("Timer test")),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
startTimer();
},
child: Text("start"),
),
Text("$_start")
],
),
);
}

结果:

Flutter countdown timer example

你也可以使用 颤动,异步库中的 倒数计时器类,用法更简单:

import 'package:quiver/async.dart';


[...]


int _start = 10;
int _current = 10;


void startTimer() {
CountdownTimer countDownTimer = new CountdownTimer(
new Duration(seconds: _start),
new Duration(seconds: 1),
);


var sub = countDownTimer.listen(null);
sub.onData((duration) {
setState(() { _current = _start - duration.elapsed.inSeconds; });
});


sub.onDone(() {
print("Done");
sub.cancel();
});
}


Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("Timer test")),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
startTimer();
},
child: Text("start"),
),
Text("$_current")
],
),
);
}

编辑 : 对于关于按钮行为的评论中的问题,请单击

使用上面的代码,使用 Timer.periodic,一个新的计时器将确实启动每个按钮点击,所有这些计时器将更新相同的 _start变量,导致一个更快的减少计数器。

There are multiple solutions to change this behavior, depending on what you want to achieve :

  • 禁用按钮一旦点击,以便用户不能再打扰倒计时(也许启用它回一旦计时器被取消)
  • wrap the Timer.periodic creation with a non null condition so that clicking the button multiple times has no effect
if (_timer != null) {
_timer = new Timer.periodic(...);
}
  • 取消计时器和重置倒计时,如果你想重新启动计时器每次点击:
if (_timer != null) {
_timer.cancel();
_start = 10;
}
_timer = new Timer.periodic(...);
  • 如果你想让按钮看起来像播放/暂停按钮:
if (_timer != null) {
_timer.cancel();
_timer = null;
} else {
_timer = new Timer.periodic(...);
}

您还可以使用这个官方的 async包,它提供了一个从 Timer扩展的 RestartableTimer类,并添加了 reset方法。

因此,只需在每次按钮点击时调用 _timer.reset();

最后,Codepen 现在支持 Flutter!因此,这里有一个活生生的例子,以便每个人都可以玩它: https://codepen.io/Yann39/pen/oNjrVOb

我已经创建了一个通用的定时器小部件,可以用来显示任何类型的定时器及其灵活性。

此 Widget 采用以下属性

  1. 秒剩余 : 计时器需要以秒为单位运行的持续时间
  2. When TimeExires : 如果计时器完成,需要执行什么操作
  3. CountDownStyle : 任何你想给计时器的样式
  4. countDownFormatter: the way user wants to display the count down timer e.g 字符串类似于 01 hours: 20 minutes: 45 seconds

you can provide a default formatter ( formatHHMMSS ) in case you don't want to supply it from every place.

//为此提供实现-formatHHMMSS(duration.inSeconds);或使用我提供的以下一个。

import 'package:flutter/material.dart';
class CountDownTimer extends StatefulWidget {
const CountDownTimer({
Key key,
int secondsRemaining,
this.countDownTimerStyle,
this.whenTimeExpires,
this.countDownFormatter,
})  : secondsRemaining = secondsRemaining,
super(key: key);


final int secondsRemaining;
final Function whenTimeExpires;
final Function countDownFormatter;
final TextStyle countDownTimerStyle;


State createState() => new _CountDownTimerState();
}


class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
AnimationController _controller;
Duration duration;


String get timerDisplayString {
Duration duration = _controller.duration * _controller.value;
return widget.countDownFormatter != null
? widget.countDownFormatter(duration.inSeconds)
: formatHHMMSS(duration.inSeconds);
// In case user doesn't provide formatter use the default one
// for that create a method which will be called formatHHMMSS or whatever you like
}


@override
void initState() {
super.initState();
duration = new Duration(seconds: widget.secondsRemaining);
_controller = new AnimationController(
vsync: this,
duration: duration,
);
_controller.reverse(from: widget.secondsRemaining.toDouble());
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed || status == AnimationStatus.dismissed) {
widget.whenTimeExpires();
}
});
}


@override
void didUpdateWidget(CountDownTimer oldWidget) {
if (widget.secondsRemaining != oldWidget.secondsRemaining) {
setState(() {
duration = new Duration(seconds: widget.secondsRemaining);
_controller.dispose();
_controller = new AnimationController(
vsync: this,
duration: duration,
);
_controller.reverse(from: widget.secondsRemaining.toDouble());
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
widget.whenTimeExpires();
} else if (status == AnimationStatus.dismissed) {
print("Animation Complete");
}
});
});
}
}


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


@override
Widget build(BuildContext context) {
return new Center(
child: AnimatedBuilder(
animation: _controller,
builder: (_, Widget child) {
return Text(
timerDisplayString,
style: widget.countDownTimerStyle,
);
}));
}
}

用法:

 Container(
width: 60.0,
padding: EdgeInsets.only(top: 3.0, right: 4.0),
child: CountDownTimer(
secondsRemaining: 30,
whenTimeExpires: () {
setState(() {
hasTimerStopped = true;
});
},
countDownTimerStyle: TextStyle(
color: Color(0XFFf5a623),
fontSize: 17.0,
height: 1.2,
),
),
)

formatHHMMSS为例:

String formatHHMMSS(int seconds) {
int hours = (seconds / 3600).truncate();
seconds = (seconds % 3600).truncate();
int minutes = (seconds / 60).truncate();


String hoursStr = (hours).toString().padLeft(2, '0');
String minutesStr = (minutes).toString().padLeft(2, '0');
String secondsStr = (seconds % 60).toString().padLeft(2, '0');


if (hours == 0) {
return "$minutesStr:$secondsStr";
}


return "$hoursStr:$minutesStr:$secondsStr";
}

以上代码的 Null 安全版本

import 'package:flutter/material.dart';


class CountDownTimer extends StatefulWidget {
const CountDownTimer({
Key? key,
required this.secondsRemaining,
required this.whenTimeExpires,
this.countDownFormatter,
this.countDownTimerStyle,
}) : super(key: key);


final int secondsRemaining;
final VoidCallback whenTimeExpires;
final TextStyle? countDownTimerStyle;
final Function(int seconds)? countDownFormatter;


@override
State createState() => _CountDownTimerState();
}


class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
late final AnimationController _controller;
late final Duration duration;


String get timerDisplayString {
final duration = _controller.duration! * _controller.value;
if (widget.countDownFormatter != null) {
return widget.countDownFormatter!(duration.inSeconds) as String;
} else {
return formatHHMMSS(duration.inSeconds);
}
}


String formatHHMMSS(int seconds) {
final hours = (seconds / 3600).truncate();
seconds = (seconds % 3600).truncate();
final minutes = (seconds / 60).truncate();


final hoursStr = (hours).toString().padLeft(2, '0');
final minutesStr = (minutes).toString().padLeft(2, '0');
final secondsStr = (seconds % 60).toString().padLeft(2, '0');


if (hours == 0) {
return '$minutesStr:$secondsStr';
}


return '$hoursStr:$minutesStr:$secondsStr';
}


@override
void initState() {
super.initState();
duration = Duration(seconds: widget.secondsRemaining);
_controller = AnimationController(
vsync: this,
duration: duration,
);
_controller
..reverse(from: widget.secondsRemaining.toDouble())
..addStatusListener((status) {
if (status == AnimationStatus.completed ||
status == AnimationStatus.dismissed) {
widget.whenTimeExpires();
}
});
}


@override
void didUpdateWidget(CountDownTimer oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.secondsRemaining != oldWidget.secondsRemaining) {
setState(() {
duration = Duration(seconds: widget.secondsRemaining);
_controller.dispose();
_controller = AnimationController(
vsync: this,
duration: duration,
);
_controller
..reverse(from: widget.secondsRemaining.toDouble())
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
widget.whenTimeExpires();
}
});
});
}
}


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


@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _controller,
builder: (_, Widget? child) {
return Text(
timerDisplayString,
style: widget.countDownTimerStyle,
);
},
),
);
}
}


一行倒计时计时器

CountdownTimer(Duration(seconds: 5), Duration(seconds: 1)).listen((data){
})..onData((data){
print('data $data');
})..onDone((){
print('onDone.........');
});

doesnt directly answer your question. But helpful for those who want to start something after some time.

Future.delayed(Duration(seconds: 1), () {
print('yo hey');
});

如果所有您需要的是一个简单的倒计时器,这是一个很好的替代方案,而不是安装软件包。编程愉快!

countDownTimer() async {
int timerCount;
for (int x = 5; x > 0; x--) {
await Future.delayed(Duration(seconds: 1)).then((_) {
setState(() {
timerCount -= 1;
});
});
}
}

Here is my Timer widget, not related to the Question but may help someone.

import 'dart:async';


import 'package:flutter/material.dart';


class OtpTimer extends StatefulWidget {
@override
_OtpTimerState createState() => _OtpTimerState();
}


class _OtpTimerState extends State<OtpTimer> {
final interval = const Duration(seconds: 1);


final int timerMaxSeconds = 60;


int currentSeconds = 0;


String get timerText =>
'${((timerMaxSeconds - currentSeconds) ~/ 60).toString().padLeft(2, '0')}: ${((timerMaxSeconds - currentSeconds) % 60).toString().padLeft(2, '0')}';


startTimeout([int milliseconds]) {
var duration = interval;
Timer.periodic(duration, (timer) {
setState(() {
print(timer.tick);
currentSeconds = timer.tick;
if (timer.tick >= timerMaxSeconds) timer.cancel();
});
});
}


@override
void initState() {
startTimeout();
super.initState();
}


@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.timer),
SizedBox(
width: 5,
),
Text(timerText)
],
);
}
}

你会得到这样的东西

enter image description here

我用的是 https://pub.dev/packages/flutter_countdown_timer

从属关系: flutter_countdown_timer: ^1.0.0

$摇摆酒吧

CountdownTimer(endTime: 1594829147719)

1594829147719是以毫秒为单位的时间戳

现在说这个有点晚了,但是你们为什么不试试动画呢? 不,我不是要你们管理动画控制器,把它们处理掉,还有其他的事情,有一个内置的小部件叫做 TweenAnimationBuilder。您可以在任何类型的值之间进行动画处理,这里有一个使用“持续时间”类的示例

TweenAnimationBuilder<Duration>(
duration: Duration(minutes: 3),
tween: Tween(begin: Duration(minutes: 3), end: Duration.zero),
onEnd: () {
print('Timer ended');
},
builder: (BuildContext context, Duration value, Widget? child) {
final minutes = value.inMinutes;
final seconds = value.inSeconds % 60;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Text('$minutes:$seconds',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30)));
}),

你也会得到 onEnd 的回调,当动画完成时会通知你;

这是输出

Many answer already provided. I suggest a shortcut way-

使用此软件包 一个 href = “ https://pub.dev/package/custom_ timer/example”rel = “ nofollow norefrer”> Custom _ timer

将其添加到包的 pubspec.yaml 文件中:

dependencies:
custom_timer: ^0.0.3

(使用最新版本)

而且实现起来非常简单

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text("CustomTimer example"),
),
body: Center(
child: CustomTimer(
from: Duration(hours: 12),
to: Duration(hours: 0),
onBuildAction: CustomTimerAction.auto_start,
builder: (CustomTimerRemainingTime remaining) {
return Text(
"${remaining.hours}:${remaining.minutes}:${remaining.seconds}",
style: TextStyle(fontSize: 30.0),
);
},
),
),
),
);
}
import 'dart:async';
import 'package:flutter/material.dart';


class CustomTimer extends StatefulWidget {
@override
_CustomTimerState createState() => _CustomTimerState();
}


class _CustomTimerState extends State<CustomTimer> {
final _maxSeconds = 61;
int _currentSecond = 0;
Timer _timer;


String get _timerText {
final secondsPerMinute = 60;
final secondsLeft = _maxSeconds - _currentSecond;


final formattedMinutesLeft =
(secondsLeft ~/ secondsPerMinute).toString().padLeft(2, '0');
final formattedSecondsLeft =
(secondsLeft % secondsPerMinute).toString().padLeft(2, '0');


print('$formattedMinutesLeft : $formattedSecondsLeft');
return '$formattedMinutesLeft : $formattedSecondsLeft';
}


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


@override
void dispose() {
_timer.cancel();
super.dispose();
}


@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.timer),
Text(_timerText),
],
),
),
);
}


void _startTimer() {
final duration = Duration(seconds: 1);
_timer = Timer.periodic(duration, (Timer timer) {
setState(() {
_currentSecond = timer.tick;
if (timer.tick >= _maxSeconds) timer.cancel();
});
});
}
}

为了以 hh: mm: ss 格式显示总秒数,可以使用以下方法:

 String getDuration(int totalSeconds) {
String seconds = (totalSeconds % 60).toInt().toString().padLeft(2, '0');
String minutes =
((totalSeconds / 60) % 60).toInt().toString().padLeft(2, '0');
String hours = (totalSeconds ~/ 3600).toString().padLeft(2, '0');


return "$hours\:$minutes\:$seconds";




}

我已经创建了一个惊人的计时器没有任何插件,在这里你也可以得到倒计时计时器。 别忘了关掉背部按钮的计时器。

Here is the link of my timer full Project. * 希望这能帮到别人,谢谢。 *

enter image description here

   import 'dart:async';


import 'package:flutter/material.dart';


void main() {
runApp(const MyApp());
}


class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);


// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: AttendanceScreen(),
);
}
}


class AttendanceScreen extends StatefulWidget {
AttendanceScreen();


@override
_AttendanceScreenState createState() => _AttendanceScreenState();
}


class _AttendanceScreenState extends State<AttendanceScreen> {
static var countdownDuration = Duration(minutes: 10);
static var countdownDuration1 = Duration(minutes: 10);
Duration duration = Duration();
Duration duration1 = Duration();
Timer? timer;
Timer? timer1;
bool countDown = true;
bool countDown1 = true;


@override
void initState() {
var hours;
var mints;
var secs;
hours = int.parse("00");
mints = int.parse("00");
secs = int.parse("00");
countdownDuration = Duration(hours: hours, minutes: mints, seconds: secs);
startTimer();
reset();
var hours1;
var mints1;
var secs1;
hours1 = int.parse("10");
mints1 = int.parse("00");
secs1 = int.parse("00");
countdownDuration1 =
Duration(hours: hours1, minutes: mints1, seconds: secs1);
startTimer1();
reset1();
super.initState();
}


@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text("Timer Example"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
onPressed: () {
_onWillPop();
},
),
),
body: Container(
color: Colors.black12,
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
),
Text(
"Timer",
style: TextStyle(fontSize: 25),
),
Container(
margin: EdgeInsets.only(top: 30, bottom: 30),
child: buildTime()),
SizedBox(
height: 20,
),
Text(
"Count down timer",
style: TextStyle(fontSize: 25),
),
Container(
margin: EdgeInsets.only(top: 30, bottom: 30),
child: buildTime1()),
]),
),
),
);
}


Future<bool> _onWillPop() async {
final isRunning = timer == null ? false : timer!.isActive;
if (isRunning) {
timer!.cancel();
}
Navigator.of(context, rootNavigator: true).pop(context);
return true;
}


void reset() {
if (countDown) {
setState(() => duration = countdownDuration);
} else {
setState(() => duration = Duration());
}
}


void reset1() {
if (countDown) {
setState(() => duration1 = countdownDuration1);
} else {
setState(() => duration1 = Duration());
}
}


void startTimer() {
timer = Timer.periodic(Duration(seconds: 1), (_) => addTime());
}


void startTimer1() {
timer = Timer.periodic(Duration(seconds: 1), (_) => addTime1());
}


void addTime() {
final addSeconds = 1;
setState(() {
final seconds = duration.inSeconds + addSeconds;
if (seconds < 0) {
timer?.cancel();
} else {
duration = Duration(seconds: seconds);
}
});
}


void addTime1() {
final addSeconds = 1;
setState(() {
final seconds = duration1.inSeconds - addSeconds;
if (seconds < 0) {
timer1?.cancel();
} else {
duration1 = Duration(seconds: seconds);
}
});
}


Widget buildTime() {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(duration.inHours);
final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
buildTimeCard(time: hours, header: 'HOURS'),
SizedBox(
width: 8,
),
buildTimeCard(time: minutes, header: 'MINUTES'),
SizedBox(
width: 8,
),
buildTimeCard(time: seconds, header: 'SECONDS'),
]);
}


Widget buildTime1() {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(duration1.inHours);
final minutes = twoDigits(duration1.inMinutes.remainder(60));
final seconds = twoDigits(duration1.inSeconds.remainder(60));
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
buildTimeCard(time: hours, header: 'HOURS'),
SizedBox(
width: 8,
),
buildTimeCard(time: minutes, header: 'MINUTES'),
SizedBox(
width: 8,
),
buildTimeCard(time: seconds, header: 'SECONDS'),
]);
}


Widget buildTimeCard({required String time, required String header}) =>
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(20)),
child: Text(
time,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 50),
),
),
SizedBox(
height: 24,
),
Text(header, style: TextStyle(color: Colors.black45)),
],
);
}
import 'package:rxdart/rxdart.dart';


final BehaviorSubject<int> resendTimeController = BehaviorSubject<int>();
static const timerDuration = 90;
int resendTimer = 0;
Timer? timer;


void startTimer() {
timer?.cancel();
resendTimer = timerDuration;


timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (resendTimer > 0) {
resendTimer--;
resendTimeController.add(resendTimer);


if (resendTimer == 0) {
timer.cancel();
}
}
});
}
and use intl this


StreamBuilder<int>(
stream: _bloc.resendTimeController,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container();
}


final DateTime date = DateTime.fromMillisecondsSinceEpoch(snapshot.data! * 1000);


return GestureDetector(
onTap: () {
if (snapshot.data == 0) {
_bloc.resendCode();
}
},
child: Text(
snapshot.data! > 0 ? 'Resend code ${DateFormat('mm:ss').format(date)}' : 'Resend SMS',
),
);
},
),
import 'package:async/async.dart';
    

late CancelableOperation? cancellableOperation;
int counter = 5;


StatefulBuilder(
builder: (context, setState) {
if (counter > 0) {
cancellableOperation = CancelableOperation.fromFuture(
Future.delayed(const Duration(seconds: 1)),
onCancel: () => {},
);
cancellableOperation?.value.whenComplete(() => setState(() => counter--));
return buildButton(
text: 'Wait($counter)',
textColor: getTextColor,
backgroundColor: Colors.grey.withOpacity(0.5),
onPressed: () {},
);
}
return buildButton(
text: 'Complete',
textColor: Colors.green,
backgroundColor: Colors.greenAccent.withOpacity(0.5),
onPressed: () {},
);
},
)

我为对话框的完整按钮做了类似的操作。它计数5秒之前显示完整的按钮。但是如果在计数完成之前关闭对话框或页面,不要忘记调用 cancellableOperation?.cancel()