跟官网做demo:Flutter入门篇之Views

424 阅读6分钟

最近想学习下Flutter,于是利用demo学习一下官网的枯燥内容, 今天就用一个简单的登录页来开始Flutter的入门篇-Views!希望做完这个demo,大家就掌握了官网第一章的粗略内容,为接下来的玩游戏赢得了宝贵的。

这篇文章适合哪些人?

  • 1.有安卓开发经验想入门Flutter的码农们。

  • 2.不喜欢看书又想学到知识的“懒人”们。

  • 3.刚打完游戏或浪费时间后心里内疚的“励志者”们。

  • 4.正在蹲坑或者经常坐公交、地铁的掘金用户们。

  • 5.看到最后一条的你们。

看完这篇你能学到什么?

  • 1.修改控件内容

  • 2.布局如此精(luo)妙(suo)

  • 3.移除添加一个控件

  • 4.给控件加上特效!kuang!

  • 5.在canvas上画个啥

  • 6.一个简单的自定义控件

  • 7.缓解“不因虚度年华而悔恨”

我准备怎么讲这些东西呢?

兴趣是最好的老si!既然标题是“跟官网做demo”,所以想了一下就准备一个登录界面,通过这个简单的demo来一条条学习以上几点,如果哪点没覆盖到可以在评论区留言。至于有的人担心上厕所的时间看不完,放心,官网没有涉及的肯定不会讲。

废话不多说,学技术,到官网,赶紧拿起你的手机猛戳下面网址:
flutter.dev/docs/get-st…
今天呢,我们要讲的这是这块内容:

咦〜右边的部分不就是我上面提到的内容么?因为我就是拿这个汉化滴!不过代码部分可是自己心领神会的结晶哦。
首先简单地设计一下,一个提供两种方式的登录界面。

首先我们解决第一个问题,上图的什么输入框、按钮在Flutter中是什么?
Widget!
翻译成白话文就是组件控件的意思。
比如说一个简单的输入框是这样的:

TextField(
          decoration: InputDecoration(hintText: "请输入手机号"),
          controller: userNameController,
        ),
        TextField(
          decoration: InputDecoration(hintText: "请输入密码"),
          controller: passwordController,
          obscureText: true,
        ),

一个按钮是这样的:

child: MaterialButton(
            child: Text("验证码登录"),
            textColor: Colors.blue,
            onPressed: () {
              _switchLoginType(LOGIN_TYPE_CODE);
            },
          ),

当然还有很多其他的组件,一种组件也有不同的样式,需要的时候再去官网查一下好了。
api.flutter.dev/flutter/wid…
更简单的方式直接上网查,比如密码输入框如何设置密文:

看到这里,你只要了解flutter中Widget就是Android中的View,也是iOS中的UIView,就行了。
了解单个控件怎么弄出来的以后,就可以布局了,即规定每个控件应该出现在什么位置以及组件之间的嵌套关系。

在安卓中,我们有线性布局、相对布局、约束布局、表格以及流布局等八拉八拉的,Flutter也应有尽有。
比如我们登录界面的两个输入框以及登录方式是线性垂直排列,即放在一个叫Column的组件中。

比如输入框部分和下面的按钮又可以放在一个Column中。
再比如关于水平线性布局Row组件等。

布局太多了,还是那句话,用到了再去查。
值得一提的是,Flutter中的布局与其他平台还是细微差别,比如对齐方式在其他平台也就是一个属性,但是在Flutter中是一个组件。
好了,GET上面两个知识点后姑且认为你们知道如何进行简单的布局了,我们再继续看。
在登录页中,对于已经登录过的账号,下次登录时都会预先设置用户名,这里便有个知识点-如何更新组件内容。
我们可以给登录页预先设置个手机号。在Android中,我们直接拿到组件再调用setText方法就可以了,但是Flutter中有点麻烦,必须先给TextField设置个TextEditingController,然后调用其setText方法。

不同的组件有不同的更新方式,这里只是结合我们demo举了TextField的例子,其他组件可以举一反三。
好了,再说一说登录方式切换,这便是官网讲到的添加/移除组件了。
我们可以回想下在Android中是怎么做的,通常的做法是把两种登录方式的布局都写上,然后根据点击事件来决定哪块内容显示隐藏。
一个道理!
比如我的“账号密码登录”和“验证码登录”都在布局中,通过loginType这个成员变量来控制显示什么布局。

/// 根据输入方式切换输入框布局
  _inputAndLoginTypeArea() {
    if (LOGIN_TYPE_PASSWORD == loginType) {
      return _mobileAndPassword();
    } else if (LOGIN_TYPE_CODE == loginType) {
      return _mobileAndCode();
    } else {
      // 处理异常情况
    }
  }

然后把这个方法设置到之前的输入框位置。

当按钮点击的时候,直接改变loginType的值的就可以了。注意修改的时候要放在State下修改,否则没有效果,这点和观察者模式很类似。

下面我们看下动画,我们知道,一般耗时操作都会有个等待提示,在这个demo中,当登录按钮点击的时候,会出现一个等待对话框,在对话框中我们做一个简单的旋转动画。
和Android一样,Flutter也提供了各种动画,比如位移、旋转、淡入出等,具体可参考https://flutter.dev/docs/development/ui/animations,今天我们讲RotationTransition和CurvedAnimation。
在我们的demo中,我们使用了旋转动画来演示loading效果。

在Android中也有RotateAnimation和以及各种Interpolator,只不过Flutter多了一个控制器的概念。
讲完了动画,我们再看看Canvas绘制。
可是……这个demo需要用到Canvvas吗?可以。比如验证码倒计时功能我们就可以用canvas来弄,只是,真实项目中用这个的确有点大材小用的意思哈。

void paint(Canvas canvas, Size size) {
    final textStyle = TextStyle(
      color: Colors.white,
      fontSize: 16,
    );
    final textSpan = TextSpan(
      text: '$number',
      style: textStyle,
    );
    final textPainter = TextPainter(
      text: textSpan,
      textDirection: TextDirection.rtl,
    );
    textPainter.layout(
      minWidth: 0,
      maxWidth: 20,
    );
    final offset = Offset(-10, -5);
    textPainter.paint(canvas, offset);
  }

这个方法是覆盖了CustomPainter中的paint(Canvas canvas, Size size),作用与View的onDraw(Canvas canvas)方法类似,无非就是用一个painter把内容(形状、文字等)绘制到canvas上,用法与Android也是大同小异。
最后,再来看看自定义控件,在Android中,我们继承View就可以叫做自定义控件,比如extends Button或者extends ImageView等,在Flutter中,我们当然也可以自定义控件,只不过需要extends Widget罢了。比如在下面的代码中我们写了一个发送验证码的控件。

class CodeButtonState extends State<CodeButton> {
  int number = 60;
  bool isRepeat = false;
  Timer timer;
  Duration oneSec = const Duration(seconds: 1);
  bool isPressed = false;
  @override
  Widget build(BuildContext context) {
    return MaterialButton(
      color: Colors.blue,
      textColor: Colors.white,
      child: _child(),
      onPressed: () {
        _countDown();
      },
    );
  }

  _child() {
    if (number <= 0) {
      if (this.isRepeat) {
        return Text("重新发送");
      }
    } else if(number == 60){
      return Text("发送验证码");
    } else {
      return CustomPaint(
        painter: DrawNumberPainter(number),
      );
    }
  }

  /// 倒计时
  _countDown() {
    if(isPressed) {
      return;
    }
    isPressed = true;
    timer = new Timer.periodic(
      oneSec,
      (Timer timer) => setState(
        () {
          if (number <= 0) {
            timer.cancel();
            setState(() {
              isPressed = false;
              this.isRepeat = true;
            });
          } else {
            setState(() {
              this.number = number - 1;
            });
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    timer.cancel();
    super.dispose();
  }
}

这样,在代码中我们只需要把这个控件作为child放进去就好了。
好了,今天通过demo和大家走马观花地把官网中Views相关内容理了一遍,后面章节中会继续通过这个demo来理下Intent相关内容,至于等到猴年马月要看老夫哪天不996聊〜〜

最后,警告一句蹲坑的同鞋们:不要忘记擦P股噢!

项目地址:github.com/andrsay/Flu…