[英]Flutter Countdown Timer
如何將傳遞的值放入構造中,制作一個四舍五入到第一個小數點並顯示在我的 RaisedButton 的子文本中的計時器? 我試過但沒有運氣。 我設法使用一個簡單的 Timer 使回調函數工作,但沒有周期性,也沒有在文本中實時更新值......
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),
),
),
);
}
}
這是一個使用Timer.periodic的示例:
倒計時從10
到0
按鈕單擊開始:
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")
],
),
);
}
結果 :
您還可以使用quiver.async庫中的CountdownTimer類,使用更加簡單:
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
變量,從而導致更快的遞減計數器。
有多種解決方案可以改變這種行為,具體取決於您想要實現的目標:
Timer.periodic
創建,以便多次單擊按鈕無效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(...);
}
你也可以使用這個官方的異步包,它提供了一個從Timer
擴展的RestartableTimer類並添加了reset
方法。
所以只需調用_timer.reset();
在每個按鈕點擊。
最后,Codepen 現在支持 Flutter! 所以這里是一個活生生的例子,這樣每個人都可以玩它: https ://codepen.io/Yann39/pen/oNjrVOb
我創建了一個通用計時器小部件,它可以用來顯示任何類型的計時器,而且它也很靈活。
此小部件具有以下屬性
hh mm ss
字符串,如01 hours: 20 minutes: 45 seconds
您可以提供一個默認格式化程序 ( formatHHMMSS
),以防您不想從每個地方提供它。
// 為此提供實現 - 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,
),
),
)
格式HHMMSS示例:
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";
}
上述代碼的空安全版本
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,
);
},
),
);
}
}
派對遲到了,但你們為什么不嘗試動畫。不,我不是在告訴你管理動畫控制器並將它們處理掉以及所有這些東西,有一個名為TweenAnimationBuilder的內置小部件。 您可以在任何類型的值之間進行動畫處理,這是一個 Duration 類的示例
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 回調,它會在動畫完成時通知您;
這是輸出
這是我的計時器小部件,與問題無關,但可能對某人有所幫助。
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)
],
);
}
}
你會得到這樣的東西
不直接回答你的問題。 但對那些想在一段時間后開始做某事的人很有幫助。
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;
});
});
}
}
我正在使用https://pub.dev/packages/flutter_countdown_timer
依賴項:flutter_countdown_timer:^1.0.0
$顫振酒吧得到
CountdownTimer(endTime: 1594829147719)
1594829147719 是您的時間戳,以毫秒為單位
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();
});
});
}
}
我創建了一個沒有任何插件的驚人計時器,在這里你也可以得到倒數計時器。 並且不要忘記在按下后停止計時器。
這是我的計時器完整項目的鏈接。 *希望這會對某人有所幫助。 謝謝你。 *
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)),
],
);
}
已經提供了許多答案。 我建議一種快捷方式-
使用這個包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),
);
},
),
),
),
);
}
你可以使用這個插件timer_builder
timer_builder 小部件,它在計划的、定期的或動態生成的時間事件上重建自己。
例子
定期重建
import 'package:timer_builder/timer_builder.dart';
class ClockWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TimerBuilder.periodic(Duration(seconds: 1),
builder: (context) {
return Text("${DateTime.now()}");
}
);
}
}
按計划重建
import 'package:timer_builder/timer_builder.dart';
class StatusIndicator extends StatelessWidget {
final DateTime startTime;
final DateTime endTime;
StatusIndicator(this.startTime, this.endTime);
@override
Widget build(BuildContext context) {
return TimerBuilder.scheduled([startTime, endTime],
builder: (context) {
final now = DateTime.now();
final started = now.compareTo(startTime) >= 0;
final ended = now.compareTo(endTime) >= 0;
return Text(started ? ended ? "Ended": "Started": "Not Started");
}
);
}
}
要以這種格式 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";
}
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()
。
倒數計時器在一行
CountdownTimer(Duration(seconds: 5), Duration(seconds: 1)).listen((data){
})..onData((data){
print('data $data');
})..onDone((){
print('onDone.........');
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.