flutter系列之:UI layout简介(flutter ui设计)
wptr33 2025-04-11 08:29 54 浏览
简介
对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了。布局的英文名叫做layout,就是用来描述如何将组件进行摆放的一个约束。
在flutter中,基本上所有的对象都是widget,对于layout来说也不例外。也就是说在flutter中layout也是用代码来完成的,这和其他的用配置文件来描述layout的语言有所不同。
你可以把layout看做是一种看不见的widget,这些看不见的widget是用来作用在可见的widget对象上,给他们实施一些限制。
flutter中layout的分类
flutter中的layout widget有很多,他们大概可以分为三类,分别是只包含一个child的layout widget,可以包含多个child的layout widget和可滑动的Sliver widgets。
这三种layout也有很多种具体的实现,对于Single-child layout widgets来说,包含下面这些widgets:
- Align — 用来对其包含在其中的组件进行对其操作。
- AspectRatio — 对其中的组件进行比例缩放。
- Baseline — 通过使用子组件的baseline来进行定位。
- Center — 自组件位于中间。
- ConstrainedBox — 类似于IOS中的constrain,表示子组件的限制条件。
- Container — 一个常用的widget,可以用来包含多个其他的widget。
- CustomSingleChildLayout — 将其单个子项的布局推迟。
- Expanded — 将Row, Column 或者 Flex的child进行扩展。
- FittedBox — 根据fit来缩放和定位其child。
- FractionallySizedBox — 将child按照总可用空间进行调整。
- IntrinsicHeight — 一个将其child调整为child固有高度的小部件。
- IntrinsicWidth — 一个将其child调整为child固有宽度的小部件。
- LimitedBox — 限制一个box的size。
- Offstage — 将child放入render tree中,但是却并不触发任何重绘。
- OverflowBox — 允许child覆盖父组件的限制。
- Padding — 为child提供padding。
- SizedBox — 给定size的box。
- SizedOverflowBox — 可以覆盖父组件限制的box。
- Transform — 子组件可以变换。
以上是包含单个child的layout组件,下面是可以包含多个child的layout组件:
- Column — 表示一列child。
- CustomMultiChildLayout — 使用代理来定位和缩放子组件。
- Flow — 流式布局。
- GridView — 网格布局。
- IndexedStack — 从一系列的child中展示其中的一个child。
- LayoutBuilder — 可以依赖父组件大小的widget tree。
- ListBody — 根据给定的axis来布局child。
- ListView — 可滚动的列表。
- Row — 表示一行child。
- Stack — 栈式布局的组件。
- Table — 表格形式的组件。
- Wrap — 可以对子child进行动态调整的widget。
可滑动的Sliver widgets有下面几种:
- CupertinoSliverNavigationBar — 是一种IOS风格的导航bar。
- CustomScrollView — 可以自定义scroll效果的ScrollView。
- SliverAppBar — material风格的app bar,其中包含了CustomScrollView。
- SliverChildBuilderDelegate — 使用builder callback为slivers提供child的委托。
- SliverChildListDelegate — 使用list来为livers提供child的委托。
- SliverFixedExtentList — 固定axis extent的sliver。
- SliverGrid — child是二维分布的sliver。
- SliverList — child是线性布局的sliver。
- SliverPadding — 提供padding的sliver。
- SliverPersistentHeader — 可变size的sliver。
- SliverToBoxAdapter — 包含单个box widget的Sliver。
常用layout举例
上面我们列出了所有的flutter layout,他们几乎满足了我们在程序中会用到的所有layout需求,这里我们以两个最基本和最常用的layout:Row和Column为例,来详细讲解layout的使用。
Row和Column都属于上面讲到的多个child的layout widget,它里面可以包含多个其他的widget组件。
先看一下Row和column的定义。
class Row extends Flex {
Row({
Key? key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection? textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline? textBaseline, // NO DEFAULT: we don't know what the text's baseline should be
List children = const [],
}) : super(
children: children,
key: key,
direction: Axis.horizontal,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
}
class Column extends Flex {
Column({
Key? key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection? textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline? textBaseline,
List children = const [],
}) : super(
children: children,
key: key,
direction: Axis.vertical,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
}
可以看到Row和Column都继承自Flex,并且他们的构造方法都是调用了Flex的构造方法,两者的区别就在于direction不同,Row的direction是Axis.horizontal,而Column的direction是Axis.vertical。
那么什么是Flex呢?
Flex是一个widget,在Flex中的子组件会按照某一个指定的方向进行展示。这个方向是可以控制的,比如横向或者竖向,如果你已经提前知道了主轴的方向,那么可以使用Row或者Column来替代Flex,因为这样更加简洁。
在Flex中,如果想要child在某个方向填满可用空间,则可以将该child包装在Expanded中。
要注意的是,Flex是不可滚动的,如果Flex中的child太多,超出了Flex中的可用空间,那么Flex将会报错,所以如果你需要展示很多child的情况下,可以考虑使用可滚动的组件,比如ListView。
如果你只有一个child,那么就没有必要使用Flex或者Row和Column了,可以考虑使用Align或者Center来对child进行定位。
在Flex中有几个非常重要的参数,比如mainAxisAlignment表示的是子组件沿主轴方向的排列规则,mainAxisSize表示的是主轴的size大小,crossAxisAlignment表示的是和主轴垂直轴的子组件排列规则。当然还有它最最重要的children属性,children是一个Widget的list列表,用来存储要展示的子组件。
以Row为例,我们创建一个简单的RowWidget:
class RowWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
textDirection: TextDirection.ltr,
children: [
YellowBox(),
YellowBox(),
YellowBox(),
],
);
}
}
这里我们返回了一个Row对象,设置了textDirection和children属性。
children里面是自定义的YellowBox:
class YellowBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.yellow,
border: Border.all(),
),
);
}
}
YellowBox是一个长和宽都是50的正方形。我们这里使用了BoxDecoration对其上色。
最后将RowWidget放到Scaffold的body里面,如下所示:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: RowWidget()
);
}
我们可以看到下面的图像:
大家可以看到YellowBox是紧贴在一起的,如果我们想要均匀分别该如何做呢?
我们可以在Row中添加一个属性叫做mainAxisAlignment,取值如下:
mainAxisAlignment: MainAxisAlignment.spaceEvenly
重新运行,生成的图像如下:
上面我们还提到了一个Expanded组件,可以用来填充剩余的可用空间,我们把最后一个YellowBox用Expanded围起来,如下所示:
return Row(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
YellowBox(),
YellowBox(),
Expanded(
child: YellowBox(),
)
],
);
生成的图像如下:
可以看到最后一个Box填充到了整个Row剩余的空间。
大家要注意,这时候mainAxisAlignment是没有效果的。
如果观察Expanded的构造函数,可以看到Expanded还有一个flex属性:
const Expanded({
Key? key,
int flex = 1,
required Widget child,
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
flex属性表示的是flex factor,默认值是1,还是上面的例子,我们将flex调整为2,看看效果:
children: [
YellowBox(),
YellowBox(),
Expanded(
flex:2,
child: YellowBox(),
)
],
运行的结果和flex=1是一样的,为什么呢?
事实上这个flex表示的是相对于其他Expanded的组件所占用的空间比例。我们可以讲所有的子组件都用Expanded进行扩充,然后再看看效果:
children: [
Expanded(
child: YellowBox(),
),
Expanded(
child: YellowBox(),
),
Expanded(
flex: 2,
child: YellowBox(),
)
],
运行结果如下:
可以看到最后一个child占用的空间是前面两个的两倍。
如果我们想要在YellowBox中间添加空格怎么办呢?有两种方法。
第一种方法是使用SizedBox,如下:
children: [
Expanded(
child: YellowBox(),
),
SizedBox(
width: 100,
),
Expanded(
child: YellowBox(),
),
Expanded(
flex: 2,
child: YellowBox(),
)
],
SizedBox里面可以包含子child,从而重新设置子child的长度和高度。如果不包含子child则会生成一个空格。
还有一种方式是使用Spacer,如下所示:
children: [
Expanded(
child: YellowBox(),
),
Spacer(flex: 2),
Expanded(
child: YellowBox(),
),
Expanded(
flex: 2,
child: YellowBox(),
)
],
生成的图像如下:
Spacer和SizedBox都可以生成空白,不同的是Spacer可以和flex一起使用,而SizedBox必须固定size大小。
总结
以上就是fluter中layout和的分类和基本layout Row和Column的使用情况了。
本文的例子:
https://github.com/ddean2009/learn-flutter.git
更多内容请参考
http://www.flydean.com/07-flutter-ui-layout-overview/最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
-
- oracle数据导入导出_oracle数据导入导出工具
- 继续学习Python中的while true/break语句
- python continue和break的区别_python中break语句和continue语句的区别
- 简单学Python——关键字6——break和continue
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
- Python 中 break 和 continue 傻傻分不清
- python中的流程控制语句:continue、break 和 return使用方法
- L017:continue和break - 教程文案
- 作为前端开发者,你都经历过怎样的面试?
- 面试被问 const 是否不可变?这样回答才显功底
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
