前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 视图布局(二)

Flutter 视图布局(二)

原创
作者头像
林小帅
修改2019-09-11 09:49:22
2.9K0
修改2019-09-11 09:49:22
举报
文章被收录于专栏:林小帅的专栏林小帅的专栏

各位少侠、小伙伴们大家好!

how old are you ? 没错又是我!

在 Flutter 视图布局(一)中文章结束时留下了一个问题,大家有尝试去实现吗?

如果大家认真看文章的话,我觉得这并不是很难的东西。

当然如果有配合 github 项目的代码来看的话,一定会发现我也已经将实现好的代码也更新上去了,可以作为实现参考。

好,那么我们就废话不多说,这次我们就来说道说道 ListBodyListView 这两个常用的布局 List Widget。

在此之前我们还是要说说 Flutter 的包管理方式,因为这是开发中必不可少的绕不开的一部分。

在 MyApp 项目目录下有个 pubspec.yaml 文件,这个文件主要是 Flutter 用于管理外部依赖项

YAML 是一个标记性语言,它对大小写敏感,由于不像其他类型文件的数据格式拥有明显的父、子级标记而是默认使用空格缩进(2个空格)代表层级,比如用“- ”(中划线+空格)来表示列表。

当然,在在默认的文件中也有示例说明,这就需要你自己去打开文件看一看啦。

在默认的文件情况下我们可以看到一级分类由以下类型组成。现在我们从上到下来分别解释一下这些东西到底是干什么的:

  1. name 项目名称
  2. description 简介
  3. version 版本号
  4. environment 环境,表示 SDK 版本
  5. dependencies 依赖项
  6. dev_dependencies 开发依赖项
  7. flutter 所需资源文件引入

然后现在我们先在 dependencies 中加入 english_words,这个英文单词的包主要是用于后续的例子中,可以先考虑引入。

english_words: ^3.1.0

在添加完新的依赖包后,当你进行保存时 VS Code 会自动进行依赖包的更新和下载,还是比较方便的,就不需要手动进行更新命令了。

flutter packages get
flutter pub get

ok,接下来我们可以说说 ListBody 和 ListView 了

配合文章一同食用的代码已同步更新到 Github 地址:

https://github.com/linxsbox/myapp.git

01 - ListBody

我们先来看看 ListBody 的源码部分

ListBody 源码部分
ListBody 源码部分

这不看都不知道,相比 Row、Column 来说简直是太简单了:

  • Axis mainAxis 主轴,默认垂直方向,即 y 轴
  • bool reverse 是否反向/颠倒顺序的
  • List<Widget> children 子元素列表 Widget 类型

都看到这了,才三个属性,那还等什么当然是上手就干啊!(我的嘴角微微上翘,噼里啪啦一顿猛敲……)

我的内心毫无波澜甚至还有些想笑
我的内心毫无波澜甚至还有些想笑

看着代码完成了,也没有明显报错,这很OK,召唤控制台 - 输入 - R

发生了什么?
发生了什么?

这是怎么回事?发生了什么!?

冷静一下不要慌,让我们来看看源码。

ListBody 源码注释部分
ListBody 源码注释部分

看完之后发现,原来 ListBody 是一个可以设定轴方向的 多子元素列表,但是需要一个可以强制范围的容器来装载它。而且这是一个很少能够直接使用的 Widget,如果需要的话应该优先选择 ListView,因为它有相同的布局方式以及提供了滚动行为。

(摸着下巴若有所思)OK,那我们就来把他放在 ListView 下。

ListBody 放在 ListView 下
ListBody 放在 ListView 下

这样就没什么问题了,正好顺便可以来说说 ListVIew。

02 - ListView

关于 ListView 还是要先认真看下源码,这次可不能那么鲁莽。

仔细一看,这属性还挺多。不着急,那我们分别都来看一看。

  • Axis scrollDirection 滚动的方向,即轴方向,Axis.vertical 垂直方向 和 Axis.horizontal 水平方向,默认为垂直方向
  • bool reverse 是否反向/颠倒顺序的,默认为 false,如为 true 则 垂直方向从底部开始,水平方向从右边开始
  • bool primary 是否是主主要的滚动 Widget,默认为 false, 如果为 true 则 controller 必须为 null
  • bool shrinkWrap 是否收缩滚动视图
  • EdgeInsetsGeometry padding 顾名思义填充的内边距
  • ScrollController controller 滚动事件,与 primary 互斥
  • ScrollPhysics physics 滚动的行为方式
  • bool addAutomaticKeepAlives
  • bool addRepaintBoundaries
  • bool addSemanticIndexes
  • double cacheExtent
  • int semanticChildCount
  • DragStartBehaviordragStartBehavior
  • List<Widget> children 子元素列表 Widget 类型

reverse

reverse 就是将列表的渲染方式是否是反向,垂直方向从底部开始,水平方向从右边开始

reverse 反向渲染/颠倒列表
reverse 反向渲染/颠倒列表

controller

关于滚动事件,如果真要说的话,那么篇幅就太长了,所以这里暂时不讲,后续会将一些 Widget 的事件 整理出来。

如果各位少侠小伙伴们有兴趣的,可以先看看这个滚动事件参考:

https://book.flutterchina.club/chapter6/scroll_controller.html

https://api.flutter.dev/flutter/widgets/ScrollView/controller.html

当 primary 为 true 时 则会 喜提满屏红。

primary 与 controller 互斥
primary 与 controller 互斥

在源码中有这样一段:如果 primary 为 true 则 controller 必须为 null,controller 滚动事件,与 primary 互斥。

addAutomaticKeepAlives

是否将子项都装在 AutomaticKeepAlive 中,默认为 true。

addAutomaticKeepAlives 源码部分说明

简单来说(翻译一下),通常列表是懒惰的,将子类元素装在 AutomaticKeepAlive 中,以便其子级元素可以使用 KeepAliveNotification 来保留状态,否则它们在屏幕外将被回收。

如果需要手动维护子类元素的子级元素那么就必须禁用此功能(false)(以及 addRepaintBoundaries 设为 false)。

再简单来说,就是子元素可以超出屏幕之外还继续保留,但是这个状态的保留由框架负责。如果你需要自己决定如何保留子元素的状态,那么就把 addAutomaticKeepAlives 和 addRepaintBoundaries 关了自己写去。

addRepaintBoundaries

是否将子项都装在 RepaintBoundary 中,默认为 true。

addRepaintBoundaries 源码部分说明
addRepaintBoundaries 源码部分说明

简单来说(翻译一下),通常在可滚动列表的容器中子项都会被装在重绘边界之内,以便列表在滚动时不需要将它们进行重绘。如果是简单的子项内容(纯色块或者短文本),则关闭addRepaintBoundaries(false)让其重绘子项可能会更有效率。

简单来说,不能再简单了,请少侠自己思考。

addSemanticIndexes

是否将子项都装在 IndexedSemantics 中,默认依然为 true。

addSemanticIndexes 源码部分说明
addSemanticIndexes 源码部分说明

cacheExtent

在视图可见区域之外有一个区域(即垂直是上下部分,水平是左右部分),用于缓存滚动即进入可见区域的子类。

进入此缓存区域的子项在即使未在可见视图内也是可见的,即是进入可见区域后就会被布局渲染,cacheExtent 主要是用于描述该区域所延伸的大小。

physics

physics 主要是 滚动的物理效果

  • ClampingScrollPhysics 默认的钳位效果
  • BouncingScrollPhysics 回弹的物理效果
  • FixedExtentScrollPhysics 拨轮式的物理效果
  • AlwaysScrollableScrollPhysics 始终可以滚动效果
  • NeverScrollableScrollPhysics 禁止滚动效果

AlwaysScrollableScrollPhysics 和 NeverScrollableScrollPhysics 就不用演示效果了,毕竟这个意思和 CSS 中 overflow 的 scroll 和 hidden 一个意思。

ClampingScrollPhysics 我也不知道为什么要用 Clamping,可能是像钳子一样拥有最大张合度吧。在默认情况下,如果列表子元素不足以超出可视范围则不会产生可滚动行为。如超出可视范围则到达列表尽头时会停留并有水波样式出现。

ClampingScrollPhysics 的滚动效果
ClampingScrollPhysics 的滚动效果

BouncingScrollPhysics 的话就是大家都熟悉的回弹效果了,当操作列表到达可视范围尽头时还可以继续超出一定的空间,当失去焦点后回到尽头的位置,这样就能给予用户一个良好的使用体验。一般来说都会在下拉刷新上拉加载这样的场景里使用。

BouncingScrollPhysics 的滚动效果
BouncingScrollPhysics 的滚动效果

FixedExtentScrollPhysics 是类似拨轮的效果,怎么说呢,这个用文字还真不好描述效果,看一张实物图大概就能理解了。

FixedExtentScrollPhysics 的滚动效果
FixedExtentScrollPhysics 的滚动效果

以上就是 ListView 属性的使用说明了,但是你可能会问了,这些子元素你写那么多不现实啊,真正使用到的时候肯定都是按需生成的,不然如果有很多子元素不可能都 copy paste一遍吧?

03 - 无线滚动例子

很好,我很佩服你提问的勇气!不过没关系,Flutter 让然也知道这个问题,那么我们就来看看它有哪些相关的方法可以使用。

不用多说,我们还是来先看源码。

ListView 源码注释说明
ListView 源码注释说明

源码中说到 ListView 有4中设置子元素的方式:

  • List<Widget>
  • ListView.builder
  • ListView.separated
  • ListView.custom

第一种 List<Widget> 就不用多说了,我们常用的直接写在列表里的方式。另外的三种方式就需要我们编码去实现了。

需要编码的三个构造函数都拥有相同的属性这也是最常用的属性:

  • padding 每个元素的边距
  • itemCount 元素的数量,默认为 null 即无限
  • itemBuilder 接受一个回调函数 参数为:BuildContext context, int index

ListView.builder

首先还是要翻译一下源码里是怎么解释这个方法的:

使用了 indexedWidgetBuilder 它可以按需生成子元素,此构造函数适用于列表需要大量或者无限子元素生成,因为其调用了元素生成器,所以仅在实际可视范围中显示。

Ok,那我们就来看看代码是如何实现的。

ListView.builder 实现动态生成子元素
ListView.builder 实现动态生成子元素

当 itemCount 设置为 null 时就可以实现无限下拉列表。少侠小伙伴们可以在代码中尝试修改一下看看效果。

ListView.separated

首先还是要翻译一下源码里是怎么解释这方法的:

使用了两个 indexedWidgetBuilder 来处理子元素,itembuilder 是按需生成子元素,separatorbuilder 是根据子元素来生成子元素之间的分隔符元素。此构造函数只能适用于子级数量确定的列表视图。

Ok,那我们就来看看代码是如何实现的。

ListView.builder 实现动态生成子元素
ListView.builder 实现动态生成子元素

其实 separated 和 builder 差别并不大,这里我只做了简单的修改就实现了分割线。

ListView.custom

没错还是要翻译一下源码里是怎么解释这方法的:

构造函数接受一个 sliverChildDelegate,它提供自定义子模型其他方面的功能。例如:sliverchildDelegate 可以控制用于估计实际不可见子级大小的算法。

ListView.custom 要实现起来的话较为麻烦,但还是可以简单实现一下。

主要实现方式有 SliverChildListDelegate 列表方式 和 SliverChildBuilderDelegate 编码方式。

SliverChildBuilderDelegate 编码方式实现
SliverChildBuilderDelegate 编码方式实现

最终效果的话,少侠小伙伴们,可以自己更新修改代码尝试哟。

配合文章一同食用的代码已同步更新到 Github 地址:

https://github.com/linxsbox/myapp.git

结语

ListView Widget 的内容其实并不难,列表的使用都有对应的场景,只要熟悉了列表的渲染特征后,碰见相应的场景自然就不用纠结到底使用哪一个更合适了。其中的难点还是在于 ListView.custom 的实现上,他需要你自己去实现列表相关的所有东西:监听滚动、渲染子元素的方式、销毁子元素等等。

最后总结

  • flutter 基本上为你考虑了一些相关场景使用的实现,所以可以很方便的使用这些内容,但是考虑过细自然也就会觉得需要了解的内容就过多。
  • 就单单从“列表”来看,大致和其他语言的实现是相似的,了解其中常用的属性即可正常使用。

参考来源:

《Flutter实战》: https://book.flutterchina.club/chapter6/scroll_controller.html Flutter 官网 API: https://api.flutter.dev/flutter/widgets/ScrollView/controller.html

相关文章

感谢大家的喜欢!

欢迎 关注、留言、分享、转发、在看。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档