周末也需要学习 分享一个 Flutter 波浪波动效果的登录页面的背景 Flutter ClipPath实现的波动

Posted 早起的年轻人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了周末也需要学习 分享一个 Flutter 波浪波动效果的登录页面的背景 Flutter ClipPath实现的波动相关的知识,希望对你有一定的参考价值。

优美的应用体验 来自于细节的处理,更源自于码农的自我要求与努力,当然也需要码农年轻灵活的思维,不局限于思维,不局限语言限制,才是编程的最高境界。

本文章实现的效果如下图所示:

1 Flutter项目中单文件定义启动测试入口

void main() 
  //启动根目录
  runApp(MaterialApp(
    //默认的页面
    home: Example820(),
  ));

2 页面初始化

波浪也是在波动,所以先来个动画控制器 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();
  
  ... 

3 页面的主体UI构建

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

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

Stack 中有三层,先来看最简单的文本 ,代码如下:

  /// 代码清单 8-31 顶部对齐的文本
  /// 
  Positioned buildTopText(Size size) 
    return Positioned(
      top: size.height * 0.2,
      left: 0,
      right: 0,
      child: Text(
        "Hello World",
        textAlign: TextAlign.center,
        style: TextStyle(
            fontSize: 33, color: Colors.white, fontWeight: FontWeight.bold),
      ),
    );
  

3.2 底部的文本输入区域

使用线性布局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(
            width: size.width * 0.8,
            margin: EdgeInsets.only(top: 18),
            child: buildInputWidget('请输入账号'),
          ),
          Container(
            width: size.width * 0.8,
            margin: EdgeInsets.only(top: 18),
            child: buildInputWidget('请输入密码', isPass: true),
          ),
          Container(
            margin: EdgeInsets.only(top: 20),
            padding: EdgeInsets.only(bottom: 60),
            width: 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)),
      ),
    ),
  );


4 波浪样式的背景构建

先通过 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类中提供的。


【x1】微信公众号的每日提醒 随时随记 每日积累 随心而过 文章底部扫码关注

【x2】各种系列的视频教程 免费开源 关注 你不会迷路

【x3】系列文章 百万 Demo 随时 复制粘贴 使用

【x4】简短的视频不一样的体验

【x5】必须有源码


不局限于思维,不局限语言限制,才是编程的最高境界。

以小编的性格,肯定是要录制一套视频的,随后会上传

有兴趣 你可以关注一下 西瓜视频 — 早起的年轻人

以上是关于周末也需要学习 分享一个 Flutter 波浪波动效果的登录页面的背景 Flutter ClipPath实现的波动的主要内容,如果未能解决你的问题,请参考以下文章

RE : 球体波浪倒计时

多份 node.js 学习资料分享

一起Talk Android吧(第五百一十九回:波浪上升动画)

Flutter 专题19 图解分享页面底部对话框 #yyds干货盘点#

在 SwiftUI 中用波浪动画填充圆圈

Python 波浪线与补码