【Flutter学习】页面布局之列表和表格处理
一,概述
Flutter
中拥有30多种预定义的布局widget
,常用的有Container
、Padding
、Center
、Flex
、Row
、Colum
、ListView
、GridView
。按照《Flutter技术入门与实战》上面来说的话,大概分为四类
- 基础布局组件:Container(容器布局),Center(居中布局),Padding(填充布局),Align(对齐布局),Colum(垂直布局),Row(水平布局),Expanded(配合Colum,Row使用),FittedBox(缩放布局),Stack(堆叠布局),overflowBox(溢出父视图容器)。
- 宽高尺寸处理:SizedBox(设置具体尺寸),ConstrainedBox(限定最大最小宽高布局),LimitedBox(限定最大宽高布局),AspectRatio(调整宽高比),FractionallySizedBox(百分比布局)
- 列表和表格处理:ListView(列表),GridView(网格),Table(表格)
- 其它布局处理:Transform(矩阵转换),Baseline(基准线布局),Offstage(控制是否显示组件),Wrap(按宽高自动换行布局)
二,列表和表格处理布局组件
- ListView(列表)
- 介绍
ListView是一个非常常用的控件,涉及到数据列表展示的,一般情况下都会选用该控件。ListView跟GridView相似,基本上是一个slivers里面只包含一个SliverList的CustomScrollView。 - 布局行为
ListView在主轴方向可以滚动,在交叉轴方向,则是填满ListView。 - 继承关系
- Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > ScrollView > BoxScrollView > ListView
看继承关系可知,这是一个组合控件。ListView跟GridView类似,都是继承自BoxScrollView。
- 构造函数
- ListView({
- Key key,
- Axis scrollDirection = Axis.vertical,
- bool reverse = false,
- ScrollController controller,
- bool primary,
- ScrollPhysics physics,
- bool shrinkWrap = false,
- EdgeInsetsGeometry padding,
- this.itemExtent,
- bool addAutomaticKeepAlives = true,
- bool addRepaintBoundaries = true,
- double cacheExtent,
- List<Widget> children = const <Widget>[],
- })
同时也提供了如下额外的三种构造方法,方便开发者使用。
- ListView.builder
- ListView.separated
- ListView.custom
- ListView({
使用场景
ListView使用场景太多了,一般涉及到列表展示的,一般都会选择ListView。
但是需要注意一点,ListView的标准构造函数适用于数目比较少的场景,如果
数目比较多
的话,最好使用ListView.builder
。ListView的标准构造函数会将所有item一次性创建,而ListView.builder会创建滚动到屏幕上显示的item。
- 参数解析
ListView大部分属性同GridView,想了解的读者可以看一下下面所写的GridView相关的内容。这里只介绍一个属性
itemExtent:ListView在滚动方向上每个item所占的高度值。
- 介绍
- GridView(网格)
- 介绍
- GridView在移动端上非常的常见,就是一个滚动的多列列表,实际的使用场景也非常的多。
- 布局行为
- GridView的布局行为不复杂,本身是尽量占满空间区域,布局行为上完全继承自ScrollView。
- 继承关系
- Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > ScrollView > BoxScrollView > GridView
- 构造函数
- GridView({
- Key key,
- Axis scrollDirection = Axis.vertical,
- bool reverse = false,
- ScrollController controller,
- bool primary,
- ScrollPhysics physics,
- bool shrinkWrap = false,
- EdgeInsetsGeometry padding,
- @required this.gridDelegate,
- bool addAutomaticKeepAlives = true,
- bool addRepaintBoundaries = true,
- double cacheExtent,
- List<Widget> children = const <Widget>[],
- })
同时也提供了如下额外的四种构造方法,方便开发者使用。
- GridView.builder
- GridView.custom
- GridView.count
- GridView.extent
- GridView({
- 参数解析
scrollDirection:滚动的方向,有垂直和水平两种,默认为垂直方向(Axis.vertical)。
reverse:默认是从上或者左向下或者右滚动的,这个属性控制是否反向,默认值为false,不反向滚动。
controller:控制child滚动时候的位置。
primary:是否是与父节点的PrimaryScrollController所关联的主滚动视图。
physics:滚动的视图如何响应用户的输入。
shrinkWrap:滚动方向的滚动视图内容是否应该由正在查看的内容所决定。
padding:四周的空白区域。
gridDelegate:控制GridView中子节点布局的delegate。
cacheExtent:缓存区域。
- 介绍
- Table(表格)
- 介绍
- 每一种移动端布局中都会有一种table布局,这种控件太常见了。至于其表现形式,完全可以借鉴其他移动端的,通俗点讲,就是表格。
- 布局行为
表格的每一行的高度,由其内容决定,每一列的宽度,则由columnWidths属性单独控制。
- 继承关系
- Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > Table
- 构造函数
- Table({
- Key key,
- this.children = const <TableRow>[],
- this.columnWidths,
- this.defaultColumnWidth = const FlexColumnWidth(1.0),
- this.textDirection,
- this.border,
- this.defaultVerticalAlignment = TableCellVerticalAlignment.top,
- this.textBaseline,
- })
- Table({
- 参数解析
columnWidths:设置每一列的宽度。
defaultColumnWidth:默认的每一列宽度值,默认情况下均分。
textDirection:文字方向,一般无需考虑。
border:表格边框。
defaultVerticalAlignment:每一个cell的垂直方向的alignment。
总共包含5种:
- top:被放置在的顶部;
- middle:垂直居中;
- bottom:放置在底部;
- baseline:文本baseline对齐;
- fill:充满整个cell。
textBaseline:defaultVerticalAlignment为baseline的时候,会用到这个属性。
- 介绍
三,常用示例
- ListView(列表)
- /**
- * ListView
- * 第一个展示四行文字
- */
- class MyListView extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- // TODO: implement build
- return new ListView(
- shrinkWrap: true,
- padding: EdgeInsets.all(20.0),
- children: <Widget>[
- new Text('I\m dedicationg every day to you'),
- new Text('Domestic life was never quite my style'),
- new Text('When you smile, you knock me out, I fall apart'),
- new Text('And I thought I was so smart')
- ],
- );
- }
- }
效果图:
源码解析:- @override
- Widget buildChildLayout(BuildContext context) {
- if (itemExtent != null) {
- return new SliverFixedExtentList(
- delegate: childrenDelegate,
- itemExtent: itemExtent,
- );
- }
- return new SliverList(delegate: childrenDelegate);
- }
ListView标准构造布局代码如上所示,底层是用到的SliverList去实现的。ListView是一个slivers里面只包含一个SliverList的CustomScrollView。源码这块儿可以参考GridView,在此不做更多的说明。
- /**
- GridView(网格)
- /**
- * GridView
- * 代码直接用了Creating a Grid List中的例子,创建了一个2列总共100个子节点的列表。
- */
- class MyGridView extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- // TODO: implement build
- return new GridView.count(
- crossAxisCount: ,
- children: List.generate(, (index){
- return new Center(
- child: new Text(
- 'Item $index',
- style: Theme.of(context).textTheme.headline,
- ),
- );
- },
- ),
- );
- }
- }
效果图
源码解析:
- @override
- Widget build(BuildContext context) {
- final List<Widget> slivers = buildSlivers(context);
- final AxisDirection axisDirection = getDirection(context);
- final ScrollController scrollController = primary
- ? PrimaryScrollController.of(context)
- : controller;
- final Scrollable scrollable = new Scrollable(
- axisDirection: axisDirection,
- controller: scrollController,
- physics: physics,
- viewportBuilder: (BuildContext context, ViewportOffset offset) {
- return buildViewport(context, offset, axisDirection, slivers);
- },
- );
- return primary && scrollController != null
- ? new PrimaryScrollController.none(child: scrollable)
- : scrollable;
- }
上面这段代码是ScrollView的build方法,GridView就是一个特殊的ScrollView。GridView本身代码没有什么,基本上都是ScrollView上的东西,主要会涉及到Scrollable、Sliver、Viewport等内容,这些内容比较多,因此源码就先略了,后面单独出一篇文章对ScrollView进行分析吧。
- /**
- Table(表格)
- Table(
- columnWidths: const <int, TableColumnWidth>{
- : FixedColumnWidth(50.0),
- : FixedColumnWidth(100.0),
- : FixedColumnWidth(50.0),
- : FixedColumnWidth(100.0),
- },
- border: TableBorder.all(color: Colors.red, width: 1.0, style: BorderStyle.solid),
- children: const <TableRow>[
- TableRow(
- children: <Widget>[
- Text('A1'),
- Text('B1'),
- Text('C1'),
- Text('D1'),
- ],
- ),
- TableRow(
- children: <Widget>[
- Text('A2'),
- Text('B2'),
- Text('C2'),
- Text('D2'),
- ],
- ),
- TableRow(
- children: <Widget>[
- Text('A3'),
- Text('B3'),
- Text('C3'),
- Text('D3'),
- ],
- ),
- ],
- )
效果图:
(1)样例其实并不复杂,FlowDelegate需要自己实现child的绘制,其实大多数时候就是位置的摆放。上面例子中,对每个child按照给定的margin值,进行排列,如果超出一行,则在下一行进行布局。
(2)另外,对这个例子多做一个说明,对于上述child宽度的变化,这个例子是没问题的,如果每个child的高度不同,则需要对代码进行调整,具体的调整是换行的时候,需要根据上一行的最大高度来确定下一行的起始y坐标。源码解析:
我们直接来看其布局源码:
第一步,当行或者列为0的时候,将自身尺寸设为0x0。
- if (rows * columns == ) {
- size = constraints.constrain(const Size(0.0, 0.0));
- return;
- }
第二步,根据textDirection值,设置方向,一般在阿拉伯语系中,一些文本都是从右往左现实的,平时使用时,不需要去考虑这个属性。
- switch (textDirection) {
- case TextDirection.rtl:
- positions[columns - ] = 0.0;
- for (int x = columns - ; x >= ; x -= )
- positions[x] = positions[x+] + widths[x+];
- _columnLefts = positions.reversed;
- tableWidth = positions.first + widths.first;
- break;
- case TextDirection.ltr:
- positions[] = 0.0;
- for (int x = ; x < columns; x += )
- positions[x] = positions[x-] + widths[x-];
- _columnLefts = positions;
- tableWidth = positions.last + widths.last;
- break;
- }
第三步,设置每一个cell的尺寸。
- for (int x = ; x < columns; x += ) {
- final int xy = x + y * columns;
- final RenderBox child = _children[xy];
- if (child != null) {
- final TableCellParentData childParentData = child.parentData;
- childParentData.x = x;
- childParentData.y = y;
- switch (childParentData.verticalAlignment ?? defaultVerticalAlignment) {
- case TableCellVerticalAlignment.baseline:
- child.layout(new BoxConstraints.tightFor(width: widths[x]), parentUsesSize: true);
- final double childBaseline = child.getDistanceToBaseline(textBaseline, onlyReal: true);
- if (childBaseline != null) {
- beforeBaselineDistance = math.max(beforeBaselineDistance, childBaseline);
- afterBaselineDistance = math.max(afterBaselineDistance, child.size.height - childBaseline);
- baselines[x] = childBaseline;
- haveBaseline = true;
- } else {
- rowHeight = math.max(rowHeight, child.size.height);
- childParentData.offset = new Offset(positions[x], rowTop);
- }
- break;
- case TableCellVerticalAlignment.top:
- case TableCellVerticalAlignment.middle:
- case TableCellVerticalAlignment.bottom:
- child.layout(new BoxConstraints.tightFor(width: widths[x]), parentUsesSize: true);
- rowHeight = math.max(rowHeight, child.size.height);
- break;
- case TableCellVerticalAlignment.fill:
- break;
- }
- }
- }
第四步,如果有baseline则进行相关设置。
- if (haveBaseline) {
- if (y == ) _baselineDistance = beforeBaselineDistance;
- rowHeight = math.max(rowHeight, beforeBaselineDistance + afterBaselineDistance);
- }
第五步,根据alignment,调整child的位置。
- for (int x = ; x < columns; x += ) {
- final int xy = x + y * columns;
- final RenderBox child = _children[xy];
- if (child != null) {
- final TableCellParentData childParentData = child.parentData;
- switch (childParentData.verticalAlignment ?? defaultVerticalAlignment) {
- case TableCellVerticalAlignment.baseline:
- if (baselines[x] != null)
- childParentData.offset = new Offset(positions[x], rowTop + beforeBaselineDistance - baselines[x]);
- break;
- case TableCellVerticalAlignment.top:
- childParentData.offset = new Offset(positions[x], rowTop);
- break;
- case TableCellVerticalAlignment.middle:
- childParentData.offset = new Offset(positions[x], rowTop + (rowHeight - child.size.height) / 2.0);
- break;
- case TableCellVerticalAlignment.bottom:
- childParentData.offset = new Offset(positions[x], rowTop + rowHeight - child.size.height);
- break;
- case TableCellVerticalAlignment.fill:
- child.layout(new BoxConstraints.tightFor(width: widths[x], height: rowHeight));
- childParentData.offset = new Offset(positions[x], rowTop);
- break;
- }
- }
- }
最后一步,则是根据每一行的宽度以及每一列的高度,设置Table的尺寸。
- size = constraints.constrain(new Size(tableWidth, rowTop));
最后梳理一下整个的布局流程:
- 当行或者列为0的时候,将自身尺寸设为0x0;
- 根据textDirection进行相关设置;
- 设置cell的尺寸;
- 如果设置了baseline,则进行相关设置;
- 根据alignment设置cell垂直方向的位置;
- 设置Table的尺寸。
- 如果经常关注系列文章的读者,可能会发现,布局控件的布局流程基本上跟上述流程是相似的。
- Table(
四,参考
【Flutter学习】页面布局之列表和表格处理的更多相关文章
- 前端学习笔记--CSS样式--列表和表格
1.列表 2.表格 odd:奇数 even:偶数
- flutter column row布局的列表自适应宽高
mainAxisSize: MainAxisSize.min
- 【Flutter学习】页面布局之其它布局处理
一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...
- 【Flutter学习】页面布局之宽高尺寸处理
一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...
- 【Flutter学习】页面布局之基础布局组件
一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...
- Flutter学习六之实现一个带筛选的列表页面
上期实现了一个网络轮播图的效果,自定义了一个轮播图组件,继承自StatefulWidget,我们知道Flutter中并没有像Android中activity的概念.页面见的跳转是通过路由从一个全屏组件 ...
- CSS3与页面布局学习总结(四)——页面布局大全
一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...
- CSS3与页面布局学习总结(四)——页面布局大全BFC、定位、浮动、7种垂直居中方法
目录 一.BFC与IFC 1.1.BFC与IFC概要 1.2.如何产生BFC 1.3.BFC的作用与特点 二.定位 2.2.relative 2.3.absolute 2.4.fixed 2.5.z- ...
- Flutter学习笔记(23)--多个子元素的布局Widget(Rwo、Column、Stack、IndexedStack、Table、Wrap)
如需转载,请注明出处:Flutter学习笔记(23)--多个子元素的布局Widget(Rwo.Column.Stack.IndexedStack.Table.Wrap) 上一篇梳理了拥有单个子元素布局 ...
随机推荐
- 双十一高并发场景背后的数据库RDS技术揭秘
[战报]11月11日聚石塔(阿里云数据库RDS产品形态)峰值QPS突破X00w,Proxy 峰值QPS超过X00w. 双十一就要来了,全世界都为其疯狂,但是在双十一抢购中经常会出现几万人抢一个红包或者 ...
- 【LeetCode 75】颜色分类
题目链接 [题解] 维护一个左边界l和一个右边界r 其中0..l-1都是'0' 而 r+1..n-1都是'2' 我们令i=l;i<=r; 枚举每一个a[i]; ①如果a[i]=2.那么把a[i] ...
- 【Linux】shell脚本参数传递
这里介绍参数传递的两种方式. 方式一:$0,$1,$2... 采用$0,$1,$2..等方式获取脚本命令行传入的参数 $0:脚本名称 $1....: 参数 例子: #编写一个shell $ vim t ...
- 10.14.1-linux设置时间等
设置时间[root@wen /]# date -s "20171014 15:42:00"2017年 10月 14日 星期六 15:42:00 CST 格式化时间[root@wen ...
- python中得字典和常用函数总结
字典是python中一种常见得数据类型,用{}表示,并且以键值对得形式存放数据. dic={},其中得key键值是不可变得,类型可以是字符串.其中,列表,字典不可以作为键,键值是不可变得.字符串,元组 ...
- iPad如何恢复
iPad在有网络连接的情况下,可以通过iCloud进行恢复. 没有连接WiFi的情况下,只能通过USB连接才能恢复. ①下载最新版本的iTunes:https://support.apple.com/ ...
- 【原】webpack--loaders,主要解释为什么需要loaders和注意事项
Why需要loaders? webpack开箱即用只支持JS和JSON两种文件类型,但是比如css.less,还有目前市场上比较新的语法糖jsx,怎么处理呢? 通过Loaders去支持其他文件类型并且 ...
- Php安装时出现的问题处理
问题从这里开始,我们一步一步说明: cd /usr/local/src/ tar zxvf php-5.5.6.tar.gz cd php-5.5.6 ./configure \ //执行当前目录下软 ...
- 提高redis cluster集群的安全性,增加密码验证
节点设置密码 1.修改配置文件 在配置文件里面增加密码选项,一定要加上masterauth,不然Redirected的时候会失败. masterauth redispassword requirepa ...
- 洛谷P3366 【模板】最小生成树(LCT)
[模板]最小生成树 题目传送门 解题思路 用LCT来维护最小生成树. 除了把各顶点作为节点外,每条边也都视为一个节点.对于要加入的边\(e\),检查其两顶点\(x\)和\(y\)是否在同一棵树中,如果 ...