Flutter中provider实现Textfield判断按钮是否可点击

2,521 阅读2分钟

背景:在有些项目中,我们经常会碰到,例如登录页面这种有textfield输入框和按钮的,当输入框全部输入了内容后,按钮控件更改可点击背景,置为可点击状态,如果存在一个输入框为输入内容,则更改为不可点击背景,置为不可点击的状态。

思路1:通过TextEditingController的addListener监听中restate(),然后刷新整个页面,这种做法会导致频繁的页面刷新和渲染,对性能很不友好。

思路2:只要能避免restate(),这就不会频繁刷新页面了,这就得用到privider了。核心类ChangeNotifier和ChangeNotifierProvider,可以通过监听控制达到同样效果,而不用频繁restate()。

1、导入privider库

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.3.2

2、新建一个ClickableModel类,继承ChangeNotifier。作为被消费对象

class ClickableModel extends ChangeNotifier {
  final List<TextEditingController> _textEditingController;
  bool _canClick = false;

  bool get isClickable => _canClick;

  ClickableModel(this._textEditingController) {
    _textEditingController.map((e) {
      e.addListener(() {
        var strList = List<String>();
        for (var item in _textEditingController) {
          strList.add(item.text.toString());
        }
        _canClick = strList.contains('') ? false : true;
        //触发消费监听
        notifyListeners();
      });
    }).toList();
  }
}

这里最总目的是为了获取是否可点击的状态,所以需要传入所有textfield的control,当每次触发调用当前构造函数时,遍历control获取到所有输入框的内容,如果存在空的,则按钮不可点击,否则是可点击的,每次最后要notifyListeners()。

3、新建消费类,这里为了可公用,封装成泛型

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,
      ),
    );
  }
}

4、页面的调用

//登录按钮
            ConsumeComponent<ClickableModel>(
              model: ClickableModel(
                  [_userNameController, _phoneController, _smsCodeController]),
              builder: (context, model, child) => Container(
                margin: const EdgeInsets.only(top: 40.0, left: 20, right: 20.0),
                child: CommonButton(
                  text: 'Next',
                  onPressed: !model.isClickable
                      ? null
                      : () {
                          //todo somethings
                        },
                  btnColor: model.isClickable ? Colors.blue : Colors.grey,
                ),
              ),
            ),

这里只需要传入一个textfield的control数组就可以了,然后根据监听获取到的可点击状态就可以处理剩下的逻辑了。如果有更好的方式或优化,欢迎留言哈哈哈哈!!!