简介

ListView是蕴含多个child组件的widget,在ListView中所有的child widget都是以list的模式来出现的,你能够自定义List的方向,然而和GridView不同的是ListView中的每一个List外面都只蕴含一个widget。

明天咱们来具体理解一下ListView的底层实现和具体的利用。

ListView详解

和GridView一样,ListView也是继承自ScrollView,示意它是一个能够滚动的View。

具体而言,ListView首先继承自BoxScrollView:

class ListView extends BoxScrollView

而后BoxScrollView又继承自ScrollView:

abstract class BoxScrollView extends ScrollView 

ListView中的特有属性

首先咱们来看下ListView中的特有属性,ListView和它的父类相比,多了三个属性,别离是itemExtent,prototypeItem和childrenDelegate。

其中itemExtent是一个double类型的数据,如果给定的是一个非空值,那么示意的是child在scroll方向的extent大小。这个属性次要用来管制children的extend信息,这样每个child就不须要自行来判断本人的extend。

应用itemExtent的益处在于,ListView能够对立的在滚动机制上进行优化,从而晋升性能体现。

prototypeItem是一个widget,从名字就能够看出,这个一个prototype的widget,也就是说是一个原型,其余的child能够参照这个原型widget的大小进行extent的设置。

ListView中的最初一个自定义属性是childrenDelegate,这个childrenDelegate和GridView中的含意是一样的,用来生成ListView中child。

之前咱们在解说GirdView的时候有提到过,GirdView中还有一个自定义的属性叫做gridDelegate,这个gridDelegate是一个SliverGridDelegate的实例,用来管制子组件在GridView中的布局。

因为ListView的子组件的布局是曾经确定的,所以就不再须要gridDelegate了,这是ListView和GridView的一大区别。

ListView作为一个继承的类,须要实现一个buildChildLayout的办法:

  @override  Widget buildChildLayout(BuildContext context) {    if (itemExtent != null) {      return SliverFixedExtentList(        delegate: childrenDelegate,        itemExtent: itemExtent!,      );    } else if (prototypeItem != null) {      return SliverPrototypeExtentList(        delegate: childrenDelegate,        prototypeItem: prototypeItem!,      );    }    return SliverList(delegate: childrenDelegate);  }

这个办法的实现逻辑和咱们之前讲到的三个属性是相关联的,在buildChildLayout中,如果itemExtent有值的话,因为itemExtent自身就是一个固定值,所以返回的是SliverFixedExtentList。

如果itemExtent没有设置,并且prototypeItem有值的话,返回的是一个SliverPrototypeExtentList。

最初,如果itemExtent和prototypeItem都没有设置的话,返回的是一个SliverList对象。

ListView的构造函数

和GridView一样,为了满足咱们的多样性的设计需要,ListView也提供了多个构造函数。

首先咱们来看下ListView的最根本的构造函数:

ListView({    Key? key,    Axis scrollDirection = Axis.vertical,    bool reverse = false,    ScrollController? controller,    bool? primary,    ScrollPhysics? physics,    bool shrinkWrap = false,    EdgeInsetsGeometry? padding,    this.itemExtent,    this.prototypeItem,    bool addAutomaticKeepAlives = true,    bool addRepaintBoundaries = true,    bool addSemanticIndexes = true,    double? cacheExtent,    List<Widget> children = const <Widget>[],    int? semanticChildCount,    DragStartBehavior dragStartBehavior = DragStartBehavior.start,    ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,    String? restorationId,    Clip clipBehavior = Clip.hardEdge,  })

这里itemExtent和prototypeItem这两个属性是内部传入的,childrenDelegate是通过其余的参数结构而来的:

 childrenDelegate = SliverChildListDelegate(         children,         addAutomaticKeepAlives: addAutomaticKeepAlives,         addRepaintBoundaries: addRepaintBoundaries,         addSemanticIndexes: addSemanticIndexes,       ),

ListView中所有的child组件都在List Widget的children中。

这个默认的构造函数,实用于child比拟少的状况,因为须要一次传入所有的child组件到list中,所以对性能的影响还是挺大的,并且传入的child是不可变的。

如果child比拟多的状况下,就须要应用到其余的构造函数了,比方 ListView.builder。

ListView.builder应用的是builder模式来构建child组件,具体而言他的childrenDelegate实现如下:

childrenDelegate = SliverChildBuilderDelegate(         itemBuilder,         childCount: itemCount,         addAutomaticKeepAlives: addAutomaticKeepAlives,         addRepaintBoundaries: addRepaintBoundaries,         addSemanticIndexes: addSemanticIndexes,       ),

这里的childrenDelegate是一个SliverChildBuilderDelegate,通过传入itemBuilder和总的itemCount就能够实现动态创建child的性能。

在ListView的理论应用过程中,为了页面难看或者更有区分度,咱们个别会在list的item中增加一些分隔符separator,为了自动化实现这个性能,ListView提供了一个ListView.separated的构造函数,用来提供list item两头的分隔符。

ListView.separated须要传入两个IndexedWidgetBuilder,别离是itemBuilder和separatorBuilder。

上面是childrenDelegate的具体实现:

 childrenDelegate = SliverChildBuilderDelegate(         (BuildContext context, int index) {           final int itemIndex = index ~/ 2;           final Widget widget;           if (index.isEven) {             widget = itemBuilder(context, itemIndex);           } else {             widget = separatorBuilder(context, itemIndex);             assert(() {               if (widget == null) {                 throw FlutterError('separatorBuilder cannot return null.');               }               return true;             }());           }           return widget;         },         childCount: _computeActualChildCount(itemCount),         addAutomaticKeepAlives: addAutomaticKeepAlives,         addRepaintBoundaries: addRepaintBoundaries,         addSemanticIndexes: addSemanticIndexes,         semanticIndexCallback: (Widget _, int index) {           return index.isEven ? index ~/ 2 : null;         },       ),

能够看到,如果index是even的话就会应用itemBuilder生成一个widget,如果index是odd的话,就会应用separatorBuilder来生成一个separator的widget。

最初,ListView还有一个更加凋谢的构造函数ListView.custom,custom和其余构造函数不同的中央在于他能够自定义childrenDelegate,从而提供了更多的扩大空间。

ListView的应用

有了下面的构造函数,咱们能够很不便的依据本人的须要来应用ListView,上面是一个简略的应用图片做child的例子:

class ListViewApp extends StatelessWidget{  const ListViewApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    return ListView.builder(      itemCount: 5,      itemBuilder: (BuildContext context, int index) {        return Container(            constraints: const BoxConstraints(maxWidth:100,maxHeight: 100),            child: Image.asset('images/head.jpg')        );      },    );  }}

下面的例子中,咱们应用的是ListView.builder构造函数,返回的Widget中,中的widget个数是5,每个item是由itemBuilder来生成的。

这里咱们把Image封装在一个Container中,并且为Container设置了一个constraints来管制图片的大小。

最终生成的界面如下:

下面的例子中,item之间是没有分隔符的,咱们能够讲下面的例子进行略微的批改一下,应用ListView.separated来结构ListView,如下所示:

class ListViewSeparatedApp extends StatelessWidget{  @override  Widget build(BuildContext context) {    return ListView.separated(           itemCount: 10,           separatorBuilder: (BuildContext context, int index) => const Divider(),           itemBuilder: (BuildContext context, int index) {             return Container(                 constraints: const BoxConstraints(maxWidth:50,maxHeight: 50),               child: Image.asset('images/head.jpg')             );           },         );  }}

这里咱们须要传入separatorBuilder来作为分隔符,为了简略起见,咱们间接应用了Divider这个widget。

最初生成的界面如下:

总结

以上就是ListView的介绍和根本的应用。

本文的例子:https://github.com/ddean2009/learn-flutter.git