Flutter 开发 一个超级酷炫的登录页

2019-09-0318:55:08APP与小程序开发Comments2,994 views字数 4910阅读模式

看到了一个比较酷炫的登录页效果,如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

Flutter 开发 一个超级酷炫的登录页

觉得很酷炫,就自己实现了一下,效果如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

Flutter 开发 一个超级酷炫的登录页

下面就来一步一步的分析是如何做出来的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

需求分析

首先还是老套路,看一下都需要做什么事情:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

开始实现

需求了解了,下面就是一步一步的实现效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

1. 点击「注册」弹出 Dialog

在这里我们需要注意的有一点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

在我们使用 showModalBottomSheet 时,默认的背景是白色的,也就是说我们自己设置的圆角是不管用的,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

所以要给这个 BottomSheet 一个背景,这个参数在 showModalBottomSheet 方法中就有:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

设置一个 backgroundColor 就ok了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

这里我是写了一个 「AnimatedWidget」,对 Dialog 里面的 Widget 同时执行透明度和位置的动画:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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)),
          ),
        )
      ],
    ),
  ),
);
复制代码

可能细心的同学看出来上面的代码有一些问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

为什么要加一个 SingleChildScrollView文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

因为我这里改变位置使用的动画效果是 「动态改变 Container margin 的值」文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

所以如果不使用 ScrollView 的话,会溢出。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

有两种颜色这个需求还是比较简单的,使用 「TextSpan」就搞定了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

代码我就不贴了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

重点来了,这个功能是相对来说比较复杂的,但是只要我们了解需求,写起来也是比较简单。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

首先我们也是把这个功能点拆分一下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

也还是一步一步来。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

该功能不用考虑太多,既然有点击手势,那必然会使用 GestureDetector文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

然后使用 GestureDetectoronTapDown 参数,该参数是在「点击按下」时回调:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

也没有多余复杂的东西,就是改变按钮的颜色。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

如何处理点击后?或者没有点击?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

GestureDetector 也帮我们封装好了:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

首先我们处理取消点击:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

把颜色变回去就行了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

然后处理抬起时的逻辑,在抬起时也有两个逻辑:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

首先说一下第一点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

缩小成圆形的时候是有一个回弹效果的,所以不能使用 AnimatedContainer 这种,必须要使用 Animation 才有这种效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

所以我使用了 AnimatedBuilder 来包装这个 Widget。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

然后说一下第二点:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

如何在缩小成圆形的时候弹出 ok 图标?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

我们可以使用 IndexStack,在开始缩小动画的时候切换 index,因为 ok 图标开始时的缩放状态是 0,所以页面上是没有图标的,方便我们后续做动画。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

Widget 代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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,我们只需要在这个参数里设置上我们想要遮罩的颜色就可以了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

但是也需要注意一点,如果最开始使用的遮罩颜色为「透明色」,那么会整体变黑一下,这个具体的原因我也没研究明白,有知道的大佬可以告知一下。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

这样按钮点击后的效果就全部完成,代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

然后监听 dialog 是否已经 dismiss,如果已经 dismiss 那么则调整 margin 的值就好了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

这样正好 dialog 会有一个下移的动画,而 logo 上移,就达到了我们想要的效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

如何把文字呈波浪形弹出?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

我们最先想到的肯定就是动画,因为也只有动画才有这种回弹的效果,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

那这么多文字,每一个都要设置动画?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

答案是肯定的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

既然知道了,那我们也只能按部就班的做了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

可以看到,每一个文字都是由透明转为不透明,并且还会更改位置,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

那我们还是先来封装一个 AnimatedWidget文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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,
      ),
    );
  }
}
复制代码

这里也有两点需要注意:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

  1. 透明度不能有负数并且不能大于1,因为我们这个效果是要有回弹的效果,所以要做判断。
  2. Tween<Offset> 这里的值是整个高度的倍数,所以不要以为是像素值。

封装好以后我们就可以愉快的玩耍了:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

文字弹出效果时间为 600ms,这里设置每隔100ms做一个动画,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

这样的效果是比较好的,更像波浪形弹出。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

主动弹出键盘我们应该都有所了解,使用 FocusNode文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

这里我们也是只需要判断最后一个动画何时做完,然后把隐藏的键盘弹出,并且把键盘弹出就ok了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

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

总结

实现这个页面耗费了我一个晚上的时间,不得不说,东西还是不少的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

想要实现这样酷炫的登录页,还是比较复杂。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

这里我实现的还不是很完美,看起来对比原图有些「着急」。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

不过无所谓了,就是改变一下动画持续时间的事。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

还是那句话,梳理好需求,什么都好做。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

代码已上传至 GitHub:github.com/wanglu1209/…文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

作者:Flutter笔记
链接:https://juejin.im/post/5d6da9caf265da03ad146bbd
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/xcx/16041.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/xcx/16041.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定