Flutter 气泡背景效果 仿苹果桌面运动的气泡
在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天、每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不妨来瞅瞅码农的轨迹。
- 优美的音乐节奏带你浏览这个效果的编码过程
- 坚持每一天,是每个有理想青年的追求
- 追寻年轻人的脚步,也许你的答案就在这里
本文章实现的效果如下图所示:
1 前言
Flutter中任何一个单页面都可以作为一个应用程序的启动页,只要写上入口函数即可
void main() => runApp(MaterialApp(home: BobbleLoginPage(),),);
首先定义气泡 每个气泡都有位置、颜色、运动速率、角度等:
///气泡属性配置
class BobbleBean {//位置Offset postion;//颜色Color color;//运动的速度double speed;//角度double theta;//半径double radius;
}
气泡是使用生成的随机透明度白色背景,代码如下:
///获取随机透明的白色
Color getRandonWhightColor(Random random) {//0~255 0为完全透明 255 为不透明//这里生成的透明度取值范围为 10~200int a = random.nextInt(190)+10;return Color.fromARGB(a, 255, 255, 255);
}
2 页面的初始化
气泡需要运动,所以这里使用到了动画控制器,同时创建气泡数据,每个气泡的位置、速率、角度都是随机不一样的
class BobbleLoginPage extends StatefulWidget {@override_BobbleLoginPageState createState() => _BobbleLoginPageState();
}class _BobbleLoginPageState extends State<BobbleLoginPage>with TickerProviderStateMixin {//创建的气泡保存集合List<BobbleBean> _list = [];//随机数据Random _random = new Random(DateTime.now().microsecondsSinceEpoch);//气泡的最大半径double maxRadius = 100;//气泡动画的最大速度double maxSpeed = 0.7;//气泡计算使用的最大弧度(360度)double maxTheta = 2.0 * pi;//动画控制器AnimationController _animationController;//流控制器StreamController<double> _streamController = new StreamController();AnimationController _fadeAnimationController;@overridevoid initState() {super.initState();for (var i = 0; i < 20; i++) {BobbleBean particle = new BobbleBean();//获取随机透明度的白色颜色particle.color = getRandonWhightColor(_random);//指定一个位置 每次绘制时还会修改particle.postion = Offset(-1, -1);//气泡运动速度particle.speed = _random.nextDouble() * maxSpeed;//随机角度particle.theta = _random.nextDouble() * maxTheta;//随机半径particle.radius = _random.nextDouble() * maxRadius;//集合保存_list.add(particle);}//动画控制器_animationController = new AnimationController(vsync: this, duration: Duration(milliseconds: 1000));//刷新监听_animationController.addListener(() {//流更新_streamController.add(0.0);});_fadeAnimationController = new AnimationController(vsync: this, duration: Duration(milliseconds: 500));_fadeAnimationController.addStatusListener((status) {if (status == AnimationStatus.completed) {//重复执行动画_animationController.repeat();}});//重复执行动画_fadeAnimationController.forward();}...
}
我们养成一个习惯,动画控制器有创建就有销毁:
@overridevoid dispose() {//销毁_animationController.dispose();super.dispose();}
然后页面的主体是由层叠布局构建
@overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.white,///填充布局body: Stack(children: [//第一部分 第一层 渐变背景buildBackground(),//第二部分 第二层 气泡buildBubble(context),//第三部分 高斯模糊buildBlureWidget(),//第四部分 顶部的文字buildTopText(),//第五部分 输入框与按钮FadeTransition(opacity: _fadeAnimationController, child: buildColumn(context)),],),);}
//第一部分 第一层 渐变背景Container buildBackground() {return Container(decoration: BoxDecoration(//线性渐变gradient: LinearGradient(//渐变角度begin: Alignment.topLeft,end: Alignment.bottomRight,//渐变颜色组colors: [Colors.lightBlue.withOpacity(0.3),Colors.lightBlueAccent.withOpacity(0.3),Colors.blue.withOpacity(0.3),],),),);}
//第二部分 第二层 气泡Widget buildBubble(BuildContext context) {//使用Stream流实现局部更新return StreamBuilder<double>(stream: _streamController.stream,builder: (BuildContext context, AsyncSnapshot<double> snapshot) {//自定义画板return CustomPaint(//自定义画布painter: CustomMyPainter(list: _list,random: _random,),child: Container(height: MediaQuery.of(context).size.height,),);},);}
绘制气泡的精华就是这个画布了,因为动画控制器一直在重复执行,一直在重复刷新画布,所以画成会反复执行paint绘制方法,每次都重新计算坐标偏移量就形成了动画效果,代码如下
class CustomMyPainter extends CustomPainter {//创建画笔Paint _paint = Paint();//保存气泡的集合List<BobbleBean> list;//随机数变量Random random;CustomMyPainter({this.list, this.random});@overridevoid paint(Canvas canvas, Size size) {//每次绘制都重新计算位置list.forEach((element) {//计算偏移var velocity = calculateXY(element.speed, element.theta);//新的坐标 微偏移var dx = element.postion.dx + velocity.dx;var dy = element.postion.dy + velocity.dy;//x轴边界计算if (element.postion.dx < 0 || element.postion.dx > size.width) {dx = random.nextDouble() * size.width;}//y轴边界计算if (element.postion.dy < 0 || element.postion.dy > size.height) {dy = random.nextDouble() * size.height;}//新的位置element.postion = Offset(dx, dy);print("dx $dx dy $dy ${element.postion}");});//循环绘制所有的气泡list.forEach((element) {//画笔颜色_paint.color = element.color;//绘制圆canvas.drawCircle(element.postion, element.radius, _paint);});}@overridebool shouldRepaint(covariant CustomPainter oldDelegate) {return true;}
}
x 与 y 的偏移量计算方法如下:
///计算坐标
Offset calculateXY(double speed, double theta) {return Offset(speed * cos(theta), speed * sin(theta));
}
3 高斯模糊
再使用高斯模糊效果 覆盖到绘制的气泡上,造成一种晕晕的效果
//第三部分 高斯模糊buildBlureWidget() {return BackdropFilter(filter: ImageFilter.blur(sigmaX: 0.3, sigmaY: 0.3),child: Container(color: Colors.white.withOpacity(0.1),),);}
4 Hello World
使用 Positioned 实现的 Stack 中的顶部对齐效果
//第四部分 顶部的文字Positioned buildTopText() {//顶部对齐return Positioned(top: 120,left: 0,right: 0,child: Text('Holl World',textAlign: TextAlign.center,style: TextStyle(color: Colors.blue,fontSize: 40.0,fontWeight: FontWeight.w900,),),);}
5 输入框与按钮
最后就是表层的输入框与按钮
//第五部分 输入框与按钮Widget buildColumn(BuildContext context) {return Container(padding: EdgeInsets.all(44),child: Column(//子Widget 底部对齐mainAxisAlignment: MainAxisAlignment.end,children: <Widget>[TextFieldWidget(hintText: '邮箱',obscureText: false,prefixIconData: Icons.mail_outline,onChanged: (value) {},),SizedBox(height: 10.0,),Column(crossAxisAlignment: CrossAxisAlignment.end,children: <Widget>[TextFieldWidget(hintText: '密码',obscureText: true,prefixIconData: Icons.lock_outline,suffixIconData: Icons.visibility,),SizedBox(height: 10.0,),Text('忘记密码?',style: TextStyle(color: Theme.of(context).accentColor,),),],),SizedBox(height: 20.0,),ButtonWidget(buttonLabel: '登录',onTap: () {},hasBorder: false,),SizedBox(height: 10.0,),ButtonWidget(buttonLabel: '跳过',onTap: () {},hasBorder: true,),],),);}
6 自定义输入框
///自定义文本输入框
class TextFieldWidget extends StatelessWidget {//占位提示文本final String hintText;//输入框前置图标final IconData prefixIconData;//输入框后置图标final IconData suffixIconData;//是否隐藏文本final bool obscureText;//输入实时回调final Function onChanged;TextFieldWidget({Key key,this.hintText,this.prefixIconData,this.suffixIconData,this.obscureText,this.onChanged,}) : super(key: key);@overrideWidget build(BuildContext context) {//构建输入框return TextField(//实时输入回调onChanged: onChanged,//是否隐藏文本obscureText: obscureText,//隐藏文本小圆点的颜色cursorColor: Theme.of(context).accentColor,//文本样式style: TextStyle(color: Theme.of(context).accentColor,fontSize: 14.0,),//输入框的边框decoration: InputDecoration(//提示文本labelText: hintText,//提示文本的样式labelStyle: TextStyle(color: Theme.of(context).accentColor),//可编辑时的提示文本的颜色focusColor: Theme.of(context).accentColor,//填充filled: true,//可编辑时 无边框样式enabledBorder: UnderlineInputBorder(borderSide: BorderSide.none,),//获取输入焦点时的边框样式focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10),borderSide: BorderSide(color: Theme.of(context).accentColor),),//文本前置的图标prefixIcon: Icon(prefixIconData,size: 18,color: Theme.of(context).accentColor,),//文本后置的图标suffixIcon: GestureDetector(onTap: () {},child: Icon(suffixIconData,size: 18,color: Theme.of(context).accentColor,),),),);}
}
7 自定义按钮
///自定义按钮
class ButtonWidget extends StatelessWidget {//按钮上的文字final String buttonLabel;//是否填充背景final bool hasBorder;//点击事件回调final GestureTapCallback onTap;ButtonWidget({Key key,@required this.buttonLabel,this.hasBorder = false,this.onTap,}) : super(key: key);@overrideWidget build(BuildContext context) {return Material(child: Ink(//边框decoration: BoxDecoration(//定义填充颜色color: hasBorder ? Colors.white : Theme.of(context).accentColor,//点击事件高亮的边框圆角borderRadius: BorderRadius.circular(10),//边框设置border: hasBorder? Border.all(color: Theme.of(context).accentColor,width: 1.0,): Border.fromBorderSide(BorderSide.none),),//事件监听回调child: buildInkWell(context),),);}InkWell buildInkWell(BuildContext context) {return InkWell(//事件回调onTap: onTap,//点击的水波纹与高亮颜色 与Ink设置的背景圆角一致borderRadius: BorderRadius.circular(10),//按钮样式child: Container(height: 60.0,child: Center(child: Text(//文本内容buttonLabel,//文本样式style: TextStyle(//文本颜色color: hasBorder ? Theme.of(context).accentColor : Colors.white,//加粗fontWeight: FontWeight.w600,//文字大小fontSize: 16.0,),),),),);}
}
【x1】微信公众号的每日提醒 随时随记 每日积累 随心而过 文章底部扫码关注
【x2】各种系列的视频教程 免费开源 关注 你不会迷路
【x3】系列文章 百万 Demo 随时 复制粘贴 使用
【x4】简短的视频不一样的体验
【x5】必须有源码
周末也需要学习 Flutter 一个气泡动画背景的登录页面-倾心之作 如果你迷茫 不妨来瞅瞅 年轻人每日都会分享
不局限于思维,不局限语言限制,才是编程的最高境界。
以小编的性格,肯定是要录制一套视频的,随后会上传
有兴趣 你可以关注一下 西瓜视频 — 早起的年轻人
Flutter 气泡背景效果 仿苹果桌面运动的气泡相关推荐
- iPhone 计算机 桌面,仿苹果电脑桌面软件 仿苹果桌面软件
有什么好用的windows10仿苹果的桌面导航软件 windows还是无法象真正的苹果系统那样漂亮的,如果你装了mac的虚拟机,就知道苹果系统是如何的好了. 怎么说呢,你装了后发现,那才叫享受,接着, ...
- html仿苹果桌面导航js css,CSS简单实现弹出框、输入框等的背景幕布,模仿苹果官网导航块的半透明效果。...
需求提要 我们如果想写一个效果类似弹出框的组件,首先简单分析一下弹出框的几个特性:弹出框肯定位于当前页面的最顶端,并且在弹出框关闭之前,其他控件都无法点击.focus等. 为了更好突出弹出框的效果,除 ...
- wpf仿苹果桌面图标动画效果
开局一张图后面全靠编. 源码下载地址:https://download.csdn.net/download/musx01230/10912990
- android仿iphone苹果桌面源码拖拉排
android仿iphone苹果桌面源码拖拉排序哦 仿苹果桌面 仿iphone ios桌面 launcher 本人见市场上很少仿排序拖拉这样的算法.所以改android源码.供大家学习使用哦. 这是a ...
- android 仿苹果 小组件,仿ios14桌面小部件
仿ios14桌面小部件,这是一款面向广大安卓手机用户推出的高仿iOS14桌面插件软件,大家可以使用这款软件快速完成自己想要的桌面显示,多种插件一键点击轻松完成设置过程,让大家体验到同款iOS14桌面强 ...
- android 仿苹果 小组件,安卓仿ios14桌面小部件
安卓仿ios14桌面小部件,是一个可以让安卓手机的界面看起来像苹果界面的软件,功能非常强大,它可以提供多种不同主题的壁纸桌面,随心选择,设置后的效果还是非常不错的,很有高级感,操作简单,上手也很快. ...
- win 2016 ssh_win仿苹果模仿MAC桌面,完美高仿主题推荐
所需工具:Mac主题(后台回复1127获取) 适用系统:Win 哈喽大家好,欢迎来到蜜蜂科技f.Mac电脑一向都是追求工匠精神,独有的设计,深受用户喜欢.可是动不动就需要将近上w的售价,让很多消费者望 ...
- iphonex重量_精仿苹果iPhone X手机配置介绍
精仿苹果iPhone X手机配置介绍 [上市时间] 2017年10月最新版 [屏幕色彩] 1600万 [分 辨 率] 1920X1080 [屏幕尺寸] 5.8英寸IPS全视角电容式触摸屏 [处 理 器 ...
- android os仿ios,安卓仿ios12桌面全套仿安卓完美版
详情 安卓仿ios12桌面全套仿安卓完美版,苹果系统的仿安卓系统工具,使用便捷,安卓手机也能体验苹果系统,从桌面.主题.UI.系统内的各种细节轻松体验效果,画面流畅轻松使用启动器操作使用,喜欢的朋友快 ...
最新文章
- 收藏 | 75道常见AI面试题助你清扫知识盲点(附解析)
- golang枚举类型 - iota用法拾遗
- xml内容过多装不下,怎么实现下滑功能(最简单的下滑功能实现)
- 高职院校计算机基础课程要求,浅谈高职院校计算机的应用基础课程的改革.doc...
- 【OS学习笔记】五 VirtualBox的下载、安装和配置
- 备份mysql_mysql备份及pymysql
- Flask 框架中 上下文基础理念,包括cookie,session存储方法,requset属性,current_app模块和g模块...
- 数据-第18课-栈与递归
- Modelsim的下载及安装
- 注释 护眼色 绿色 RGB
- matlab中zeros和ones函数使用方法
- android 遥控器 地址码,RK3128平台android系统修改添加遥控器键值码值
- Justinmind 如何让自己的项目可以发布到网上,实现各个端打开网页就能看
- 为什么一个概念会非常难懂呢?人是如何理解的呢?
- 数据库常用命令及关键字
- Visual Studio 好用的插件
- Mysql迁移到GaussDb_GaussDB T 使用DUMP/LOAD导出导入迁移备份数据
- HTML+CSS+JS实现轮播效果
- (JavaScript)贪婪模式和非贪婪模式(懒惰模式)
- Redis实现点赞与关注
热门文章
- 如何从900万张图片中对600类照片进行分类,附代码
- 田忌赛马贪心算法_acm田忌赛马问题在线等急求!!
- 【机器学习】机器学习从零到掌握之二 -- 教你实现K近邻算法
- 一个实例带你搞懂Apriori关联分析算法
- 机器学习之特征工程-特征选择
- 更新版 | GPU CUDA 进阶课程
- java checkproperties(this)_【转载】java读取.properties配置文件的几种方法
- 字符串固定长度 易语言_易语言宽字符数据类型怎么设置
- win10你的组织已关闭自动更新问题怎么解决?
- 15个Linux Yum命令实例--安装/卸载/更新