flutter系列之:在flutter中使用流式布局
简介
我们在开发web应用的时候,有时候为了适应浏览器大小的调整,需要动态对页面的组件进行位置的调整。这时候就会用到flow layout,也就是流式布局。
同样的,在flutter中也有流式布局,这个流式布局的名字叫做Flow。事实上,在flutter中,Flow通常是和FlowDelegate一起使用的,FlowDelegate用来设置Flow子组件的大小和位置,通过使用FlowDelegate.paintChildre可以更加高效的进行子widget的重绘操作。今天我们来详细讲解flutter中flow的使用。
Flow和FlowDelegate
先来看下Flow的定义:
class Flow extends MultiChildRenderObjectWidget
Flow继承自MultiChildRenderObjectWidget,说它里面可以包含多个子widget。
再来看下它的构造函数:
Flow({
Key? key,
required this.delegate,
List<Widget> children = const <Widget>[],
this.clipBehavior = Clip.hardEdge,
}) : assert(delegate != null),
assert(clipBehavior != null),
super(key: key, children: RepaintBoundary.wrapAll(children));
可以看到Flow中主要有三个属性,分别是delegate,children和clipBehavior。
children很好理解了,它就是Flow中的子元素。
clipBehavior是一个Clip类型的变量,表示的是如何对widget进行裁剪。这里的默认值是none。
最后一个非常重要的属性就是FlowDelegate,FlowDelegate主要用来控制Flow中子widget的位置变换。所以,当我们在Flow中定义好子widget之后,剩下的就是定义FlowDelegate来控制如何展示这些子widget。
FlowDelegate是一个抽象类,所以我们在使用的时候,需要继承它。
FlowDelegate有几个非常重要的方法:
Size getSize(BoxConstraints constraints) => constraints.biggest;
这个方法用来定义Flow的size,对于Flow来说,它的size是和子widget的size是独立的,Flow的大小通过getSize方法来定义。
接下来是getConstraintsForChild方法:
BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;
getConstraintsForChild用来控制子widget的Constraints。
paintChildren用来控制如何绘制子widget,也是我们必须要实现的方法:
void paintChildren(FlowPaintingContext context);
FlowDelegate还有两个方法,分别用来判断是否需要Relayout和Repaint,这两个方法的参数都是FlowDelegate:
bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
bool shouldRepaint(covariant FlowDelegate oldDelegate);
Flow的应用
有了上面的介绍,我们基本上已经了解如何构建Flow了,接下来我们通过一个具体的例子来加深对Flow的理解。
在本例中,我们主要是使用Flow来排列几个图标。
首先我们定义一个图标的数组:
final List<IconData> buttonItems = <IconData>[
Icons.home,
Icons.ac_unit,
Icons.adb,
Icons.airplanemode_active,
Icons.account_box_rounded,
];
然后通过每个图标对应的IconData来构建一个IconButton的widget:
Widget flowButtonItem(IconData icon) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: IconButton(
icon: Icon(icon,
size: 50,
color: Colors.blue
),
onPressed: () {
buttonAnimation.status == AnimationStatus.completed
? buttonAnimation.reverse()
: buttonAnimation.forward();
},
)
);
}
这里我们使用的是IconButton,为了在不同IconButton之间留一些空间,我们将IconButton封装在Padding中。
在onPressed方法中,我们希望能够处理一些动画效果。这里的buttonAnimation是一个AnimationController对象:
AnimationController buttonAnimation = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
有了flowButtonItem之后,我们就可以构建Flow了:
Widget build(BuildContext context) {
return Flow(
delegate: FlowButtonDelegate(buttonAnimation: buttonAnimation),
children:
buttonItems.map<Widget>((IconData icon) => flowButtonItem(icon)).toList(),
);
}
Flow的child就是我们刚刚创建的flowButtonItem,FlowButtonDelegate是我们需要新建的类,因为之前在构建flowButtonItem的时候,我们希望进行一些动画的绘制,而FlowDelegate又是真正用来控制子Widget绘制的类,所以我们需要将buttonAnimation作为参数传递给FlowButtonDelegate。
下面是FlowButtonDelegate的定义:
class FlowButtonDelegate extends FlowDelegate {
FlowButtonDelegate({required this.buttonAnimation})
: super(repaint: buttonAnimation);
final Animation<double> buttonAnimation;
@override
bool shouldRepaint(FlowButtonDelegate oldDelegate) {
return buttonAnimation != oldDelegate.buttonAnimation;
}
@override
void paintChildren(FlowPaintingContext context) {
double dy = 0.0;
for (int i = 0; i < context.childCount; ++i) {
dy = context.getChildSize(i)!.height * i;
context.paintChild(
i,
transform: Matrix4.translationValues(
0,
dy * buttonAnimation.value,
0,
),
);
}
}
FlowButtonDelegate继承自FlowDelegate,并且传入了buttonAnimation对象。
这里我们根据buttonAnimation是否发生变化来决定是否进行Repaint。
如果需要进行Repaint,那么就要调用paintChildren的方法。
在paintChildren中,我们根据child自身的height和buttonAnimation的值来进行动画的绘制。
那么buttonAnimation的值是如何变化的呢?这就要回顾之前我们创建flowButtonItems的onPress方法了。
在onPress方法中,我们调用了buttonAnimation.reverse或者buttonAnimation.forward这两个方法来修改buttonAnimation的值。
运行之后的效果如下:

初始状态下,所有的组件都是在一起的。
当我们点击上面的图标的时候,我们可以得到下面的界面:

图标在动画中展开了。
总结
Flow是一种比较复杂的layout组件,如果和动画进行结合使用,可以得到非常完美的效果。
本文的例子:https://github.com/ddean2009/learn-flutter.git
flutter系列之:在flutter中使用流式布局的更多相关文章
- 移动web中的流式布局和viewport知识介绍
1 流式布局 其实 流式布局 就是百分比布局,通过盒子的宽度设置成百分比来根据屏幕的宽度来进行伸缩,不受固定像素的限制,内容向两侧填充. 这样的布局方式 就是移动web开发使用的常用布局方式 ...
- 自定义View(三)--实现一个简单地流式布局
Android中的流式布局也就是常说的瀑布流很是常见,不仅在很多项目中都能见到,而且面试中也有很多面试官问道,那么什么是流式布局呢?简单来说就是如果当前行的剩余宽度不足以摆放下一个控件的时候,则自动将 ...
- Flutter 流式布局列表实例+上拉加载
页面变化的几种方式: 一.StatefulWidget的setState形式 先声明两个变量. ; List<Map> list = []; 写了一个方法,获取数据: void _getH ...
- Flutter 布局类组件:流式布局(Wrap和Flow)
前言 把超出屏幕显示范围会自动折行的布局称为流式布局.Flutter中通过Wrap和Flow来支持流式布局,将Row换成Wrap后溢出部分则会自动折行. Wrap 接口描述 Wrap({ Key ke ...
- 20个Flutter实例视频教程-第12节: 流式布局 模拟添加照片效果
视频地址: https://www.bilibili.com/video/av39709290/?p=12 博客地址: https://jspang.com/post/flutterDemo.html ...
- Android 自定义View修炼-Android中常见的热门标签的流式布局的实现
一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...
- ASP.NET Core SignalR中的流式传输
什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时 ...
- 自定义流式布局:ViewGroup的测量与布局
目录 1.View生命周期以及View层级 1.1.View生命周期 1.2.View层级 2.View测量与MeasureSpec类 2.1.MeasureSpec类 2.2.父View的限制 :测 ...
- 流式布局&固定宽度&响应式&rem
我们现在在切页面布局的使用常用的单位是px,这是一个绝对单位,web app的屏幕适配有很多中做法,例如:流式布局.限死宽度,还有就是通过响应式来做,但是这些方案都不是最佳的解决方法. 1.流式布局: ...
- Android 自动换行流式布局的RadioGroup
效果图 用法 使用FlowRadioGroup代替RadioGroup 代码 import android.content.Context; import android.util.Attribute ...
随机推荐
- kibana启动停止命令
前提条件:kibana文件是属于kibana用户的 注意:路径根据具体实际情况修改 更改日志所属用户和用户组 chown kibana:kibana /usr/local/kibana-7.5.1-l ...
- 常用的清理 Kubernetes 集群资源命令
1. Kubernetes 基础对象清理 清理 Evicted 状态的 Pod kubectl get pods --all-namespaces -o wide | grep Evicted | a ...
- Kubernetes ConfigMap热更新
ConfigMap是用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中. 总结 更新 ConfigMap 后: 使用该 ConfigMap 挂载的 Env 不会同步更新 ...
- day05-离线留言和离线文件
多用户即时通讯系统05 4.编码实现04(拓展) 拓展功能: 实现离线留言,如果某个用户不在线 ,当登陆后,可以接收离线的消息 实现离线发文件,如果某个功能没有在线,当登录后,可以接收离线的文件 4. ...
- 基于python的MD5脚本
摘要 鉴于网上的各大MD5爆破网站,当网络差时访问速度慢,至此小弟写了个基于python的MD5爆破脚本,欢迎各位师傅在评论区留下您们宝贵的意见. 开发思路 1.通过 string模块 自动生成字典: ...
- 组件化开发2-安装cocoaPods
第一步:安装ruby 不能一上来就换ruby源.虽然mac自带了ruby,但是版本一般都偏低,如果不进行更新会导致版本依赖问题. 这里使用rvm来管理ruby,它允许共存多个ruby.RVM:Ruby ...
- Java 泛型程序设计
1. 泛型类 public class Pair<T> { private T first; private T second; public void setSecond(T seco ...
- Docker搭建kafka及监控
环境安装 docker安装 yum update yum install docker # 启动 systemctl start docker # 加入开机启动 systemctl enable do ...
- 聊聊Linux中CPU上下文切换
目录 什么是CPU上下文 CPU上下文切换 上一任务的CPU上下文保存在哪? 进程上下文切换 内核空间和用户空间 top命令查看CPU资源 系统调用 进程上下文切换 和 系统调用的区别? 进程切换的常 ...
- java的分页原理详解
首先先创建一个Student实体类. import java.io.Serializable; import java.util.Map; public class Student implement ...