Flutter | 一个超级酷炫的登陆页是怎样炼成的

近些日子在 UIMovement 上看到了一个比较酷炫的登陆页效果,以下:git

以为很酷炫,就本身实现了一下,效果以下:github

下面就来一步一步的分析是如何作出来的。微信

需求分析

首先仍是老套路,看一下都须要作什么事情:async

  1. 首先咱们最清晰明了的需求就是点击「注册」弹出 Dialog
  2. 弹出 Dialog 后延迟一段时间弹出 Dialog 里的内容
  3. Dialog 内说明文字有两种颜色
  4. 点击 「Accepter」按钮会变色缩小回弹并展现 ok图标
  5. 点击「Accepter」按钮时 Dialog 内其余文字都被「白色遮罩」
  6. 「Accepter」按钮 动画结束后 dismiss 掉当前dialog 并把 logo向上移
  7. 跳转到第二页,文字呈波浪形弹出
  8. 文字弹出后显示对话框并弹出键盘

开始实现

需求了解了,下面就是一步一步的实现效果。ide

1. 点击「注册」弹出 Dialog

在这里咱们须要注意的有一点:动画

在咱们使用 showModalBottomSheet 时,默认的背景是白色的,也就是说咱们本身设置的圆角是无论用的,ui

因此要给这个 BottomSheet 一个背景,这个参数在 showModalBottomSheet 方法中就有:this

showModalBottomSheet(
  context: context,
  backgroundColor: Colors.transparent,
  builder: (context) {
    Future.delayed(Duration(milliseconds: 50), () {
      _animationController.forward();
    });
    return AnimatedUserAgreement(
      animation: _animation,
    );
  });
)
复制代码

设置一个 backgroundColor 就ok了。lua

2. 弹出 Dialog 后延迟一段时间弹出 Dialog 里的内容

这里我是写了一个 「AnimatedWidget」,对 Dialog 里面的 Widget 同时执行透明度和位置的动画:spa

return Container(
  height: 270,
  padding: EdgeInsets.all(30),
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(30), color: Colors.white),
  child: Opacity(
    opacity: _opacityTween.evaluate(animation),
    child: Stack(
      children: <Widget>[
        SingleChildScrollView(
          child: Container(
            child: UserAgreementDialog(),
            margin: EdgeInsets.only(top: _offsetTween.evaluate(animation)),
          ),
        )
      ],
    ),
  ),
);
复制代码

可能细心的同窗看出来上面的代码有一些问题:

为何要加一个 SingleChildScrollView

由于我这里改变位置使用的动画效果是 「动态改变 Container margin 的值」

因此若是不使用 ScrollView 的话,会溢出。

3. Dialog 内说明文字有两种颜色

有两种颜色这个需求仍是比较简单的,使用 「TextSpan」就搞定了。

代码我就不贴了。

4. 点击 「Accepter」按钮会变色缩小回弹并展现 ok图标

重点来了,这个功能是相对来讲比较复杂的,可是只要咱们了解需求,写起来也是比较简单。

首先咱们也是把这个功能点拆分一下:

  1. 点击按钮的时候会变色
  2. 点击后会变回原来的颜色并缩小成一个圆形
  3. 变成圆形后动画效果展现 ok 图标

也仍是一步一步来。

1. 点击按钮的时候会变色

该功能不用考虑太多,既然有点击手势,那必然会使用 GestureDetector

而后使用 GestureDetectoronTapDown 参数,该参数是在「点击按下」时回调:

onTapDown: (d) {
  setState(() {
    btnColor = btnColors[1];
  });
复制代码

也没有多余复杂的东西,就是改变按钮的颜色。

2. 点击后会变回原来的颜色并缩小成一个圆形

如何处理点击后?或者没有点击?

GestureDetector 也帮咱们封装好了:

  • onTapUp:在点击抬起时回调
  • onTapCancel:在取消点击时回调

首先咱们处理取消点击:

onTapCancel: () {
  setState(() {
    btnColor = btnColors[0];
  });
}
复制代码

把颜色变回去就好了。

而后处理抬起时的逻辑,在抬起时也有两个逻辑:

  1. 按钮会缩小成圆形
  2. 缩小成圆形的时候会弹出 ok 图标

首先说一下第一点:

缩小成圆形的时候是有一个回弹效果的,因此不能使用 AnimatedContainer 这种,必需要使用 Animation 才有这种效果。

因此我使用了 AnimatedBuilder 来包装这个 Widget。

而后说一下第二点:

如何在缩小成圆形的时候弹出 ok 图标?

咱们可使用 IndexStack,在开始缩小动画的时候切换 index,由于 ok 图标开始时的缩放状态是 0,因此页面上是没有图标的,方便咱们后续作动画。

Widget 代码以下:

AnimatedBuilder(
  animation: _widthAnimation,
  builder: (BuildContext context, Widget child) {
    return Container(
      width: _widthAnimation.value,
      alignment: Alignment.center,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(40)),
        color: btnColor),
      margin: EdgeInsets.only(top: btnMargin),
      height: btnHeight,
      child: IndexedStack(
        alignment: Alignment.center,
        index: index,
        children: <Widget>[
          Text(
            'Accepteer',
            style: TextStyle(fontSize: 18),
          ),
          ScaleTransition(
            scale: _scaleTween.animate(_animation),
            child: Image.asset(
              'images/ok.png',
              width: 35,
              height: 35,
            ),
          )
        ],
      ),
    );
  },
),
复制代码

5. 点击「Accepter」按钮时 Dialog 内其余文字都被「白色遮罩」

这个也很简单,Container 默认就有一个参数是:foregroundDecoration,咱们只须要在这个参数里设置上咱们想要遮罩的颜色就能够了。

可是也须要注意一点,若是最开始使用的遮罩颜色为「透明色」,那么会总体变黑一下,这个具体的缘由我也没研究明白,有知道的大佬能够告知一下。

这样按钮点击后的效果就所有完成,代码以下:

onTapUp: (d) {
  Future.delayed(Duration(milliseconds: 60), () {
    setState(() {
      foregroundColor = Colors.white70;
      btnColor = btnColors[0];
      index = 1;
    });
    _widthController.forward();
    Future.delayed(Duration(milliseconds: 200), () {
      _controller.forward().then((va) {
        Navigator.pop(context);
      });
    });
  });
}
复制代码

6. 动画结束后 dismiss 掉当前dialog 并把 logo向上移

这个相对来讲就更简单了,咱们只须要在 logo 的上方套一个 AnimatedContainer

而后监听 dialog 是否已经 dismiss,若是已经 dismiss 那么则调整 margin 的值就行了。

代码以下:

setState(() {
  logoMargin = 100;
});
复制代码

这样正好 dialog 会有一个下移的动画,而 logo 上移,就达到了咱们想要的效果。

7. 跳转到第二页,文字呈波浪形弹出

如何把文字呈波浪形弹出?

咱们最早想到的确定就是动画,由于也只有动画才有这种回弹的效果,

那这么多文字,每个都要设置动画?

答案是确定的。

既然知道了,那咱们也只能循序渐进的作了。

能够看到,每个文字都是由透明转为不透明,而且还会更改位置,

那咱们仍是先来封装一个 AnimatedWidget

代码以下:

class AnimatedStrWidget extends AnimatedWidget {
  final Tween<double> _opacityTween = Tween(begin: 0, end: 1);
  final Tween<Offset> _offsetTween =
      Tween(begin: Offset(0, 3), end: Offset(0, 0));
  final Widget child;

  AnimatedStrWidget(
      {Key key, @required Animation<double> animation, @required this.child})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Opacity(
      opacity: _opacityTween.evaluate(animation) < 0
          ? 0
          : _opacityTween.evaluate(animation) > 1
              ? 1
              : _opacityTween.evaluate(animation),
      child: SlideTransition(
        position: _offsetTween.animate(animation),
        child: child,
      ),
    );
  }
}
复制代码

这里也有两点须要注意:

  1. 透明度不能有负数而且不能大于1,由于咱们这个效果是要有回弹的效果,因此要作判断。
  2. Tween<Offset> 这里的值是整个高度的倍数,因此不要觉得是像素值。

封装好之后咱们就能够愉快的玩耍了:

void startAnim() async {
  for (int i = 0; i < strs.length; i++) {
    Future.delayed(
      Duration(
        milliseconds: i * 100,
      ), () {
        _strController[i].forward();
      });
  }
}
复制代码

文字弹出效果时间为 600ms,这里设置每隔100ms作一个动画,

这样的效果是比较好的,更像波浪形弹出。

8. 文字弹出后显示对话框并弹出键盘

主动弹出键盘咱们应该都有所了解,使用 FocusNode

这里咱们也是只须要判断最后一个动画什么时候作完,而后把隐藏的键盘弹出,而且把键盘弹出就ok了。

代码以下:

_strPositionAnimation[strs.length - 1].addStatusListener((status){
  if(status == AnimationStatus.completed){
    setState(() {
      opacity = 1;
      FocusScope.of(context).requestFocus(myFocusNode);
    });
  }
});
复制代码

总结

实现这个页面耗费了我一个晚上的时间,不得不说,东西仍是很多的。

想要实现这样酷炫的登陆页,仍是比较复杂。

这里我实现的还不是很完美,看起来对比原图有些「着急」。

不过无所谓了,就是改变一下动画持续时间的事。

仍是那句话,梳理好需求,什么都好作。

代码已上传至 GitHub:github.com/wanglu1209/…

另我我的建立了一个「Flutter 交流群」,能够添加我我的微信 「17610912320」来入群。

推荐阅读:

Flutter | WReorderList 一个能够指定两个item互换位置的组件

Flutter | 如何实现一个水波纹扩散效果的 Widget

Flutter | 自定义一个 Stepper 步骤组件

img
相关文章
相关标签/搜索