zoukankan      html  css  js  c++  java
  • [Flutter] Flutter 波浪波动效果的登录页面

    转自: https://biglead.blog.csdn.net/article/details/109911109

    作者:

    B站视频教程: https://www.bilibili.com/video/BV1hK4y1j7mT

    效果如下图所示

    页面初始化

    波浪也是在波动,所以先来个动画控制器 Example820 的构建如下:

    class Example820 extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _ExampleState();
      }
    }
    
    class _ExampleState extends State with SingleTickerProviderStateMixin {
      ///动画控制器
      AnimationController _animationController;
    
      @override
      void initState() {
        super.initState();
        //创建动画控制器
        _animationController = AnimationController(
          //默认的初始值
          value: 0.0,
          //执行时间
          duration: Duration(seconds: 10),
          //值变化范围
          upperBound: 1,
          lowerBound: -1,
          vsync: this,
        );
        //重复执行
        _animationController.repeat();
      }
    
      @override
      void dispose() {
        //销毁
        _animationController.dispose();
        super.dispose();
      }
      ... 
    }

    页面的主体UI构建

    页面主体使用 Scaffold脚手架构建,通过层叠布局Stack来组合,代码如下:

      @override
      Widget build(BuildContext context) {
        //获取当前组件的大小
        Size size = MediaQuery
            .of(context)
            .size;
        return Scaffold(
          //允许键盘弹出布局文件上移
          resizeToAvoidBottomPadding: true,
          body: Container(
            //填充
             size.width,
            height: size.height,
            //层叠
            child: Stack(
              children: <Widget>[
                //第一部分 水波纹背景
                buildFirstAnimation(size),
                //第二部分 顶部的文本
                buildTopText(size),
                //第三部分 底部的按钮
                buildBottomButton(size),
              ],
            ),
          ),
        );
      }

    波浪样式的背景构建

    先通过 Container 的BoxDecoration 来设置渐变的背景样式,然后再通过 ClipPath 来裁剪,最后通过 AnimatedBuilder 与 AnimationController 动起来,代码如下:

      ///  构建 AnimatedBuilder 与裁剪水波纹
      /// 
      AnimatedBuilder buildFirstAnimation(Size size) {
        return AnimatedBuilder(
          //绑定动画控制器
          animation: _animationController,
          builder: (BuildContext context, Widget child) {
            //裁剪组件
            return ClipPath(
              //自定义裁剪路径
              clipper: HeaderClipper(_animationController.value),
              //裁剪的子Widget
              child: Container(
                //高度
                height: size.height * 0.5,
                //线性渐变颜色的样式
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    //线性渐变的方向
                      begin: Alignment.bottomLeft,
                      end: Alignment.topRight,
                      colors: [Color(0xFFE0647B), Color(0xFFFCDD89)]),
                ),
              ),
            );
          },
        );
      }

    ClipPath 用来裁剪自定义图片,定义如下:

    /// 代码清单 8-34 自定义 Clipper
    /// 
    class HeaderClipper extends CustomClipper<Path> {
      ///取值为 -1 ~ 1.0
      double moveFlag = 0;
    
      HeaderClipper(this.moveFlag);
    
      @override
      Path getClip(Size size) {
        //创建 Path
        Path path = Path();
        //移动到点 P0点 也是曲线的起点
        path.lineTo(0, size.height * 0.8);
        //计算控制点 P1 的坐标
        double xCenter = size.width * 0.5 +
            (size.width * 0.6 + 1) * sin(moveFlag * pi);
        double yCenter = size.height * 0.8 + 69 * cos(moveFlag*pi);
        //构建 二阶贝塞尔曲线
        path.quadraticBezierTo(xCenter, yCenter, size.width, size.height * 0.8);
    
        path.lineTo(size.width, 0);
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) {
        //刷新
        return true;
      }
    }

    用到了 二阶贝塞尔曲线 ,占位分析图如下:

    二阶贝塞尔曲线:

    也用到了 pi :

    pi 、cos、sin 是Flutter Sdk dart.math类中提供的。

    底部的文本输入区域

    使用线性布局Column 来构建,代码如下:

     ///底部对齐的输入框
      Positioned buildBottomButton(Size size) {
        return Positioned(
          bottom: 60,
          left: 0,
          right: 0,
          child: Column(
            //包裹子Widget
            mainAxisSize: MainAxisSize.min,
            //主方向子Widget 底部对齐 (Column的垂直方向)
            mainAxisAlignment: MainAxisAlignment.end,
            //次方向子Widget居中对齐 (Column的水平方向)
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Container(
                 size.width * 0.8,
                margin: EdgeInsets.only(top: 18),
                child: buildInputWidget('请输入账号'),
              ),
              Container(
                 size.width * 0.8,
                margin: EdgeInsets.only(top: 18),
                child: buildInputWidget('请输入密码', isPass: true),
              ),
              Container(
                margin: EdgeInsets.only(top: 20),
                padding: EdgeInsets.only(bottom: 60),
                 size.width * 0.7,
                child: ElevatedButton(
                  onPressed: () {},
                  child: Text(
                    '登录',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }

    因为这里的文本输入框是比较类似的,所以也进行了封装调用 ,代码如下:

    /// 
    Widget buildInputWidget(String hint, {bool isPass = false}) {
      return TextField(
        //是否隐藏文本
        obscureText: isPass,
        //文本的边框装饰
        decoration: InputDecoration(
          //提示文本
          hintText: hint,
          //提示文本的样式
          hintStyle: TextStyle(color: Color(0xFFACACAC), fontSize: 14),
          //输入内容的内边距
          contentPadding: EdgeInsets.only(top: 20, bottom: 20, left: 38),
          //输入框可用时的边框样式
          enabledBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Colors.lightBlueAccent),
            borderRadius: BorderRadius.all(Radius.circular(30.0)),
          ),
          //输入框获取输入焦点时的边框样式
          focusedBorder: OutlineInputBorder(
            borderSide: BorderSide(color: Colors.green),
            borderRadius: BorderRadius.all(Radius.circular(30.0)),
          ),
        ),
      );
    }
  • 相关阅读:
    js 比较好的博客
    网络相关
    gulp学习笔记--简单入门
    数组和对象的复制
    seajs学习笔记
    art-template引擎模板
    angularJS中的$apply(),$digest(),$watch()
    CMD和AMD
    通过script标签实现跨域
    jQuery基础知识
  • 原文地址:https://www.cnblogs.com/yangyxd/p/14428913.html
Copyright © 2011-2022 走看看