专栏首页林小帅的专栏Flutter 视图布局(二)
原创

Flutter 视图布局(二)

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

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 源码部分

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

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

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

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

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

发生了什么?

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

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

ListBody 源码注释部分

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

(摸着下巴若有所思)OK,那我们就来把他放在 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 反向渲染/颠倒列表

controller

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

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

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

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

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

primary 与 controller 互斥

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

addAutomaticKeepAlives

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

addAutomaticKeepAlives 源码部分说明

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

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

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

addRepaintBoundaries

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

addRepaintBoundaries 源码部分说明

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

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

addSemanticIndexes

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

addSemanticIndexes 源码部分说明

cacheExtent

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

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

physics

physics 主要是 滚动的物理效果

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

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

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

ClampingScrollPhysics 的滚动效果

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

BouncingScrollPhysics 的滚动效果

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

FixedExtentScrollPhysics 的滚动效果

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

03 - 无线滚动例子

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

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

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 实现动态生成子元素

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

ListView.separated

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

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

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

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

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

ListView.custom

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

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

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

主要实现方式有 SliverChildListDelegate 列表方式 和 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

相关文章

感谢大家的喜欢!

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

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Flutter 视图布局-前言

    在学习 Flutter 的过程中也看到一些江湖侠客们对于 Flutter 的议论。他们大多觉得 Flutter 不够友好、括号太多了,导致看起来代码非常复杂,对...

    林小帅
  • Flutter Hello World

    这里我们创建一个 名叫 myapp 的 flutter项目。(别忘了要先进入你的工作文件夹)

    林小帅
  • Flutter 视图布局(三)

    之前的几篇中开头也把一些要注意的东西说完了,所以也不用那么多废话了,不多逼逼直接进入主题,就问你们开心不开心

    林小帅
  • 学习python DAY 01-------列表-----简单的数据结构

    CODE-S
  • 改进异常处理的 6 条建议

    来源:ImportNew - 唐尤华 , 合理地使用异常处理可以帮你节省数小时(甚至数天)调试时间。一个乘法异常会毁掉你的晚餐乃至周末计划。如果处置不及时,甚至...

    企鹅号小编
  • 我们的挚爱——数据科学技术

    数据科学家最爱的几款工具! 一个能干的数据科学家经常被看作是分析学中额的独角兽,这是因为他们的工作往往需要深厚的数学和统计学的知识、熟悉计算机科学,还要有掌握一...

    小莹莹
  • 一个很有借鉴价值的编程故事

    这是一个真实的故事,关于我自己的。一个理智的生命个体是怎么一步步走向疯狂? 我穿着西装,坐在办公室里,脑子里有一个模糊的创业想法。然后,我决定学习编程。我曾经无...

    小小科
  • 边缘计算不会取代云计算

    几乎所有的新技术都是对传统技术的一种突破创新,云计算也不例外。边缘计算在业界的一些评论家看来将会取代云计算,但问题是这是真的吗? 边缘计算,是一种分散式运算的架...

    SDNLAB
  • 如何培养解决复杂问题的能力

    数据如何采集?是服务端主动到监控节点拉取信息?还是客户端主动上报相关的信息,从而划分为两种类型,一种是有专门的客户端,一种是使用主机自带的协议,例如snmp协...

    SRE运维实践
  • Pandas-4. Panel

    从warning信息可知,该方法已经废弃,建议用MultiIndex on a DataFrame来处理3D信息。

    悠扬前奏

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动