Flutter - provider实现发送验证码倒计时

2,696 阅读2分钟

背景

项目中发送验证码功能几乎成了必备,Flutter中实现倒计时的核心类要用到Timmer, 使用Timer.periodic命名构造方法实现倒计时的监听。

_timer = Timer.periodic(Duration(seconds: _interval), (timer) {}

1、为啥使用provider

普遍的做法就是在每次倒计时的时候,重新restate刷新倒计时显示,这样就会导致不必要的页面渲染以及性能的损耗。Flutter提供了ChangeNotifier,可以通过provider,对发送验证码组件进行监听,当倒计时到结束整个过程时,倒计时按钮的变化完成被监听控制,话不多说,代码呈上~~~

pubspec.yaml中导入provider库

链接:pub.flutter-io.cn/packages/pr…

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.3.2

2、倒计时模型类用于被监听消费,这里处理了倒计时的逻辑

class CountDownTimeModel extends ChangeNotifier {
  int _timeTotal; //倒计总时长
  int _interval; //倒计单位
  int currentTime; //倒计时当前时间
  Timer _timer; //倒计器
  bool isFirst = true; //第一次未点击过

  int get() => currentTime;

  bool get isFinish => currentTime == _timeTotal;

  bool get isNotClicked => this.isFirst;

  CountDownTimeModel(this._timeTotal, this._interval) {
    currentTime = this._timeTotal;
  }

  void startCountDown() {
    if (_timer != null) {
      _timer.cancel();
      _timer = null;
    }

    isFirst = false;

    _timer = Timer.periodic(Duration(seconds: _interval), (timer) {
      if (timer.tick == _timeTotal) {
        //倒计完当前时间重新等于总时间
        currentTime = _timeTotal;
        _timer.cancel();
        _timer = null;
      } else {
        currentTime--;
      }
      //触发监听
      notifyListeners();
    });
  }

  void cancelTimer() {
    _timer?.cancel();
    _timer = null;
  }

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

3、使用provider新建消费组件,用于消费需要被监听的模型,这里为了方便,抽取成泛型:

class ConsumeComponent<T extends ChangeNotifier> extends StatefulWidget {
  final T model;
  final Widget child;
  final ValueWidgetBuilder<T> builder;

  ConsumeComponent({
    Key key,
    @required this.model,
    @required this.builder,
    this.child,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => _ConsumeComponentState<T>();
}

class _ConsumeComponentState<T extends ChangeNotifier>
    extends State<ConsumeComponent<T>> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>.value(
      value: widget.model,
      child: Consumer<T>(
        child: widget.child,
        builder: widget.builder,
      ),
    );
  }
}

核心的代码基本已经处理完了,剩下的就是调用了

调用

 ConsumeComponent<CountDownTimeModel>(
                    model: CountDownTimeModel(6, 1),
                    builder: (context, model, _) => InkWell(
                      onTap: !model.isFinish
                          ? null
                          : () {
                              model.startCountDown();
                            },
                      child: Text(
                        model.isNotClicked	
                            ? 'Send'	//未点击过的初始状态
                            : model.isFinish	
                                ? 'Resend'	//倒计时结束
                                : model.currentTime.toString(),//过程中
                        style: TextStyle(
                          color: model.isFinish ? Colors.blue : Colors.grey,
                        ),
                      ),
                    ),
                  ),

基本这样就完成了,这就不需要每次倒计时restate页面去build整个widget。我的理解是将被消费对象添加进消费组件中,当被消费对象中notifyListeners()后会触发消费组件进行消费,也就可以控制被被消费对象的行为了,这有点类似java的订阅监听。 可能有理解错漏的地方,还希望建议指正,多多包涵哈哈。