小菜在學習 Flutter 過程中,可能會遇到倒計時等需求,此時需要用到 Timer 計時器,小菜簡單嘗試一下;
Timer
Timer 作為一個抽象類,提供了兩種工廠方法進行調用;可以作為一次或者重複觸發的倒計時計時器;
案例嘗試
1. Timer()
factory Timer(Duration duration, void Function() callback) {
if (Zone.current == Zone.root) {
return Zone.current.createTimer(duration, callback);
}
return Zone.current.createTimer(duration, Zone.current.bindCallbackGuarded(callback));
}
Timer 允許指定延遲時間之後執行,通過設定 Timeout 來指定延遲時間,之後通過 callback 回調對執行結果進行監聽處理;
Timer(Duration(seconds: 3), () {
print("_timer01() -> Timer(Duration(seconds: 3) printed after 3 seconds");
Toast.show('Timer(Duration(seconds: 3) printed after 3 seconds !', context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
});
2. Timer.periodic()
factory Timer.periodic(Duration duration, void callback(Timer timer)) {
if (Zone.current == Zone.root) {
return Zone.current.createPeriodicTimer(duration, callback);
}
var boundCallback = Zone.current.bindUnaryCallbackGuarded<Timer>(callback);
return Zone.current.createPeriodicTimer(duration, boundCallback);
}
簡單了解 Timer.periodic() 命名構造方法可得,該命名構造方法通過定時綁定回調進行再次 Timer 倒計時處理;
Timer.periodic() 可以重複性、周期性的進行倒計時,若不進行手動調用,則會一直關閉,即便頁面關閉也會繼續調用;
其中 Timer.tick 為調用次數,Timer.isActive 代表目前 Timer 是否處于活躍狀态;
Timer.periodic(Duration(seconds: 2), (timer) {
print('_timer02() -> Timer.periodic() -> Timer.tick -> ${timer.tick} -> Timer.isActive -> ${timer.isActive}');
});
3. Timer.run()
static void run(void Function() callback) {
new Timer(Duration.zero, callback);
}
Timer 的執行為異步操作,Flutter 提供了便利的 Timer.run() 命名構造函數可以友善盡快執行,可以簡單了解為倒計時為 0;
小菜嘗試了如下操作順序,首先執行同步的 A -> B -> C,之後才會是異步的 run() 和 Duration.zero;
print('_timer03() -> A');
Timer.run(() {
print('_timer03() -> Timer.run()');
});
print('_timer03() -> B');
Timer(Duration.zero, () {
print('_timer03() -> Duration.zero');
});
print('_timer03() -> C');
4. cancel()
Timer() 計時器可以通過 cancel() 來取消,尤其是在進行周期性的 Timer.periodic() 調用時,需要在合适的時機及時取消;小菜嘗試在 Timer() 回調内取消和方法外回調兩種方式;
4.1 Timer() 回調内取消
Timer.periodic(Duration(seconds: 2), (timer) {
if (timer.tick == 3) {
timer.cancel();
}
print('_timer04() -> Timer.periodic() -> Timer.tick -> ${timer.tick} -> Timer.isActive -> ${timer.isActive}');
});
4.2 方法外取消
_timer = Timer.periodic(Duration(seconds: 2), (timer) {
print('_timer05() -> Timer.periodic() -> Timer.tick -> ${timer.tick} -> Timer.isActive -> ${timer.isActive}');
});
_timer.cancel();
ACETimerButton 自定義計時器
小菜嘗試了一個簡單的計時器,類似于擷取驗證碼按鈕;timeout 為倒計時時長,color 用于自定義文本顔色,preName 為文本内容;
ACETimerButton(this.timeout, {this.color, this.preName});
整個定義過程很簡單,隻需在按鈕點選時更新按鈕文本内容以及進行 Timer 周期性倒計時計算,并在倒計時結束和 Widget 銷毀時及時取消并銷毀 Timer 即可;
class ACETimerButton extends StatefulWidget {
final int timeout;
final Color color;
final String preName;
ACETimerButton(this.timeout, {this.color, this.preName});
@override
_ACETimerButtonState createState() => _ACETimerButtonState();
}
class _ACETimerButtonState extends State<ACETimerButton> {
Timer _timer;
int _timeOut;
String _name;
bool _isClick = false;
@override
Widget build(BuildContext context) => GestureDetector(
onTap: () {
if (!_isClick) {
_startTimer();
}
_isClick = true;
},
child: Container(
padding: EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.2),
borderRadius: BorderRadius.circular(4.0)),
child: Center(child: Text(_name ?? 'click', style: TextStyle(color: widget.color ?? Colors.blue, fontSize: 16.0)))));
@override
void initState() {
super.initState();
_name = widget.preName;
_timeOut = widget.timeout;
}
_startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_timeOut--;
_name = '$_timeOut s';
});
if (_timeOut == 0) {
_cancelTimer();
_isClick = false;
_name = widget.preName;
_timeOut = widget.timeout;
}
});
}
_cancelTimer() {
if (_timer != null) {
_timer.cancel();
_timer = null;
}
_isClick = false;
}
@override
void dispose() {
super.dispose();
_cancelTimer();
}
}
Timer 案例源碼 小菜對 Timer 計時器的學習暫時告一段落,對于 ACETimerButton 自定義計時器按鈕還不夠完善;如有錯誤,請多多指導!
來源: 阿策小和尚