08-可滚动Widget
可滚动Widget
ViewPort视口
在Flutter中,术语ViewPort(视口),如无特别说明,则是指一个Widget的实际显示区域。例如,一个ListView的显示区域高度是800像素,虽然其列表项总高度可能远远超过800像素,但是其ViewPort仍然是800像素。
主轴和纵轴
在可滚动widget的坐标描述中,通常将滚动方向称为主轴,非滚动方向称为纵轴。由于可滚动widget的默认方向一般都是沿垂直方向,所以默认情况下主轴就是指垂直方向,水平方向同理。
Scrollable
当内容超过显示视口(ViewPort)时,如果没有特殊处理,Flutter则会提示Overflow错误。为此,Flutter提供了多种可滚动widget(Scrollable Widget)用于显示列表和长布局。可滚动Widget都直接或间接包含一个Scrollable widget:
Scrollable({...this.axisDirection = AxisDirection.down,this.controller,this.physics,@required this.viewportBuilder, //后面介绍
})
- axisDirection:滚动方向。
- physics:此属性接受一个ScrollPhysics对象,它决定可滚动Widget如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画;或者滑动到边界时,如何显示。
- controller:此属性接受一个ScrollController对象。ScrollController的主要作用是控制滚动位置和监听滚动事件。
Scrollbar
Scrollbar是一个Material风格的滚动指示器(滚动条),如果要给可滚动widget添加滚动条,只需将Scrollbar作为可滚动widget的父widget即可,如:
Scrollbar(child: SingleChildScrollView(...),
);
SingleChildScrollView
SingleChildScrollView类似于Android中的ScrollView,它只能接收一个子Widget。定义如下:
SingleChildScrollView({this.scrollDirection = Axis.vertical, //滚动方向,默认是垂直方向this.reverse = false, this.padding, bool primary, this.physics, this.controller,this.child,
})
除了通用属性,我们重点看一下reverse和primary两个属性:
- reverse:该属性API文档解释是:是否按照阅读方向相反的方向滑动,
- primary:指是否使用widget树中默认的PrimaryScrollController;当滑动方向为垂直方向(scrollDirection值为Axis.vertical)并且controller没有指定时,primary默认为true.
ListView
ListView是最常用的可滚动widget,它可以沿一个方向线性排布所有子widget。我们看看ListView的默认构造函数定义:
ListView({... //可滚动widget公共参数Axis scrollDirection = Axis.vertical,bool reverse = false,ScrollController controller,bool primary,ScrollPhysics physics,EdgeInsetsGeometry padding,//ListView各个构造函数的共同参数 double itemExtent,bool shrinkWrap = false,bool addAutomaticKeepAlives = true,bool addRepaintBoundaries = true,double cacheExtent,//子widget列表List<Widget> children = const <Widget>[],
})
itemExtent:该参数如果不为null,则会强制children的"长度"为itemExtent的值;这里的"长度"是指滚动方向上子widget的长度,即如果滚动方向是垂直方向,则itemExtent代表子widget的高度,如果滚动方向为水平方向,则itemExtent代表子widget的长度。
shrinkWrap:该属性表示是否根据子widget的总长度来设置ListView的长度,默认值为false 。默认情况下,ListView的会在滚动方向尽可能多的占用空间。当ListView在一个无边界(滚动方向上)的容器中时,shrinkWrap必须为true。
addAutomaticKeepAlives:该属性表示是否将列表项(子widget)包裹在AutomaticKeepAlive widget中;典型地,在一个懒加载列表中,如果将列表项包裹在AutomaticKeepAlive中,在该列表项滑出视口时该列表项不会被GC,它会使用KeepAliveNotification来保存其状态。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false。
addRepaintBoundaries:该属性表示是否将列表项(子widget)包裹在RepaintBoundary中。当可滚动widget滚动时,将列表项包裹在RepaintBoundary中可以避免列表项重绘,但是当列表项重绘的开销非常小(如一个颜色块,或者一个较短的文本)时,不添加RepaintBoundary反而会更高效。和addAutomaticKeepAlive一样,如果列表项自己维护其KeepAlive状态,那么此参数必须置为false。
ListView.builder
ListView.builder适合列表项比较多(或者无限)的情况,因为只有当子Widget真正显示的时候才会被创建。下面看一下ListView.builder的核心参数列表:
ListView.builder({// ListView公共参数已省略 ...@required IndexedWidgetBuilder itemBuilder,int itemCount,...
})
- itemBuilder:它是列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项。
- itemCount:列表项的数量,如果为null,则为无限列表。
ListView.separated
ListView.separated可以生成列表项之间的分割器,它除了比ListView.builder多了一个separatorBuilder参数,该参数是一个分割器生成器。
class ListView3 extends StatelessWidget {@overrideWidget build(BuildContext context) {//下划线widget预定义以供复用。 Widget divider1=Divider(color: Colors.blue,);Widget divider2=Divider(color: Colors.green);return ListView.separated(itemCount: 100,//列表项构造器itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));},//分割器构造器separatorBuilder: (BuildContext context, int index) {return index%2==0?divider1:divider2;},);}
}
GridView
GridView可以构建一个二维网格列表,其默认构造函数定义如下:
GridView({Axis scrollDirection = Axis.vertical,bool reverse = false,ScrollController controller,bool primary,ScrollPhysics physics,bool shrinkWrap = false,EdgeInsetsGeometry padding,@required SliverGridDelegate gridDelegate, //控制子widget layout的委托bool addAutomaticKeepAlives = true,bool addRepaintBoundaries = true,double cacheExtent,List<Widget> children = const <Widget>[],
})
- gridDelegate参数,类型是SliverGridDelegate,它的作用是控制GridView子widget如何排列(layout)。
SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法,Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent:
SliverGridDelegateWithFixedCrossAxisCount
该子类实现了一个横轴为固定数量子元素的layout算法,其构造函数为:
SliverGridDelegateWithFixedCrossAxisCount({@required double crossAxisCount,double mainAxisSpacing = 0.0,double crossAxisSpacing = 0.0,double childAspectRatio = 1.0,
})
- crossAxisCount:横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了,即ViewPort横轴长度/crossAxisCount。
- mainAxisSpacing:主轴方向的间距。
- crossAxisSpacing:横轴方向子元素的间距。
- childAspectRatio:子元素在横轴长度和主轴长度的比例。由于crossAxisCount指定后子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在主轴的长度。
可以发现,子元素的大小是通过crossAxisCount和childAspectRatio两个参数共同决定的。
GridView.count
GridView.count构造函数内部使用了SliverGridDelegateWithFixedCrossAxisCount,我们通过它可以快速的创建横轴固定数量子元素的GridView:
GridView.count(crossAxisCount: 3,childAspectRatio: 1.0,children: <Widget>[Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast),],
);
SliverGridDelegateWithMaxCrossAxisExtent
该子类实现了一个横轴子元素为固定最大长度的layout算法,其构造函数为:
SliverGridDelegateWithMaxCrossAxisExtent({double maxCrossAxisExtent,double mainAxisSpacing = 0.0,double crossAxisSpacing = 0.0,double childAspectRatio = 1.0,
})
- maxCrossAxisExtent为子元素在横轴上的最大长度,之所以是“最大”长度,是因为横轴方向每个子元素的长度仍然是等分的,
- childAspectRatio所指的子元素横轴和主轴的长度比为最终的长度比。其它参数和SliverGridDelegateWithFixedCrossAxisCount相同。
GridView.extent
GridView.extent构造函数内部使用了SliverGridDelegateWithMaxCrossAxisExtent,我们通过它可以快速的创建纵轴子元素为固定最大长度的的GridView:
GridView.extent(maxCrossAxisExtent: 120.0,childAspectRatio: 2.0,children: <Widget>[Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast),],);
GridView.builder
上面我们介绍的GridView都需要一个Widget数组作为其子元素,这些方式都会提前将所有子widget都构建好,所以只适用于子Widget数量比较少时,当子widget比较多时,我们可以通过GridView.builder来动态创建子Widget。GridView.builder 必须指定的参数有两个:
GridView.builder(...@required SliverGridDelegate gridDelegate, @required IndexedWidgetBuilder itemBuilder,
)
其中itemBuilder为子widget构建器。
CustomScrollView
CustomScrollView是可以使用sliver来自定义滚动模型(效果)的widget。它可以包含多种滚动模型,举个例子,假设有一个页面,顶部需要一个GridView,底部需要一个ListView,而要求整个页面的滑动效果是统一的,即它们看起来是一个整体,如果使用GridView+ListView来实现的话,就不能保证一致的滑动效果,因为它们的滚动效果是分离的,所以这时就需要一个"胶水",把这些彼此独立的可滚动widget(Sliver)"粘"起来,而CustomScrollView的功能就相当于“胶水”。
Sliver
Sliver有细片、小片之意,在Flutter中,Sliver通常指具有特定滚动效果的可滚动块。可滚动widget,如ListView、GridView等都有对应的Sliver实现如SliverList、SliverGrid等。对于大多数Sliver来说,它们和可滚动Widget最主要的区别是Sliver不会包含Scrollable Widget,也就是说Sliver本身不包含滚动交互模型 ,正因如此,CustomScrollView才可以将多个Sliver"粘"在一起,这些Sliver共用CustomScrollView的Scrollable,最终实现统一的滑动效果。
示例
import 'package:flutter/material.dart';class CustomScrollViewTestRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {//因为本路由没有使用Scaffold,为了让子级Widget(如Text)使用//Material Design 默认的样式风格,我们使用Material作为本路由的根。return Material(child: CustomScrollView(slivers: <Widget>[//AppBar,包含一个导航栏SliverAppBar(pinned: true,expandedHeight: 250.0,flexibleSpace: FlexibleSpaceBar(title: const Text('Demo'),background: Image.asset("./images/avatar.png", fit: BoxFit.cover,),),),SliverPadding(padding: const EdgeInsets.all(8.0),sliver: new SliverGrid( //GridgridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, //Grid按两列显示mainAxisSpacing: 10.0,crossAxisSpacing: 10.0,childAspectRatio: 4.0,),delegate: new SliverChildBuilderDelegate((BuildContext context, int index) {//创建子widget return new Container(alignment: Alignment.center,color: Colors.cyan[100 * (index % 9)],child: new Text('grid item $index'),);},childCount: 20,),),),//Listnew SliverFixedExtentList(itemExtent: 50.0,delegate: new SliverChildBuilderDelegate((BuildContext context, int index) {//创建列表项 return new Container(alignment: Alignment.center,color: Colors.lightBlue[100 * (index % 9)],child: new Text('list item $index'),);},childCount: 50 //50个列表项),),],),);}
}
代码分为三部分:
- SliverAppBar:SliverAppBar对应AppBar,两者不同之处在于SliverAppBar可以集成到CustomScrollView。SliverAppBar可以结合FlexibleSpaceBar实现Material Design中头部伸缩的模型,具体效果,读者可以运行该示例查看。
- SliverGrid:它用SliverPadding包裹以给SliverGrid添加补白。SliverGrid是一个两列,宽高比为4的网格,它有20个子widget。
- SliverFixedExtentList:它是一个所有子元素高度都为50像素的列表。
转载于:https://www.cnblogs.com/SLchuck/p/10332150.html
08-可滚动Widget相关推荐
- 可滚动Widget SingleChildScrollView
SingleChildScrollView类似于Android中的ScrollView,它只能接收一个子Widget.定义如下: SingleChildScrollView({this.scrollD ...
- 深入Flutter(四) Infinite scrolling -- 无限滚动
文章系列 深入Flutter(一) 积极推进"组合"方式 深入Flutter(二) 线性时间复杂度的 build 和 layout 深入Flutter(三) Element树和Re ...
- Flutter键盘弹出造成布局异常解决
在使用flutter开发时,经常会遇到输入框弹起的时候,debug环境下会提示布局异常.下面我们分两种情况进行讨论分析 输入框在可滚动widget上 输入框如果在滚动的widget上时,键盘弹起, ...
- 读书笔记——Flutter实战
第一章 起步 1.2:初识Flutter 静态编译与动态解释:静态编译的程序在执行前全部被翻译为机器码,通常将这种类型称为AOT (Ahead of time)即 "提前编译":而 ...
- OFBIz之旅[结构解析]
OFBIz之旅[结构] 注意: 1 ,持久层,在 OFBIZ 中的定义,就是 Model . DAO 被划分到业务层中. OFBIz 已经改名为 OpenTaps 项目发展了.其自身的工作流引擎也已经 ...
- flutter listview 滚动到底部_Flutter常用Widget详解(三)
前言 前面两篇文章给大家介绍了Widget中对应原生开发中的一些常用基础控件,Text.TextField.Button.Dialog.Picker等,本篇我们将和大家一起学习ListView.Gri ...
- Windows Mobile Widget Emulator
今天Vimpyboy 在codeplex发布了Windows Mobile Widget Emulator.这是一个用来调试Windows Mobile 6.5 Widget的工具,我在做Window ...
- web.xml.jsf_使用JSF 2.2功能来开发可滚动,可延迟加载的Ajax数据表
web.xml.jsf 这次,我想与您分享我最近从JSF 2.2功能中学到的知识. 为此,我决定创建一个简单的ajax,可滚动,延迟加载的数据表. 请注意, 绝不这是相当大的库如Primefaces ...
- 使用JSF 2.2功能来开发可滚动,可延迟加载的Ajax数据表
这次,我想与您分享我最近从JSF 2.2功能中学到的知识. 为此,我决定创建一个简单的ajax,可滚动的延迟加载数据表. 请注意, 绝不这是相当大的库如Primefaces , RichFaces的或 ...
最新文章
- python里的tplt什么意思 Python的format格式化输出
- tomcat配置manager
- Chrome 控制台console的用法
- C++字符串分割替换 ubuntu版本
- 国外知名的开源项目托管网站
- import oracle utility_教你如何Oracle数据导入
- 水文勘测工比赛计算机基本应用,第四届全国水文勘测工大赛内业操作试题A卷.doc...
- 【Python】区分List 和String
- 从零基础入门Tensorflow2.0 ----七、37. 文本生成之---3. 采样文本生成
- 第88课:Spark Streaming从Flume Pull数据案例实战及内幕源码解密
- esp8266WIFI模块教程:正点原子ATK-ESP8266进行网络通信,单片机与电脑,单片机与手机发送数据
- 腾讯首款区块链AR游戏上线《一起来捉妖》,风物志里的奇珍异兽
- 在华为 (Huawei) 工作是怎样一番体验?
- 根据经纬度查询具体地址
- [django]从前端返回字符串,后端转换为字典,执行数据添加操作
- Basic Block
- python抓取酷我MV
- 国外问卷调查是骗人的吗?
- 安卓微信8.0.22 正式版,5个隐秘改变你发现了吗?
- Android开发中onClick事件的几种实现,分析,对比