列表是最常用的一個組件,通常相對于比較大的數據量都會使用到列表來顯示。
滾動監聽
當使用 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: <Widget>[
// 頭部內容
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),
),
],
)