1 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的值;这里的“长度”是指滚动方向上子组件的长度,也就是说如果滚动方向是垂直方向,则itemExtent代表子组件的高度;如果滚动方向为水平方向,则itemExtent就代表子组件的宽度。
- shrinkWrap:该属性表示是否根据子组件的总长度来设置ListView的长度,默认值为false。默认情况下,ListView的会在滚动方向尽可能多的占用空间。当ListView在一个无边界(滚动方向上)的容器中时,shrinkWrap必须为true。
- addAutomaticKeepAlives:该属性表示是否将列表项(子组件)包裹在AutomaticKeepAlive组件中;典型地,在一个懒加载列表中,如果将列表项包裹在AutomaticKeepAlive中,在该列表项滑出视口时它也不会被GC(垃圾回收),它会使用KeepAliveNotification来保存其状态。如果列表项自己维护其KeepAlive状态,那么此参数必须置为false。
- addRepaintBoundaries:该属性表示是否将列表项(子组件)包裹在RepaintBoundary组件中。当可滚动组件滚动时,将列表项包裹在RepaintBoundary中可以避免列表项重绘,但是当列表项重绘的开销非常小(如一个颜色块,或者一个较短的文本)时,不添加RepaintBoundary反而会更高效。和addAutomaticKeepAlive一样,如果列表项自己维护其KeepAlive状态,那么此参数必须置为false
- ScrollDirection: 滚动方向
- reverse:决定滚动方向是否与阅读方向一致
- scrollController:控制滚动的位置
- primary:当内容不足以滚动时,是否支持滚动;对于iOS系统还有一个效果:当用户点击状态栏时是否滑动到顶部。
- ScrollPhysics:控制用户滚动视图的交互
- AlwaysScrollableScrollPhysics:列表总是可滚动的。在iOS上会有回弹效果,在android上不会回弹。那么问题来了,如果primary设置为false(内容不足时不滚动),且 physics设置为AlwaysScrollableScrollPhysics,列表是否可以滑动?答案是可以,感兴趣的可以试一下
- PageScrollPhysics:一般是给PageView控件用的滑动效果。如果listview设置的话在滑动到末尾时会有个比较大的弹起和回弹
- ClampingScrollPhysics:滚动时没有回弹效果,同android系统的listview效果
- NeverScrollableScrollPhysics:就算内容超过列表范围也不会滑动
- BouncingScrollPhysics:不论什么平台都会有回弹效果
- FixedExtentScrollPhysics:不适用于ListView,原因:需要指定scroller为FixedExtentScrollController,这个scroller只能用于ListWheelScrollViews。
ListView默认构造
适合只有少量的子组件的情况,因为这种方式需要将所有children都提前创建好(这需要做大量工作),而不是等到子widget真正显示的时候再创建
ListView(shrinkWrap: true,padding: const EdgeInsets.all(20.0),children: <Widget>[const Text('I\'m dedicating every day to you'),const Text('Domestic life was never quite my style'),const Text('When you smile, you knock me out, I fall apart'),const Text('And I thought I was so smart'),],);
ListView.builder
@required IndexedWidgetBuilder itemBuilder 构建列表项
- itemBuilder:它是列表项的构建器,类型为IndexedWidgetBuilder,返回值为一个widget。当列表滚动到具体的index位置时,会调用该构建器构建列表项。
- itemCount:列表项的数量,如果为null,则为无限列表。
ListView.builder(itemCount: 100,itemExtent: 50.0, //强制高度为50.0itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));});
ListView.custom
```dart ListView.custom(childrenDelegate: CustomSliverChildDelegate())
class CustomSliverChildDelegate extends SliverChildDelegate { /// 根据index构造child @override Widget build(BuildContext context, int index) { // KeepAlive将把所有子控件加入到cache,已输入的TextField文字不会因滚动消失 // 仅用于演示 return KeepAlive( keepAlive: true, child: TextField(decoration: InputDecoration(hintText: ‘请输入’))); }
/// 决定提供新的childDelegate时是否需要重新build。在调用此方法前会做类型检查,不同类型时才会调用此方法,所以一般返回true。 @override bool shouldRebuild(SliverChildDelegate oldDelegate) { return true; }
/// 提高children的count,当无法精确知道时返回null。 /// 当 build 返回 null时,它也将需要返回一个非null值 @override int get estimatedChildCount => 100;
/// 预计最大可滑动高度,如果设置的过小会导致部分child不可见,设置报错 @override double estimateMaxScrollOffset(int firstIndex, int lastIndex, double leadingScrollOffset, double trailingScrollOffset) { return 2500; }
/// 完成layout后的回调,可以通过该方法获取即将渲染的视图树包括哪些子控件 @override void didFinishLayout(int firstIndex, int lastIndex) { print(‘didFinishLayout firstIndex=$firstIndex firstIndex=$lastIndex’); } }
<a name="DNLEf"></a>#### ListView.separated- itemBuilder构建列表项- separatorBuilder 构建分割组件ListView.separated可以在生成的列表项之间添加一个分割组件,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割组件生成器```dartclass 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;},);}}
<br />
滚动效果的设置,通过physics属性
NeverScrollablePhysics (不滚动效果)<br /> BouncingScrollPhysics (ios风格的滚动效果)<br /> ClampingScrollPhysics (安卓风格滚动效果)<br /> FixedExtentScrollPhysics (固定范围的滚动效果)<br />应用代码
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;},)
2 GridView
[
](https://book.flutterchina.club/chapter6/gridview.html)
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>[],})
SliverGridDelegate是一个抽象类,定义了GridViewLayout相关接口,子类需要通过实现它们来实现具体的布局算法。
Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,我们可以直接使用SliverGridDelegateWithFixedCrossAxisCount
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指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在主轴的长度。
GridView
GridView(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, //横轴三个子widgetchildAspectRatio: 1.0 //宽高比为1时,子widget),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.count
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),],);
GridView.extent
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.builder(...@required SliverGridDelegate gridDelegate,@required IndexedWidgetBuilder itemBuilder,)*/class InfiniteGridView extends StatefulWidget {@override_InfiniteGridViewState createState() => new _InfiniteGridViewState();}class _InfiniteGridViewState extends State<InfiniteGridView> {List<IconData> _icons = []; //保存Icon数据@overridevoid initState() {// 初始化数据_retrieveIcons();}@overrideWidget build(BuildContext context) {return GridView.builder(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, //每行三列childAspectRatio: 1.0 //显示区域宽高相等),itemCount: _icons.length,itemBuilder: (context, index) {//如果显示到最后一个并且Icon总数小于200时继续获取数据if (index == _icons.length - 1 && _icons.length < 200) {_retrieveIcons();}return Icon(_icons[index]);});}//模拟异步获取数据void _retrieveIcons() {Future.delayed(Duration(milliseconds: 200)).then((e) {setState(() {_icons.addAll([Icons.ac_unit,Icons.airport_shuttle,Icons.all_inclusive,Icons.beach_access, Icons.cake,Icons.free_breakfast]);});});}}
3 CustomScrollView
CustomScrollView才可以将多个Sliver”粘”在一起,这些Sliver共用CustomScrollView的Scrollable,所以最终才实现了统一的滑动效果
假设有一个页面,顶部需要一个GridView,底部需要一个ListView,而要求整个页面的滑动效果是统一的,即它们看起来是一个整体。如果使用GridView+ListView来实现的话,就不能保证一致的滑动效果,因为它们的滚动效果是分离的,所以这时就需要一个”胶水”,把这些彼此独立的可滚动组件”粘”起来,而CustomScrollView的功能就相当于“胶水”。
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) {//创建子widgetreturn 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个子组件。* 底部SliverFixedExtentList:它是一个所有子元素高度都为50像素的列表。*/
<br />
4 SliverGrid
delegate
SliverChildDelegate 系统提供个两个已经实现好的代理:SliverChildListDelegate/SliverChildBuilderDelegate
gridDelegate
SliverGridDelegate 系统提供个两个已经实现好的代理:SliverGridDelegateWithFixedCrossAxisCount、SliverGridDelegateWithMaxCrossAxisExtent
_mySliverAppBar() {return SliverAppBar(title: Text('SliverGrid'),expandedHeight: 250,flexibleSpace: FlexibleSpaceBar(background: Image.network(ImageUrlConstant.imageUrl1,fit: BoxFit.cover,),collapseMode: CollapseMode.parallax,),);}_mySliverChildBuilderDelegate() {return SliverChildBuilderDelegate((BuildContext context, int index) {return Container(height: 50,color: Colors.primaries[index % 8],);},childCount: 10,);}_mySliverGridDelegateWithFixedCrossAxisCount() {return SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2,mainAxisSpacing: 10,crossAxisSpacing: 5,childAspectRatio: 1.5,);}_mySliverGridDelegateWithMaxCrossAxisExtent() {return SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 200,mainAxisSpacing: 10,crossAxisSpacing: 5,childAspectRatio: 1,);}@overrideWidget build(BuildContext context) {return Container(color: Colors.white,child: CustomScrollView(slivers: [_mySliverAppBar(),SliverGrid(delegate: _mySliverChildBuilderDelegate(),gridDelegate: _mySliverGridDelegateWithFixedCrossAxisCount(),// gridDelegate: _mySliverGridDelegateWithMaxCrossAxisExtent(),),],),);}
5 SliverList
SliverListz原型
const SliverList({Key key,@required SliverChildDelegate delegate,}) : super(key: key, delegate: delegate);
SliverList的使用:
class _SliverListFulState extends State<SliverListFul> {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("SliverList学习"),),body:CustomScrollView(slivers: [SliverList(delegate: SliverChildBuilderDelegate((content, index) {return Container(height: 65,color: Colors.primaries[index % Colors.primaries.length],);}, childCount: 20),),],)),);}}
6 SliverSafeArea

顶部有一部分被刘海屏遮挡住了,解决此问题的方法是将SliverGrid包裹在SliverSafeArea中:
CustomScrollView(slivers: <Widget>[SliverSafeArea(sliver: SliverGrid(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 5, mainAxisSpacing: 3),delegate: SliverChildBuilderDelegate((BuildContext context, int index) {return Container(color: Colors.primaries[index % Colors.primaries.length],);}, childCount: 20),),)],)
7 SingleChildScrollView
里面只能嵌套一个组件,eg : Column,Row
const SingleChildScrollView({Key key,//滚动方向,默认是垂直方向this.scrollDirection = Axis.vertical,//是否按照阅读方向相反的方向滑动this.reverse = false,//内容边距this.padding,//是否使用widget树中默认的PrimaryScrollControllerbool primary,//此属性接受一个ScrollPhysics类型的对象,它决定可以滚动如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画,或者滑动到边界时,如何显示。//默认情况下,Flutter会根据具体平台分别使用不同的ScrollPhysics对象,对应不同的显示效果,如当滑动到边界时,继续拖动的话,在iOS上会出现弹性效果,//而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果,可以显示指定一个固定的ScrollPhysics。//Flutter SDK包含两个ScrollPhysics的子类。1.ClampingScrollPhysics:Android下微光效果,2.BouncingScrollPhysics:iOS下弹性效果this.physics,//此属性接收一个ScrollController对象,ScrollController的主要作用是控制滚动位置和监听滚动事件。//默认情况下,Widget树中会有一个默认的PrimaryScrollController,如果子树中的可滚动组件没有显示的指定controller,并且primary属性值为true时,可滚动组件会使用这个默认的ScrollController。//这种机制带来的好处是父组件可以控制子树中可滚动的滚动行为,例:scaffold正是使用这种机制在iOS中实现了点击导航回到顶部的功能。this.controller,this.child,})
使用案例
SingleChildScrollView(child: new Column(mainAxisAlignment: MainAxisAlignment.spaceBetween,crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[new Container(child: new Column(children: <Widget>[new TextField(decoration: const InputDecoration(labelText: "Description",),style: Theme.of(context).textTheme.title,),new TextField(decoration: const InputDecoration(labelText: "Description",),style: Theme.of(context).textTheme.title,),new TextField(decoration: const InputDecoration(labelText: "Description",),style: Theme.of(context).textTheme.title,),],)),new Container(margin: new EdgeInsets.only(bottom: 16.0),child: new FloatingActionButton(backgroundColor: new Color(0xFFE57373),child: new Icon(Icons.check),onPressed: (){}),)],),)
