列表是最常用的一个组件,通常相对于比较大的数据量都会使用到列表来显示。
滚动监听
当使用 ScrollView、ListView、PageView 等带有滚动条的组件的时候,如何监听它的滚动信息呢?Flutter 内建了 Notification 机制,一个 Widget 可以通过 Notification 将一个事件冒泡到 Widget Tree 的上层节点。监听一个 Notification 的方法是用 NotificationListener 包裹需要监听事件的子视图。
new LayoutBuilder(builder: (context, constraints) {
return new NotificationListener(
onNotification: (ScrollNotification note) {
print(note.metrics.pixels.toInt()); // 滚动位置。
},
child: new ListView.builder(
itemCount: 40,
itemBuilder: (BuildContext context, int index) {
return new Container(
padding: const EdgeInsets.all(8.0),
child: new Text('今天吃什么?'),
);
},
),
);
});
当给 ListView 外包裹一个 NotificationListener 的时候,就可以在 NotificationListener 的 onNotification 事件里监听滚动条的信息。
在 onNotification 的回调函数里会有一个 ScrollNotification 的对象,此对象只有两个属性:metrics 和 context。
metrics 记录着滚动条的信息,它有以下只读属性:
- atEdge → bool - 是否能够正好匹配 min/maxScrollExtent。
- axis → Axis - 视图滚动的轴。
- axisDirection → AxisDirection - 滚动的方向。
- extentAfter → double - 位于可滚动视口的“下方”的数量。
- extentBefore → double - 位于可滚动视口的“上方”的数量。
- extentInside → double - 可见内容的数量。
- maxScrollExtent → double - 滚动最大的范围。
- minScrollExtent → double - 滚动最小的范围。
- outOfRange → bool - 是否在范围内。
- pixels → double - 当前滚动位置。
- viewportDimension → double - 沿着 axisDirection 的视口范围。
控制器
在 ScrollView、ListView、PageView 里都提供了 controller 属性,此属性用于控制滚动条的行为。比如,指定滚动到某个位置,实现回到顶部等功能。
实现滚动到某个位置。
// 先创建一个 controller
var controller = new ScrollController();
// 在 ListView 上添加 controller
new ListView.builder(
itemCount: 40,
controller: this.controller,
itemBuilder: (BuildContext context, int index) {
return new Container(
padding: const EdgeInsets.all(8.0),
child: new Text('今天吃什么? $index'),
);
},
),
// 点击时滚动到某个位置。
onPressed: () {
this.controller.animateTo(
100.0,
duration: new Duration(milliseconds: 300), // 300ms
curve: Curves.bounceIn, // 动画方式
);
},
对应的有一个没有动画效果的 API:jumpTo(double index)
。
下拉刷新
Flutter 是提供了一个下拉刷新的组件的,你可以使用原生的下拉刷新组件,或者使用第三方的组件。
使用第三方组件
使用的是一个叫 pull_to_refresh
的组件,它同时支持下拉刷新和上拉加载功能。
安装依赖:
dependencies:
pull_to_refresh: ^1.1.5
使用它:
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'dart:async';
new SmartRefresher(
enablePullDown: true,
// enablePullUp: true,
onRefresh: (bool up) async {
await new Future.delayed(const Duration(milliseconds: 2000)); // 等待异步操作
this.controller2.sendBack(true, RefreshStatus.completed); // 设置状态为完成
setState(() {}); // 更新界面
},
onOffsetChange: (bool up, double offset) {},
headerBuilder: (context, mode) {
return new ClassicIndicator(
mode: mode,
height: 45.0,
releaseText: '松开手刷新',
refreshingText: '刷新中',
completeText: '刷新完成',
failedText: '刷新失败',
idleText: '下拉刷新',
);
},
controller: this.controller2, // 控制器
child: new ListView.builder(
itemCount: 1000,
controller: this.controller,
itemBuilder: (BuildContext context, int index) {
return new Container(
padding: const EdgeInsets.all(8.0),
child: new Text('今天吃什么? $index'),
);
},
),
),
上拉加载
Flutter 也提供上来加载功能。
越界回弹效果
在 ScrollView 的里有一个属性:physics
是用于设置回弹效果的。
physics 的默认值是 ScrollPhysics
,是没有回弹效果的。那么看看其他值:
- AlwaysScrollableScrollPhysics - 在 Android 上,默认情况下会压缩过度滚动并导致过度滚动。在 iOS 上,过度滚动将加载弹簧,该弹簧将在发布时将滚动视图返回到其正常范围。
- BouncingScrollPhysics - 用于设置列表在滑出界时,产生一个回弹效果。
- ClampingScrollPhysics - 滚动物理环境允许滚动偏移超出内容范围,但随后将内容反弹回这些边界的边缘。
使用 BouncingScrollPhysics 即可实现回弹效果。
new ListView(
physics: new BouncingScrollPhysics(),
)
new CustomScrollView(
physics: new BouncingScrollPhysics(),
)
滚动视觉差
new CustomScrollView(
physics: new BouncingScrollPhysics(),
// 需要使用 slivers 才可以
slivers: [
// 头部内容
new SliverAppBar(
// 高度
expandedHeight: 256.0,
pinned: true,
floating: !true,
snap: !true,
flexibleSpace: new FlexibleSpaceBar(
// 标题
title: Text('标题'),
centerTitle: true,
// 背景图
background: new Image.network(
'http://img.anfone.net/Outside/anfone/201666/2016661523021277.jpg',
fit: BoxFit.cover,
),
),
),
// 列表内容
new SliverList(
delegate: new SliverChildBuilderDelegate((ctx, index) {
return new Text('item: $index');
}, childCount: this.count),
),
],
)