美文网首页All in FlutterFlutter教程网Flutter
Flutter仿微信,支付宝密码输入框+自定义键盘

Flutter仿微信,支付宝密码输入框+自定义键盘

作者: Yinll | 来源:发表于2018-12-12 19:04 被阅读116次

    大家好,我又来了。
    今年这个冬天真的是“寒冬”啊,我是真的被“冻伤”了,一年的计划全部被打算了,贼无奈,也让我遭受了一定的打击,希望之光在哪?(吐槽到此为止)
    回到咱们的正题,刚用Flutter做完一个金融项目,当中使用到了类似于微信,和支付宝的那种密码输入框,然后为了安全一点也自己实现了自定义的键盘,今天跟大家分享一波
    效果如下图所示:


    Flutter自定义密码——键盘.jpg

    当中的布局形式,大家可根据自己的具体需求来调整就好了,我这里写的demo是这样的布局,这个调整起来很简单(本来想弄成gif的,然而不会。。。)。

    我们分析下这个东东,首先我们需要自定义好这个密码输入框,当我们在输入一个密码的时候,密码输入框就填充一位 ,这个过程其实我们自己把它绘制出来就好:

    1. 先绘制六个密码框
    2. 接受调用者传过来的密码,根据密码长度来绘制密码框的填充个数
    ///  自定义 密码输入框 第一步 —— 使用画笔画出单个的框
    class CustomJPasswordField extends StatelessWidget {
    
      ///  传入当前密码
     String data;
      CustomJPasswordField(this.data);
    
      @override
      Widget build(BuildContext context) {
        return CustomPaint(
          painter: MyCustom(data),
        );
      }
    }
    
      ///  继承CustomPainter ,来实现自定义图形绘制
    class MyCustom extends CustomPainter {
    
      ///  传入的密码,通过其长度来绘制圆点
      String pwdLength;
      MyCustom(this.pwdLength);
    
       ///  此处Sizes是指使用该类的父布局大小
      @override
      void paint(Canvas canvas, Size size) {
    
        // 密码画笔
      Paint mPwdPaint;
        Paint mRectPaint;
    
        // 初始化密码画笔  
        mPwdPaint = new Paint();
        mPwdPaint..color = Colors.black;
    
    //   mPwdPaint.setAntiAlias(true);
        // 初始化密码框  
        mRectPaint = new Paint();
        mRectPaint..color = Color(0xff707070);
    
       ///  圆角矩形的绘制
        RRect r = new RRect.fromLTRBR(
            0.0, 0.0, size.width, size.height, new Radius.circular(size.height / 12));
       ///  画笔的风格
        mRectPaint.style = PaintingStyle.stroke;
        canvas.drawRRect(r, mRectPaint);
    
       ///  将其分成六个 格子(六位支付密码)
        var per = size.width / 6.0;
        var offsetX = per;
        while (offsetX < size.width) {
          canvas.drawLine(
              new Offset(offsetX, 0.0), new Offset(offsetX, size.height), mRectPaint);
          offsetX += per;
        }
     
        ///  画实心圆
        var half = per/2;
        var radio = per/8;
        mPwdPaint.style = PaintingStyle.fill;
        ///  当前有几位密码,画几个实心圆
        for(int i =0; i< pwdLength.length && i< 6; i++){
          canvas.drawArc(new Rect.fromLTRB(i*per+half-radio, size.height/2-radio, i*per+half+radio, size.height/2+radio), 0.0, 2*pi, true, mPwdPaint);
        }
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    }
    

    到这里为止,我们就写完了我们第一个重头,自定义的密码输入框,然后第二步,实现自定义密码键盘,密码键盘也可以通过完全自定义绘制出来,但是我这里用的一种比较简单的实现方式,直接使用多个按钮组装成一个键盘,


    自定义键盘.png

    这个键盘其实就是12个相同样式的按钮组成,只是各自的文字内容不同,因此我们首先可以定义好一个公共的按钮样式,然后我们在其中通过回调的方式来将点击事件抛给调用者定义,

    import 'package:flutter/material.dart';
    
    ///  自定义 键盘 按钮
    class CustomKbBtn extends StatefulWidget {
    ///  按钮显示的文本内容
      String text;
    
      CustomKbBtn({Key key, this.text, this.callback}) : super(key: key);
     ///  按钮 点击事件的回调函数
      final callback;
      @override
      State<StatefulWidget> createState() {
        return ButtonState();
      }
    }
    
    class ButtonState extends State<CustomKbBtn> {
      ///回调函数执行体
      var backMethod;
    
      void back() {
        widget.callback('$backMethod');
      }
    
      @override
      Widget build(BuildContext context) {
    
     /// 获取当前屏幕的总宽度,从而得出单个按钮的宽度
        MediaQueryData mediaQuery = MediaQuery.of(context);
        var _screenWidth = mediaQuery.size.width;
    
        return new Container(
            height:50.0,
            width: _screenWidth / 3,
            child: new OutlineButton(
              // 直角
              shape: new RoundedRectangleBorder(
                  borderRadius: new BorderRadius.circular(0.0)),
              // 边框颜色
              borderSide: new BorderSide(color: Color(0x10333333)),
              child: new Text(
                widget.text,
                style: new TextStyle(color: Color(0xff333333), fontSize: 20.0),
              ),
             // 按钮点击事件
              onPressed: back,
            ));
      }
    }
    

    有了按钮之后,我们就将它拼装成一个完整的键盘:

    
    /// 自定义密码 键盘
    
    class MyKeyboard extends StatefulWidget {
      final callback;
    
      MyKeyboard(this.callback);
    
      @override
      State<StatefulWidget> createState() {
        return new MyKeyboardStat();
      }
    }
    
    class MyKeyboardStat extends State<MyKeyboard> {
      final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    
      /// 定义 确定 按钮 接口  暴露给调用方
      ///回调函数执行体
      var backMethod;
      void onCommitChange() {
        widget.callback(new KeyEvent("commit"));
      }
    
      void onOneChange(BuildContext cont) {
        widget.callback(new KeyEvent("1"));
      }
    
      void onTwoChange(BuildContext cont) {
        widget.callback(new KeyEvent("2"));
      }
    
      void onThreeChange(BuildContext cont) {
        widget.callback(new KeyEvent("3"));
      }
    
      void onFourChange(BuildContext cont) {
        widget.callback(new KeyEvent("4"));
      }
    
      void onFiveChange(BuildContext cont) {
        widget.callback(new KeyEvent("5"));
      }
    
      void onSixChange(BuildContext cont) {
        widget.callback(new KeyEvent("6"));
      }
    
      void onSevenChange(BuildContext cont) {
        widget.callback(new KeyEvent("7"));
      }
    
      void onEightChange(BuildContext cont) {
        widget.callback(new KeyEvent("8"));
      }
    
      void onNineChange(BuildContext cont) {
        widget.callback(new KeyEvent("9"));
      }
    
      void onZeroChange(BuildContext cont) {
        widget.callback(new KeyEvent("0"));
      }
    
      /// 点击删除
      void onDeleteChange() {
        widget.callback(new KeyEvent("del"));
      }
    
      @override
      Widget build(BuildContext context) {
        return new Container(
          key: _scaffoldKey,
          width: double.infinity,
          height: 250.0,
          color: Colors.white,
          child: new Column(
            children: <Widget>[
              new Container(
                height:30.0,
                color: Colors.white,
                alignment: Alignment.center,
                child: new Text(
                  '下滑隐藏',
                  style: new TextStyle(fontSize: 12.0, color: Color(0xff999999)),
                ),
              ),
    
              ///  键盘主体
              new Column(
                children: <Widget>[
                  ///  第一行
                  new Row(
                    children: <Widget>[
                      CustomKbBtn(
                          text: '1', callback: (val) => onOneChange(context)),
                      CustomKbBtn(
                          text: '2', callback: (val) => onTwoChange(context)),
                      CustomKbBtn(
                          text: '3', callback: (val) => onThreeChange(context)),
                    ],
                  ),
    
                  ///  第二行
                  new Row(
                    children: <Widget>[
                      CustomKbBtn(
                          text: '4', callback: (val) => onFourChange(context)),
                      CustomKbBtn(
                          text: '5', callback: (val) => onFiveChange(context)),
                      CustomKbBtn(
                          text: '6', callback: (val) => onSixChange(context)),
                    ],
                  ),
    
                  ///  第三行
                  new Row(
                    children: <Widget>[
                      CustomKbBtn(
                          text: '7', callback: (val) => onSevenChange(context)),
                      CustomKbBtn(
                          text: '8', callback: (val) => onEightChange(context)),
                      CustomKbBtn(
                          text: '9', callback: (val) => onNineChange(context)),
                    ],
                  ),
    
                  ///  第四行
                  new Row(
                    children: <Widget>[
                      CustomKbBtn(text: '删除', callback: (val) => onDeleteChange()),
                      CustomKbBtn(
                          text: '0', callback: (val) => onZeroChange(context)),
                      CustomKbBtn(text: '确定', callback: (val) => onCommitChange()),
                    ],
                  ),
                ],
              )
            ],
          ),
        );
      }
    }
    
    

    这里的回调函数,其实是将所有的按钮事件处理交给调用者自己去处理,
    这里就引出了代码中的KeyEvent()这个类,我们看看这个类的实现

    ///  支符密码  用于 密码输入框和键盘之间进行通信
    class KeyEvent {
     ///  当前点击的按钮所代表的值
      String key;
      KeyEvent(this.key);
    
      bool isDelete() => this.key == "del";
      bool isCommit() => this.key == "commit";
    }
    

    这个类实际上只是拿到了按钮最终代表的实际内容,然后调用者可以根据这个key的值来判断当前点击的是 数字按钮 还是说是 删除按钮 或者是 确定按钮,以此来进行密码的修改,。

    到这里为止,所有的内容基本都准备好了,接下来就是使用了:
    这里得注意一个点,密码键盘是从屏幕的最下方弹出来的,这里我使用到了Flutter的showBottomSheet,这个是一个官方的widget,通过这个来实现键盘的弹出。

    直接上代码吧

    /// 支付密码  +  自定义键盘
    
    class main_keyboard extends StatefulWidget {
      static final String sName = "enter";
    
      @override
      State<StatefulWidget> createState() {
        return new keyboardState();
      }
    }
    
    
    class keyboardState extends State<main_keyboard> {
     /// 用户输入的密码
      String pwdData = '';
    
     /*
        GlobalKey:整个应用程序中唯一的键
        ScaffoldState:Scaffold框架的状态
        解释:_scaffoldKey的值是Scaffold框架状态的唯一键
       */
      final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
    
      // VoidCallback:没有参数并且不返回数据的回调
      VoidCallback _showBottomSheetCallback;
    
      @override
      void initState() {
    
        _showBottomSheetCallback = _showBottomSheet;
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          key: _scaffoldKey,
          body: _buildContent(context),
        );
      }
    
      Widget _buildContent(BuildContext c) {
        return new Container(
          width: double.maxFinite,
          height: 300.0,
          color: Color(0xffffffff),
          child: new Column(
            children: <Widget>[
    
              new Padding(
                padding: const EdgeInsets.only(top: 50.0),
                child: new Text(
                  '请在此输入新支付密码',
                  style: new TextStyle(fontSize: 18.0, color: Color(0xff333333)),
                ),
              ),
    
              ///密码框
              new Padding(
                padding: const EdgeInsets.only(top: 15.0),
                child: _buildPwd(pwdData),
              ),
            ],
          ),
        );
      }
    
      /// 密码键盘 确认按钮 事件
      void onAffirmButton() {
    
      }
    
    /// 密码键盘的整体回调,根据不同的按钮事件来进行相应的逻辑实现
      void _onKeyDown(KeyEvent data){
    // 如果点击了删除按钮,则将密码进行修改
        if (data.isDelete()) {
          if (pwdData.length > 0) {
            pwdData = pwdData.substring(0, pwdData.length - 1);
            setState(() {});
          }
        } 
    // 点击了确定按钮时
    else if (data.isCommit()) {
          if (pwdData.length != 6) {
    //        Fluttertoast.showToast(msg: "密码不足6位,请重试", gravity: ToastGravity.CENTER);
            return;
          }
          onAffirmButton();
        } 
    //点击了数字按钮时  将密码进行完整的拼接
    else {
          if (pwdData.length < 6) {
            pwdData += data.key;
          }
          setState(() {});
        }
      }
      /// 底部弹出 自定义键盘  下滑消失
      void _showBottomSheet() {
        setState(() {
          // disable the button  // 禁用按钮
          _showBottomSheetCallback = null;
        });
    
     /*
          currentState:获取具有此全局键的树中的控件状态
          showBottomSheet:显示持久性的质感设计底部面板
          解释:联系上文,_scaffoldKey是Scaffold框架状态的唯一键,因此代码大意为,
               在Scaffold框架中显示持久性的质感设计底部面板
         */
        _scaffoldKey.currentState
            .showBottomSheet<void>((BuildContext context) {
         /// 将自定义的密码键盘作为其child   这里将回调函数传入
          return new MyKeyboard(_onKeyDown);
        })
            .closed
            .whenComplete(() {
          if (mounted) {
            setState(() {
              // re-enable the button  // 重新启用按钮
              _showBottomSheetCallback = _showBottomSheet;
            });
          }
        });
      }
    
    /// 构建 密码输入框  定义了其宽度和高度
      Widget _buildPwd(var pwd) {
        return new GestureDetector(
          child: new Container(
            width: 250.0,
            height:40.0,
    //      color: Colors.white,  自定义密码输入框的使用
            child: new CustomJPasswordField(pwd),
          ),
    // 用户点击输入框的时候,弹出自定义的键盘
          onTap: () {
            _showBottomSheetCallback();
          },
        );
      }
    }
    
    

    大功告成,这个时候我们就实现了想要的效果啦。
    回想了下我写的博客,基本都是代码偏多,我把该有的说明都在代码中写成注释了,我觉得这样更加的直观,希望各位喜欢这种方式,如果本文帮助到了你,希望你能点点喜欢,给我一点点鼓励,每次看到有人评论和点了喜欢,都会很开心,哈哈。要是能点点关注就更好了。

    有啥问题欢迎及时联系我,我们下次再见啦!

    代码来啦Github传送门
    喜欢的话,麻烦点点star哦!

    相关文章

      网友评论

        本文标题:Flutter仿微信,支付宝密码输入框+自定义键盘

        本文链接:https://www.haomeiwen.com/subject/caephqtx.html