前言 -- 这是一篇陆陆续续写了三天的文章
在实际的开发中我们经常会遇到用列表展示数据,当内容超过一屏的时候可以进行滚动,来查看更多的内容,Android中这样的控件有ScrollView,ListView,RecycleView,GridView,ViewPager,那么在Flutter中都有哪些滚动的Widget呢,这篇文章我们将一一介绍。
正文 - Flutter中的可滚动Widget
-
SingleChildScrollView - 只能包含一个子Widget的可滚动Widget
这个其实很像Android中的ScrollView,在Android中当我们用ScrollView包裹控件,如果控件很多的时候,我们一般会用LinearLayout或者RelativeLayout来包裹这些控件,相同的在Flutter中使用SingleChildScrollView来包裹很多子Widget的时候我们也需要用ListBody或者其他子Widget来嵌套,然后再在SingleChildScrollView中使用
效果图
下面看下使用方法:
SingleChildScrollView(
padding: EdgeInsets.all(15),
child: Text(
"hello world 你好吗" * 500,
style: TextStyle(fontSize: 18, color: Colors.green),
),
),
下面看下构造方法
const SingleChildScrollView({
Key key,
this.scrollDirection = Axis.vertical,//滑动方向,默认垂直方向,类型Axis
this.reverse = false,//控制是从头开始滑动还是从尾开始滑动,false是从头开始滑动
this.padding,//子Widget的内边距,
bool primary,//是否是与父级关联的主滚动视图,当为true时,即使SingleChildScrollView中没有足够的内容也能滑动
this.physics,//设置SingleChildScrollView滚动效果,类型ScrollPhysics
this.controller,//可以控制 SingleChildScrollView 滚动的位置当 primary 为 true 时,controller 必须为 null,类型ScrollController
this.child,//SingleChildScrollView 的列表项
this.dragStartBehavior = DragStartBehavior.start,//确定处理拖动开始行为的方式
}) :
...;
ScrollPhysics
- 如果想让 SingleChildScrollView 里没有足够的内容也能滑动,则设置为 AlwaysScrollableScrollPhysics()
- 如果想让 SingleChildScrollView 在没有足够的内容的时候不能滑动,则设置为 ScrollPhysics()
DragStartBehavior
- 如果设置为[DragStartBehavior.start],则在检测到拖动手势时将开始滚动拖动行为
- 如果设置为[DragStartBehavior.down],它将在首次检测到向下事件时开始
ScrollController提供以下的几个功能:
- 设置 SingleChildScrollView 滑动的初始位置
- 可以控制 SingleChildScrollView 是否存储和恢复滑动的位置
- 可以读取、设置当前滑动的位置
-
ListView - 可以线性排列子Widget的可滚动Widget
ListView 共有四种使用方法,下面一一介绍
1.使用默认的构造函数,给 children 属性赋值
先看效果图
看下代码实现
ListView(
children: <Widget>[
ListTile(
title: Text("title1"),
),
ListTile(
title: Text("title2"),
),
ListTile(
title: Text("title3"),
),
ListTile(
title: Text("title4"),
),
ListTile(
title: Text("title5"),
),
ListTile(
title: Text("title6"),
),
ListTile(
title: Text("title7"),
),
ListTile(
title: Text("title8"),
),
ListTile(
title: Text("title9"),
),
ListTile(
title: Text("title10"),
),
ListTile(
title: Text("title11"),
),
ListTile(
title: Text("title12"),
),
ListTile(
title: Text("title13"),
),
ListTile(
title: Text("title14"),
),
ListTile(
title: Text("title15"),
),
ListTile(
title: Text("title16"),
)
],
)
PS:这种方式只适用于那些只有少量子Widget的ListView,ListView在创建的时候,其子Widget也会一起创建
下面看下ListView的构造函数
ListView({
Key key,
Axis scrollDirection = Axis.vertical,//滑动的方向,默认为 Axis.vertical,垂直方向可滑动
bool reverse = false,//控制 ListView 里列表项的排列顺序,是按照插入顺序排,还是按照插入顺序相反的方向排序
ScrollController controller,//可以控制 ListView 滚动的位置
bool primary,//是否是与父级关联的主滚动视图
ScrollPhysics physics,//设置 ListView 的滚动效果
bool shrinkWrap = false,//是否根据列表项的总长度来设置 ListView的长度
EdgeInsetsGeometry padding,//ListView 的内边距
this.itemExtent,//itemExtent 指的是列表项的大小
bool addAutomaticKeepAlives = true,//是否用 AutomaticKeepAlive 来包列表项,默认为 true
bool addRepaintBoundaries = true,// 是否用 RepaintBoundary 来包列表项,默认为 true
bool addSemanticIndexes = true,//是否用 IndexedSemantics 来包列表项,默认为 true,使用 IndexedSemantics 是为了正确的用于辅助模式
double cacheExtent,//ListView 可见部分的前面和后面的区域可以用来缓存列表项,这部分区域的 item 即使不可见,也会加载出来,所以当滑动到这个区域的时候,缓存的区域就会变的可见,cacheExtent 就表示缓存区域在可见部分的前面和后面有多少像素
List<Widget> children = const <Widget>[],//ListView 的列表项
int semanticChildCount,//提供语义信息的列表项的数量,默认为 ListView 的 item 的数量
DragStartBehavior dragStartBehavior = DragStartBehavior.start,//同SingleChildScrollView中解释
}) : childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
...
);
shrinkWrap: 当 shrinkWrap 为 false 时,ListView 会在滚动方向扩展到可占用的最大空间
当 shrinkWrap 为 true 时,ListView 在滚动方向占用的空间就是其列表项的总长度,但是这样会很耗性能,因为当其列表项发生变化时,ListView 的大小会重新计算
itemExtent: 如果滚动方向是垂直方向,则 itemExtent 代表的是 子Widget 的高度,
如果滚动方向为水平方向,则 itemExtent 代表的是 子Widget 的长度
如果 itemExtent 不为 null,则会强制所有 子Widget 在滑动方向的大小都为 itemExtent
指定 itemExtent 会比较高效,因为 子Widget 的高度就不需要在去计算,ListView 也可以提前知道列表的长度
addAutomaticKeepAlives: 在一个 lazy list 里,如果 子Widget 为了保证自己在滑出可视界面时不被回收,就需要把 addAutomaticKeepAlives 设为 true
当 子Widget 不需要让自己保持存活时,为了提升性能,请把 addAutomaticKeepAlives 设为 false
如果 子Widget 自己维护其 KeepAlive 状态,那么此参数必须置为false。
addRepaintBoundaries: 当 addRepaintBoundaries 为 true 时,可以避免列表项重绘,提高性能
但是当列表项重绘的开销非常小(如一个颜色块,或者一个较短的文本)时,不添加 RepaintBoundary 反而会更高效。
2.适用ListView.builder可用于和数据绑定实现大量或者无限的列表
先看效果图
看下实现代码
class ScrollLayoutDemo extends StatefulWidget {
String title;
List<String> items = List<String>.generate(500, (i) => "item $i");
ScrollLayoutDemo({Key key, this.title});
@override
_ScrollLayoutDemoState createState() => _ScrollLayoutDemoState();
}
class _ScrollLayoutDemoState extends State<ScrollLayoutDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: ListView.builder(
itemCount: widget.items.length,
itemBuilder: (context,index){
return ListTile(
title: Text(widget.items[index]),
);
},
)
);
}
}
其构造函数大部分属性和ListView都一样,只有两个:
itemCount:代表 子Widget 的数量,虽然是可选的,但是还是建议赋值,可以让 ListView 预估最大滑动距离,从而提升性能。如果为null,则子节点数由[itemBuilder]返回null的最小索引确定。
itemBuilder:必传参数,itemBuilder 用于创建实际可见的 子Widget,只有索引大于或等于零且小于 itemCount 才会调用 itemBuilder。
3.使用 ListView.separated,具有分割项的 ListView.builder
ListView.separated相比ListView.builder多了一个separatorBuilder,separatorBuilder是用于构建分割项的,而且是必选的
先看下效果
使用方法
ListView.separated(
itemCount: widget.items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(widget.items[index]),
);
},
separatorBuilder: (context, index) {
return Container(
constraints: BoxConstraints.tightFor(height: 1),
color: Colors.greenAccent,
);
},
));
4.使用 ListView.custom,需要使用 SliverChildDelegate
先看下效果图
看下代码实现
//使用方法
ListView.custom(
childrenDelegate: SliverChildListDelegate(
_getWidget()
),
)
代码中添加widget的方法如下
_getWidget(){
List<Widget> widgets = [];
for (var i = 0;i < 100;i++) {
widgets.add(ListTile(title: Text("item $i"),));
}
return widgets;
}
先看下ListView.custom的构造函数,
const ListView.custom({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
this.itemExtent,
@required this.childrenDelegate,
double cacheExtent,
int semanticChildCount,
}) : assert(childrenDelegate != null),
super(
...
);
由构造函数可以看出ListView.custom跟ListView主要的区别就是childrenDelegate,这个是必传参数,类型是SliverChildDelegate
,这是一个抽象类,主要有两个实现类SliverChildBuilderDelegate和SliverChildListDelegate(我们demo中就是使用这个类来实现列表的)
-
GridView
和ListView一样,共有五种方法来创建一个GridView,下面来一一介绍
1、使用默认的构造函数,给children属性赋值 - 只适用于那些只有少量子Widget的GridView
先看下效果图
下面看下代码实现
GridView(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
children: _getWidget(),
)
下面看下GridView的源码:
我们主要看下GridView新增的属性,一些滚动公共的属性在前面在SingleChildScrollview中有介绍,这里就不再赘述
GridView({
Key key,
Axis scrollDirection = Axis.vertical,//滚动方向,默认垂直滚动,类型是Axis
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,//控制GridView子Widget的布局的委托类型是SliverGridDelegate
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
}) : assert(gridDelegate != null),
...,
super(
...
);
-
SliverGridDelegate:SliverGridDelegate 的实现有两个:
SliverGridDelegateWithMaxCrossAxisExtent:横轴 子Widget 为固定长度的布局算法
SliverGridDelegateWithFixedCrossAxisCount:横轴 子Widget 为固定数量的布局算法
下面看下这两个Delegate的构造函数,以及参数的含义
SliverGridDelegateWithFixedCrossAxisCount的构造函数
const SliverGridDelegateWithFixedCrossAxisCount({
@required this.crossAxisCount,//交叉轴子view的数量
this.mainAxisSpacing = 0.0,//主轴方向的的间距(主轴为垂直方向则为行间距,主轴为水平方向则为列间距)
this.crossAxisSpacing = 0.0,//交叉轴方向的间距(交叉轴为垂直方向则为行间距,交叉轴为水平方向则为列间距)
this.childAspectRatio = 1.0,//横轴和主轴的比例
}) : ...;
SliverGridDelegateWithMaxCrossAxisExtent的构造函数
const SliverGridDelegateWithMaxCrossAxisExtent({
@required this.maxCrossAxisExtent,
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
this.childAspectRatio = 1.0,
}) :...;
和上面构造函数的唯一区别就是maxCrossAxisExtent这个参数,其他的都一样
maxCrossAxisExtent:在交叉轴上每一项的的最大范围,例如当主轴是垂直的,GridView的宽度是500px,这个值为150px则Delegate就是自动计算一共可以显示4列,每列是125px。
2、使用GridView.count 创建GridView
这个实现的效果其实和GridView很像,就不上效果图了,我们直接看其构造函数
GridView.count({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required int crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio,
),
childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
...
);
从构造函数我们可以看到,这个方法其实就是将GridView中的SliverGridDelegateWithFixedCrossAxisCount,拆了出来,将SliverGridDelegateWithFixedCrossAxisCount中的属性直接放在构造函数中,使用方式其实没有差别,下面看下使用方式
GridView.count(
crossAxisCount: 3,
children: _getWidget(),
)
就是这么简单。
同理既然SliverGridDelegateWithFixedCrossAxisCount这个可以拆,那么SliverGridDelegateWithMaxCrossAxisExtent是不是一样可以,答案是肯定的,这就有了第三种方式
3、使用 GridView.extent来创建GridView
看下其构造方法
GridView.extent({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
childAspectRatio: childAspectRatio,
),
childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
...
);
下面看下实现
GridView.extent(
maxCrossAxisExtent: 120,
children: _getWidget(),
)
4、使用GridView.builder,可用于和数据绑定实现大量或者无限的列表
这个和ListView.builder的使用就很相似了,我们先看下效果图
下面看下实现代码
class GridViewLayoutDemo extends StatefulWidget {
String title;
List<Widget> items = _getWidget();
GridViewLayoutDemo({Key key, this.title});
@override
_GridViewLayoutDemoState createState() => _GridViewLayoutDemoState();
}
_getWidget() {
List<Widget> widgets = [];
for (var i = 0; i < 100; i++) {
widgets.add(ListTile(
title: Text("item $i"),
));
}
return widgets;
}
class _GridViewLayoutDemoState extends State<GridViewLayoutDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
),
itemCount: widget.items.length,
itemBuilder: (context,index){
return widget.items[index];
},
)
);
}
}
下面看下构造函数
GridView.builder({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
int semanticChildCount,
}) : assert(gridDelegate != null),
childrenDelegate = SliverChildBuilderDelegate(
itemBuilder,
childCount: itemCount,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
...
);
这里主要是gridDelegate和itemBuilder这两个参数,和ListView中itemCount和itemBuilder属性很像。
5、使用 GridView.custom来创建GridView
先看下其实现方式
GridView.custom(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3
),
childrenDelegate: SliverChildListDelegate(
_getWidget()
),
),
下面看下构造函数
const GridView.custom({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required this.childrenDelegate,//类型是SliverChildDelegate
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(gridDelegate != null),
assert(childrenDelegate != null),
super(
...
);
这里其实是增加了childrenDelegate类型是SliverChildDelegate,可以定制子widget,和ListView.custom中的一样,用法也是一样的
-
CustomScrollView - 可以自定义滑动效果的可滚动的Widget
首先先看下实现效果
看下代码实现
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 100,
flexibleSpace: FlexibleSpaceBar(
title: Text("demo"),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0
),
delegate: SliverChildBuilderDelegate((context,index){
return Container(
alignment: Alignment.center,
color: Colors.lightGreen[100*(index % 9)],
child: Text("grid item $index"),
);
},
childCount: 20),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate((contxt,index){
return Container(
alignment: Alignment.center,
color: Colors.pink[100*(index % 9)],
child: Text("grid item $index"),
);
},
childCount: 100)
),
],
)
下面看下CustomScrollView的构造函数
它里面的属性我们在介绍其他控件是都有介绍 ,这里就不做赘述
const CustomScrollView({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
Key center,//默认为空,当shrinkWrap=false时,必须为空
double anchor = 0.0,
double cacheExtent,
this.slivers = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super(
...
);
这里主要看下slivers这个属性,这个属性的值只能是以Sliver开头的一系列Widget:
-
SliverList
看下构造函数
const SliverList({
Key key,
@required SliverChildDelegate delegate,
}) : super(key: key, delegate: delegate);
用法和上面的ListView.custom类似,属性含义可以参考上文
-
SliverFixedExtentList
看下SliverFixedExtentList的构造函数
const SliverFixedExtentList({
Key key,
@required SliverChildDelegate delegate,
@required this.itemExtent,
}) : super(key: key, delegate: delegate);
用法和上面的ListView.custom类似,属性含义可以参考上文
-
SliverGrid
看下SliverGrid的构造函数
const SliverGrid({
Key key,
@required SliverChildDelegate delegate,
@required this.gridDelegate,
}) : super(key: key, delegate: delegate);
这个Widget跟GridView很像,除了这个还有SliverGrid.extent(),和SliverGrid.count()两种构造方法,用法分别和GridView.extent()和GridView.count()也是相似
-
SliverPadding
看下SliverPadding的构造函数
const SliverPadding({
Key key,
@required this.padding,//内边距
Widget sliver,//这里widget也只能是Sliver开头的Widget
}) : assert(padding != null),
super(key: key, child: sliver);
-
SliverAppBar
看下SliverAppBar的构造函数
const SliverAppBar({
Key key,
this.leading,//左侧的图标或文字,多为返回箭头
this.automaticallyImplyLeading = true,//没有leading为true的时候,默认返回箭头,没有leading且为false,则显示title
this.title,//标题
this.actions,//标题右侧的操作
this.flexibleSpace,//可以理解为SliverAppBar的背景内容区
this.bottom,//SliverAppBar的底部区
this.elevation,//阴影
this.forceElevated = false,//是否显示阴影
this.backgroundColor,//背景颜色
this.brightness,//状态栏主题,默认Brightness.dark,可选参数light
this.iconTheme,//SliverAppBar图标主题
this.actionsIconTheme,//action图标主题
this.textTheme,//文字主题
this.primary = true,//是否显示在状态栏的下面,false就会占领状态栏的高度
this.centerTitle,//标题是否居中显示
this.titleSpacing = NavigationToolbar.kMiddleSpacing,//标题横向间距
this.expandedHeight,//合并的高度,默认是状态栏的高度加AppBar的高度
this.floating = false,//滑动时是否悬浮
this.pinned = false,//标题栏是否固定
this.snap = false,//配合floating使用
}): assert(automaticallyImplyLeading != null),
assert(forceElevated != null),
assert(primary != null),
assert(titleSpacing != null),
assert(floating != null),
assert(pinned != null),
assert(snap != null),
assert(floating || !snap, 'The "snap" argument only makes sense for floating app bars.'),
super(key: key)
-
SliverSafeArea:-让内容显示在安全区域内
看下其构造函数
const SliverSafeArea({
Key key,
this.left = true,//是否左侧在可是区域内
this.top = true,//是否顶部在可是区域内
this.right = true,//是否右侧在可是区域内
this.bottom = true,//是否底部在可是区域内
this.minimum = EdgeInsets.zero,//最小边距
@required this.sliver,//这里widget也只能是Sliver开头的Widget
}) : assert(left != null),
assert(top != null),
assert(right != null),
assert(bottom != null),
super(key: key);
这个Widget可以保证内容展示在可视区域内
- PageView-可以一页一页滑动的Widget,子Widget会占据当前屏幕的所有可视区域,类似于Android中的ViewPager
PageView有三种实现方式,下面我们一一介绍
1、使用默认的构造函数,给 children 属性赋值
使用默认构造函数写 PageView,只适用于那些只有少量 子Widget 的 PageView。
先看效果图
看下实现代码
PageView(
onPageChanged: (index){
},
children: <Widget>[
Center(
child: Text(
"title0",
style: TextStyle(
fontSize: 24,
color: Colors.purple
),
),
),
Center(
child: Text(
"title1",
style: TextStyle(
fontSize: 24,
color: Colors.red
),
),
),
Center(
child: Text(
"title2",
style: TextStyle(
fontSize: 24,
color: Colors.green
),
),
),
Center(
child: Text(
"title3",
style: TextStyle(
fontSize: 24,
color: Colors.blue
),
),
)
],
)
可以看到使用很简单,接下来看下其构造函数
PageView({
Key key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
PageController controller,
this.physics,
this.pageSnapping = true,//默认值为 true,设置为false以禁用页面捕捉,对自定义滚动行为很有用。
this.onPageChanged,//当 PageView 当前页面切换的时候调用,是个方法
List<Widget> children = const <Widget>[],//PageView 的列表项
this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildListDelegate(children),
super(key: key);
2、使用 PageView.builder
PageView.builder 可以和数据绑定,用于构建大量或无限的列表。而且只会构建那些实际可见的 子Widget
看下演示效果
看下使用方法
PageView.builder(
onPageChanged: (index){
},
itemCount: 10,
itemBuilder: (context,index){
return Center(
child: Text(
"title $index",
style: TextStyle(
fontSize: 24,
color: Colors.purple
),
),
);
},
)
看下其构造方法
PageView.builder({
Key key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
PageController controller,
this.physics,
this.pageSnapping = true,
this.onPageChanged,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
super(key: key);
可以看到多了和 ListView.builder 类似的 itemCount 和 itemBuilder 属性,用法也是一样的。
3、使用 PageView.custom
先看下演示效果
看下代码实现
PageView.custom(
onPageChanged: (index) {},
childrenDelegate: SliverChildListDelegate(<Widget>[
Center(
child: Text(
"title1",
style: TextStyle(fontSize: 24, color: Colors.green),
),
),
Center(
child: Text(
"title2",
style: TextStyle(fontSize: 24, color: Colors.green),
),
),
Center(
child: Text(
"title3",
style: TextStyle(fontSize: 24, color: Colors.green),
),
),
]),
),
下面我们看下构造函数
PageView.custom({
Key key,
this.scrollDirection = Axis.horizontal,
this.reverse = false,
PageController controller,
this.physics,
this.pageSnapping = true,
this.onPageChanged,
@required this.childrenDelegate,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(childrenDelegate != null),
controller = controller ?? _defaultPageController,
super(key: key);
可以看到这里主要是childrenDelegate 这个属性,类型是SliverChildDelegate,它具有定制子Widget的能力,这个跟ListView.custom()中的childrenDelegate属性用法相同
结语
截止到此,关于可滚动的Widget就介绍完了。
以下是我的Flutter系列的链接,后续会持续更新,欢迎大家指正。
Flutter 系列文章
- Flutter 学习 - 开篇
- Flutter 学习 - 基础框架
- Flutter 学习 - 网络请求和数据解析
- Flutter 学习 - Widget 之 Text
- Flutter 学习 - Widget 之 RichText
- Flutter 学习 - Widget 之 Image和Icon
- Flutter 学习 - Widget 之 TextField
- Flutter 学习 - Widget 之 菜单按钮
- Flutter 学习 - Widget 之 布局 Widget
- Flutter 学习 - 容器类Widget
- Flutter 学习 - 可滚动的 Widget
- Flutter 学习 - 功能类Widget