如需轉載,請註明出處:Flutter學習筆記(25)--ListView實現上拉刷新下拉載入 前面我們有寫過ListView的使用:Flutter學習筆記(12)--列表組件,當列表的數據非常多時,需要使用長列表,比如淘寶後臺的訂單列表,手機通訊錄等,這些列表項數據很多,長列表也是使用ListVie ...
如需轉載,請註明出處:Flutter學習筆記(25)--ListView實現上拉刷新下拉載入
前面我們有寫過ListView的使用:Flutter學習筆記(12)--列表組件,當列表的數據非常多時,需要使用長列表,比如淘寶後臺的訂單列表,手機通訊錄等,這些列表項數據很多,長列表也是使用ListView作為基礎組件,只不過需要添加一個列表項構造器itemBuilder。Flutter的長列表組件其實相當於Android中的RecyclerView,它會自動為您回收列表元素。在創建ListView.builder時,需要傳入兩個參數,一個列表的初始長度,一個itemBuilder函數。ListVIew還支持基於Sliver的延遲構建模型。
基於Sliver的延遲構建模式:
通常可滾動組件的子組件可能會非常多,占用的總高度也會非常大,如果要一次性將子組件全部構建出將會導致性能差的問題出現,為此,Flutter中提出一個Sliver(中文為"薄片"的意思)概念,如果一個可滾動組件支持Sliver模型,那麼該滾動組件可以將子組件分成好多個薄片(Sliver),只有當Sliver出現在視口時才會去構建它,這種模型也成為"基於Sliver的延遲構建模型"。可滾動組件中有很多都支持基於Sliver的延遲構建模型,如ListView、GridView,但是也有不支持該模型的,如SingleChildScrollView
使用ListVIew.separated給列表項之間添加一個分割組件
import 'package:flutter/material.dart'; void main() => runApp(DemoApp()); class DemoApp extends StatelessWidget { //初始化數據源 final List<String> items = new List<String>.generate(200, (i)=>"Item $i"); @override Widget build(BuildContext context) { return new MaterialApp( title: 'SingleChildScrollView Demo', home: new Scaffold( appBar: AppBar( title: new Text('SingleChildScrollView Demo'), ), body:new ListView.separated( //列表滑動到邊界時,顯示iOS的彈出效果 physics: BouncingScrollPhysics(), itemCount: items.length, //列表項構造器 itemBuilder: (context,index){ return ListTile(title: new Text('${items[index]}'),); }, //分割構造器 separatorBuilder: (context,index){ //分割組件 return new Divider(color: Colors.blue,); }, ), ), ); } }
-
下拉刷新
Flutter給我們提供了下拉刷新功能RefreshIndicator的組件,先整體說明一下下麵Demo的代碼邏輯,其實很簡單,body返回一個RefreshIndicator組件,在該組件內的子組件是一個ListView,重點說一下RefreshIndicator的下拉回調方法onRefresh,在回調方法內延遲2秒中後將list內容清空,並且重新給list列表添加新的數據。
import 'package:flutter/material.dart'; void main() => runApp(DemoApp()); class DemoApp extends StatefulWidget { @override State<StatefulWidget> createState() { return new _DemoAppState(); } } class _DemoAppState extends State<DemoApp> { var _items = new List<String>(); @override void initState() { super.initState(); getData(); } @override Widget build(BuildContext context) { return new MaterialApp( title: 'ListView Demo', home: new Scaffold( appBar: new AppBar( title: new Text('ListView Demo'), ), body: new RefreshIndicator( onRefresh: _onRefresh, child: new ListView.separated( physics: BouncingScrollPhysics(), itemBuilder: (context,index){ return ListTile(title:new Text('${_items[index]}')); }, //分割線構造器 separatorBuilder: (context,index){ return new Divider(color: Colors.blue,); }, //_items.length + 1是為了給最後一行的載入loading留出位置 itemCount: _items.length ), ), ), ); } void getData() { //初始數據源 for (int i=0;i<20;i++){ _items.insert(_items.length, "第${_items.length}條原始數據"); print(_items[i]); } } Future<void> _onRefresh() async { await Future.delayed(Duration(seconds: 2)).then((e){ setState(() { _items.clear(); for (int i=0;i<20;i++){ _items.insert(_items.length, "第${_items.length}條下拉刷新後的數據"); } }); }); } @override void dispose() { super.dispose(); } }
這裡需要註意的是,onRefresh回調方法要增加async....await,不然會出現下拉刷新的loading不會消失的問題:
Future<void> _onRefresh() async { await Future.delayed(Duration(seconds: 2)).then((e){ setState(() { _items.clear(); for (int i=0;i<20;i++){ _items.insert(_items.length, "第${_items.length}條下拉刷新後的數據"); } }); }); }
-
上拉載入
先縷一下實現的思路,我們想要實現的效果是每頁20條內容,共5頁的內容,1-4頁末尾數據後要展示載入新數據的loading,到第5頁末尾數據展示“我是有底線的”,因此,我們的itemCount就要是itemCount: _items.length + 1.而不是itemCount: _items.length + 1,這是因為要在最後留出來loading的位置,接下來就是要處理構建LisvtView裡面的每一條item,如果當前item的索引是列表數據的最後一條數據,並且不是最後一頁的話,展示loading,如果當前item的索引是列表數據的最後一條數據,並且是最後一頁的話,展示“我是有底線的”,如果當前item的索引不是列表數據的最後一條,則展示下一條數據的內容。最後要處理的就是當頁面滑動到最後了,要怎麼獲取新的數據。前面我們在寫頁面滑動的部分有講到過controller屬性(此屬性接收一個ScrollController對象,ScrollController的主要作用是控制滾動位置和監聽滾動事件),我們現在需要做的就是通過ListView的controller控制器來判斷頁面是否滑動到了最底部,如果滑動到了最底部,則獲取新的數據並插入到list裡面,最後通過setState通知頁面重新構建。
import 'package:flutter/material.dart'; void main() => runApp(DemoApp()); class DemoApp extends StatefulWidget { @override State<StatefulWidget> createState() { return new _DemoAppState(); } } class _DemoAppState extends State<DemoApp> { ScrollController _controller = new ScrollController(); var _items = new List<String>(); var _mPage = 0; @override void initState() { super.initState(); getData(); //給_controller添加監聽 _controller.addListener((){ //判斷是否滑動到了頁面的最底部 if(_controller.position.pixels == _controller.position.maxScrollExtent){ //如果不是最後一頁數據,則生成新的數據添加到list裡面 if(_mPage < 4){ _retrieveData(); } } }); } @override Widget build(BuildContext context) { return new MaterialApp( title: 'ListView Demo', home: new Scaffold( appBar: new AppBar( title: new Text('ListView Demo'), ), body: new RefreshIndicator( onRefresh: _onRefresh, child: new ListView.separated( controller: _controller, physics: BouncingScrollPhysics(), itemBuilder: (context,index){ //判斷是否構建到了最後一條item if(index == _items.length){ //判斷是不是最後一頁 if(_mPage < 4){ //不是最後一頁,返回一個loading窗 return new Container( padding: EdgeInsets.all(16.0), alignment: Alignment.center, child: SizedBox( width: 24.0, height: 24.0, child: CircularProgressIndicator(strokeWidth: 2.0,), ), ); }else{ //是最後一頁,顯示我是有底線的 return new Container( padding: EdgeInsets.all(16.0), alignment: Alignment.center, child: new Text('我是有底線的!!!',style:TextStyle(color: Colors.blue),), ); } }else{ return ListTile(title:new Text('${_items[index]}')); } }, //分割線構造器 separatorBuilder: (context,index){ return new Divider(color: Colors.blue,); }, //_items.length + 1是為了給最後一行的載入loading留出位置 itemCount: _items.length + 1 ), ), ), ); } void getData() { //初始數據源 for (int i=0;i<20;i++){ _items.insert(_items.length, "第${_items.length}條原始數據"); print(_items[i]); } } void _retrieveData() { //上拉載入新的數據 _mPage++; Future.delayed(Duration(seconds: 2)).then((e){ for (int i=0;i<20;i++){ _items.insert(_items.length, "這是新載入的第${_items.length}條數據"); } setState(() { }); }); } Future<void> _onRefresh() async { await Future.delayed(Duration(seconds: 2)).then((e){ setState(() { _mPage = 0; _items.clear(); for (int i=0;i<20;i++){ _items.insert(_items.length, "第${_items.length}條下拉刷新後的數據"); } }); }); } @override void dispose() { //移除監聽,防止記憶體泄漏 _controller.dispose(); super.dispose(); } }
以上就是今天下拉刷新和上拉載入的全部內容了,如果有錯誤的地方或者有任何疑問,歡迎留言!!!