Flutter 135: 圖解 Timer & ACETimerButton 自定義計時器按鈕

    小菜在學習 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 自定義計時器按鈕還不夠完善;如有錯誤,請多多指導!

來源: 阿策小和尚

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章