flutter ListView

本文最后更新于:2022年4月22日 上午

页面滚动 ListView

ListView

flutter中需要通过 ListView才能够让页面进行滚动

ListView的基本使用

itemExtent指定子元素的 宽或者高(根据展示的方向),显示指定更有利于优化性能,

return ListView(
   // 指定子元素的 宽或者高(根据展示的方向),显示指定更有利于优化性能,
   itemExtent: 100,
   children: [
     Text('1'),
     Text('2'),
     Text('3'),
     Text('4'),
   ],
 );

list.generate

可以快速生成多个元素

return ListView(
  children: List.generate(100, (index) => Text('内容$index')),
);

ListView.separated 分割线

ListView.separated有一个separatorBuilder属性来设置分割线

return ListView.separated(
    itemBuilder: (BuildContext context, int index) {
      return Text('内容$index');
    },
    separatorBuilder: (BuildContext context, int index) {
      // 分割线
      return Divider(
        color: Colors.green,
        thickness: 2,
        indent: 10,
        endIndent: 10,
      );
    },
    itemCount: 100
);

Divider

分割线 widget

ListView.builder

这个性能会相对更好一些,它会在item即将展示的时候再进行构建,且可通过配置来修改预渲染的范围

return ListView.builder(
    itemCount: 50,
    itemExtent: 50,
    itemBuilder: (BuildContext context, int index) {
      return ListTile(
        title: Text('联系人$index'),
        subtitle: Text('1777828617$index'),
        leading: Icon(Icons.person),
        trailing: Icon(Icons.call_end),
      );
    }
);

ListTile

一个由前缀,后缀,title,subTitle的widget,比较适合用来展示联系人

gridView.builder 网格布局

可以给 gridDelegate来设置 SliverGridDelegateWithMaxCrossAxisExtent或者SliverGridDelegateWithFixedCrossAxisCount来修改交叉轴是根据 个数 还是 最大宽 来布局侧轴的排列

可以通过添加 controller来完成返回顶部的操作,返回顶部可以让值设置为负值 -20,这样会有一个反弹的效果

也可以实现向下翻页的效果 _controller.jumpTo(_controller.offset + (屏幕高度 - appbar高度) )

return GridView.builder(
  controller: _controller,
  itemExtent: 100, // 每个item最大主轴方向 高度/宽度 约束,设置这个可以让滚动条正确计算
  itemCount: 100,
  // 设置交叉轴的 item 个数
  // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
  // 通过 主轴 设置占据的最大值来设置 item
  gridDelegate:
    SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 100, mainAxisSpacing: 5, crossAxisSpacing: 5),
  itemBuilder: (BuildContext context, index) {
    return Container(
      width: 100,
      height: 100,
      color: Color.fromRGBO(
          Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), Random().nextInt(10) / 10),
    );
  },
);

physics属性的作用

flutter会根据操作系统来模拟不同的效果,如果需要手动设置效果反弹效果可以手动设置

  • BouncingScrollPhysics ios 效果
  • ClampingScrollPhysics android 效果
  • [NeverScrollableScrollPhysics](https://api.flutter-io.cn/flutter/widgets/NeverScrollableScrollPhysics/NeverScrollableScrollPhysics.html) 不可滚动,可以通过 controller 来操作滚动
  • [AlwaysScrollableScrollPhysics](https://api.flutter-io.cn/flutter/widgets/AlwaysScrollableScrollPhysics/AlwaysScrollableScrollPhysics.html) 永远可以滚动
  • FixedExtentScrollPhysics 精准的停留在某个元素上
ListView.builder(
  physics: BouncingScrollPhysics()
);

文档 https://api.flutter-io.cn/flutter/widgets/ScrollPhysics-class.html

Random

dart 中用来生成随机数的函数

ListWheelScrollView 3d效果滚动

类似于ios 上 时间选择器的效果

ListWheelScrollView({
  Key? key,
  this.controller,
  this.physics,  // 使用 FixedExtentScrollPhysics() 可以精准的停留在某个item上
  this.diameterRatio = RenderListWheelViewport.defaultDiameterRatio, // 直径比例,上下的高度
  this.perspective = RenderListWheelViewport.defaultPerspective,
  this.offAxisFraction = 0.0, // 与轴心的偏移
  this.useMagnifier = false,  // 中间元素是否开启放大镜
  this.magnification = 1.0,  // 中间元素的缩放大小
  this.overAndUnderCenterOpacity = 1.0, // 上下的非选中元素的透明度
  required this.itemExtent,  // 子元素的高度
  this.squeeze = 1.0,
  this.onSelectedItemChanged,  // 选中后的回调
  this.renderChildrenOutsideViewport = false,
  this.clipBehavior = Clip.hardEdge,
  this.restorationId,
  this.scrollBehavior,
  required List<Widget> children,
})

PageView 轮播图滚动效果

PageView({
  Key? key,
  this.scrollDirection = Axis.horizontal, // 水平方向滚动
  this.reverse = false,
  PageController? controller,
  this.physics, 
  this.pageSnapping = true, // 是否可使页面停留在一半效果
  this.onPageChanged,  // 选择后回调
  List<Widget> children = const <Widget>[],
  this.dragStartBehavior = DragStartBehavior.start,
  this.allowImplicitScrolling = false,
  this.restorationId,
  this.clipBehavior = Clip.hardEdge,
  this.scrollBehavior,
  this.padEnds = true,
})

ReorderableListView

ReorderableListView拖拽列表

SingleChildScrollView

ListView的区别是,在高度不足以滚动的时候,用户是感受不到滚动的回弹效果的,ListView如果在元素少的时候依旧可以感受到滚动的回弹效果

SingleChildScrollView 较少用,一般用于 默认情况下不需要滚动,但是某些情况有可能需要滚动的时候

SingleChildScrollView(
  child: Column()
)

Column嵌套 ListView

通过 ExpandedListView进行包裹

因为 Column会讲自己向下级传递的约束伪装成 无限高的,如果直接嵌套 ListView,那么ListView也会将自己的高度设置为无限高,这时候高度就出现问题了,而通过 Expanded就可以避免这问题

例如Column把其它的非弹性元素布局完后发现只剩下600高度,那么就会把600高度的约束平均分给 Expanded

Column(
  children: [
    FlutterLogo(size: 50),
    Expanded(
      child: ListView(children: [])
    )
  ]
)

CustomScrollView 同时使用 ListView 和 GridView

如果项目中出现 同时使用 ListViewGridView的需求的话,就需要通过自定义 ScrollView的方式来实现,因为ListViewGridView本质也是这个,只是往里面插入了一个元素return <Widget>[ sliver ];

SliverList: ListView的本质

SliverGrid: GridView 的本质

通过直接使用 SliverListSliverGrid来构建自定义ListViewGridView

通过给 delegate设置为SliverChildBuilderDelegate来构建子元素

// 同时使用 ListView 和 GridView, 自定义 ScrollView
return CustomScrollView(
  slivers: [
    SliverAppBar(
      title: Text('标题'),
    ),
    SliverList(
        delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
          return ListTile(
            title: Text('联系人$index'),
            subtitle: Text('1777828617$index'),
            leading: Icon(Icons.person),
            trailing: Icon(Icons.call_end),
          );
        }, childCount: 20)),
    SliverGrid(
        delegate: SliverChildBuilderDelegate((BuildContext context, index) {
          return Container(
            width: 100,
            height: 100,
            color: Color.fromRGBO(
                Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), Random().nextInt(10) / 10),
          );
        }, childCount: 20),
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3))
  ],
);

如果要在slivers中放普通元素,需要使用 SliverToBoxAdapter进行一个简单的转接

SliverAppBar

安全区域内的 appBar

SliverSafeArea 安全区域

在通过包裹 SliverSafeArea来让内容在安全区域内展示,放只在刘海和屏幕底部的操作栏遮挡

return CustomScrollView(
  controller: _scrollController,
  slivers: [
    SliverSafeArea(
        sliver: SliverPadding(
          padding: EdgeInsets.all(5),
          sliver: SliverGrid(
            delegate: SliverChildBuilderDelegate((BuildContext context, index) {
              return Container(
                width: 100,
                height: 100,
                color: Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1),
              );
            }, childCount: 50),
            gridDelegate:
            SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, mainAxisSpacing: 5, crossAxisSpacing: 5),
          ),
        ))
  ],
);

SliverPadding

可以在安全区域设置外padding


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。