Flutter listview如何實現(xiàn)下拉刷新上拉加載更多功能
下拉刷新
在Flutter中系統(tǒng)已經(jīng)為我們提供了google material design的刷新功能 , 樣式與原生Android一樣.
我們可以使用RefreshIndicator組件來實現(xiàn)Flutter中的下拉刷新,下面?zhèn)冞€是先來看下如何使用吧
RefreshIndicator
構(gòu)造方法:
const RefreshIndicator({ Key key, @required this.child, this.displacement: 40.0, //觸發(fā)下拉刷新的距離 @required this.onRefresh, //下拉回調(diào)方法 this.color, //進度指示器前景色 默認為系統(tǒng)主題色 this.backgroundColor, //背景色 this.notificationPredicate: defaultScrollNotificationPredicate, })
然后我們看一下效果以及實現(xiàn)方式:
然后我們看一下代碼:
class _MyHomePageState extends State<MyHomePage> { List list = new List(); //列表要展示的數(shù)據(jù) @override void initState() { // TODO: implement initState super.initState(); getData(); } /** * 初始化list數(shù)據(jù) 加延時模仿網(wǎng)絡(luò)請求 */ Future getData() async { await Future.delayed(Duration(seconds: 2), () { setState(() { list = List.generate(15, (i) => '哈嘍,我是原始數(shù)據(jù) $i'); }); }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: new Text(widget.title), ), body: RefreshIndicator( onRefresh: _onRefresh, child: ListView.builder( itemBuilder: _renderRow, itemCount: list.length, ), ), ); } Widget _renderRow(BuildContext context, int index) { return ListTile( title: Text(list[index]), ); } /** * 下拉刷新方法,為list重新賦值 */ Future<Null> _onRefresh() async { await Future.delayed(Duration(seconds: 3), () { print('refresh'); setState(() { list = List.generate(20, (i) => '哈嘍,我是新刷新的 $i'); }); }); } }
代碼不復雜,我們一步步分析:
MyHomePage 只是返回一個State,這里省略了.
首先body里我們返回了一個RefreshIndicator,這個組件自帶下拉回調(diào),然后里面我們包裹了一個listview,
然后使用List.generate()方法來創(chuàng)建了一個長度為15的List,并把List里的值賦值給ListView Item中的ListTile。
下拉回調(diào)onRefresh 我們返回了一個改變list的方法 .
在上面的代碼中我們使用_onRefresh()方法來處理下拉刷新的回調(diào)
/** * 下拉刷新方法,為list重新賦值 */ Future<Null> _onRefresh() async { await Future.delayed(Duration(seconds: 3), () { print('refresh'); setState(() { list = List.generate(20, (i) => '哈嘍,我是新刷新的 $i'); }); }); }
其中 Future.delayed()方法可以選擇延遲處理任務(wù),這里我們假設(shè)網(wǎng)絡(luò)的延遲是3秒.
這樣一個簡單的下拉刷新就實現(xiàn)了.
上拉加載更多
對于加載更多的組件在Flutter中是沒有提供的,所以在這里我們就需要考慮如何實現(xiàn)的。
在ListView中有一個ScrollController屬性,它就是專門來控制ListView滑動事件,在這里我們可以根據(jù)ListView的位置來判斷是否滑動到了底部來做加載更多的處理。
在這里我們可以使用如下代碼來判斷ListView 是否滑動到了底部
@override void initState() { // TODO: implement initState super.initState(); getData(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print('滑動到了最底部'); _getMore(); } }); }
_scrollController是我們初始化的ScrollController對象,通過監(jiān)聽我們可以判斷現(xiàn)在的位置是否是最大的下滑位置來判斷是否下滑到了底部。
看一下代碼和效果:
class _MyHomePageState extends State<MyHomePage> { List list = new List(); //列表要展示的數(shù)據(jù) ScrollController _scrollController = ScrollController(); //listview的控制器 int _page = 1; //加載的頁數(shù) bool isLoading = false; //是否正在加載數(shù)據(jù) @override void initState() { // TODO: implement initState super.initState(); getData(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print('滑動到了最底部'); _getMore(); } }); } /** * 初始化list數(shù)據(jù) 加延時模仿網(wǎng)絡(luò)請求 */ Future getData() async { await Future.delayed(Duration(seconds: 2), () { setState(() { list = List.generate(15, (i) => '哈嘍,我是原始數(shù)據(jù) $i'); }); }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: new Text(widget.title), ), body: RefreshIndicator( onRefresh: _onRefresh, child: ListView.builder( itemBuilder: _renderRow, itemCount: list.length, controller: _scrollController, ), ), // This trailing comma makes auto-formatting nicer for build methods. ); } Widget _renderRow(BuildContext context, int index) { return ListTile( title: Text(list[index]), ); } /** * 下拉刷新方法,為list重新賦值 */ Future<Null> _onRefresh() async { await Future.delayed(Duration(seconds: 3), () { print('refresh'); setState(() { list = List.generate(20, (i) => '哈嘍,我是新刷新的 $i'); }); }); } /** * 上拉加載更多 */ Future _getMore() async { if (!isLoading) { setState(() { isLoading = true; }); await Future.delayed(Duration(seconds: 1), () { print('加載更多'); setState(() { list.addAll(List.generate(5, (i) => '第$_page次上拉來的數(shù)據(jù)')); _page++; isLoading = false; }); }); } } @override void dispose() { // TODO: implement dispose super.dispose(); _scrollController.dispose(); } }
滑動到底部的時候,我們執(zhí)行加載更多的方法,給list數(shù)據(jù)多加5條,這次我們把延遲改到了1秒:
/** * 上拉加載更多 */ Future _getMore() async { if (!isLoading) { setState(() { isLoading = true; }); await Future.delayed(Duration(seconds: 1), () { print('加載更多'); setState(() { list.addAll(List.generate(5, (i) => '第$_page次上拉來的數(shù)據(jù)')); _page++; isLoading = false; }); }); } }
是的,看著上面的效果我們已經(jīng)實現(xiàn)了下拉加載更多,但是因為我們是滑動到底部觸發(fā)的,如果在正在請求的過程中多次下拉就會造成多次加載更多的情況,所以我們還得對這個做下處理為了避免多次觸發(fā),我們加了一個isLoading,在上拉方法執(zhí)行的過程中不會再次執(zhí)行.
可以看到,我們僅僅在上面代碼的基礎(chǔ)上加上了一個isLoading的變量,當這個變量的值為true時,就不會觸發(fā)加載更多的操作。
而因為是網(wǎng)絡(luò)請求,可能需要分頁,所以我們加了個page參數(shù)來查看是第幾次觸發(fā)上拉加載.
因為我們加了個監(jiān)聽,在組件卸載掉的時候記得移除這個監(jiān)聽,所以:
@override void dispose() { // TODO: implement dispose super.dispose(); _scrollController.dispose(); }
這個一定不要忘記,養(yǎng)成好習慣,每次加了監(jiān)聽都跑到這個方法里移除掉.
這樣,我們一個簡單的上拉加載更多的功能就實現(xiàn)了.
但是還有個問題,沒有用戶交互啊,加載的時候要有個提示,于是我們嘗試上拉的時候展示一個加載中的組件給用戶:
首先我們創(chuàng)建加載更多時顯示的Vidget
/** * 加載更多時顯示的組件,給用戶提示 */ Widget _getMoreWidget() { return Center( child: Padding( padding: EdgeInsets.all(10.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Text( '加載中... ', style: TextStyle(fontSize: 16.0), ), CircularProgressIndicator(strokeWidth: 1.0,) ], ), ), ); }
然后我們在listview的itemcount那里把count+1,相當于我們給listview加了個尾部的組件.
body: RefreshIndicator( onRefresh: _onRefresh, child: ListView.builder( itemBuilder: _renderRow, itemCount: list.length + 1, //這里!這里!這里! controller: _scrollController, ),
看一下效果是否滿意:
嗯,基本符合要求,感覺那個刷新圖標加的有點丑,畫蛇添足了,不過功能都是ok了的.
當然, 大家可以根據(jù)自己的需要去自己實現(xiàn)想要的樣式
看一下全部的代碼:
/* * Created by 李卓原 on 2018/9/13. * email: zhuoyuan93@gmail.com * */ class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { List list = new List(); //列表要展示的數(shù)據(jù) ScrollController _scrollController = ScrollController(); //listview的控制器 int _page = 1; //加載的頁數(shù) bool isLoading = false; //是否正在加載數(shù)據(jù) @override void initState() { // TODO: implement initState super.initState(); getData(); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print('滑動到了最底部'); _getMore(); } }); } /** * 初始化list數(shù)據(jù) 加延時模仿網(wǎng)絡(luò)請求 */ Future getData() async { await Future.delayed(Duration(seconds: 2), () { setState(() { list = List.generate(15, (i) => '哈嘍,我是原始數(shù)據(jù) $i'); }); }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: new Text(widget.title), ), body: RefreshIndicator( onRefresh: _onRefresh, child: ListView.builder( itemBuilder: _renderRow, itemCount: list.length + 1, controller: _scrollController, ), ), // This trailing comma makes auto-formatting nicer for build methods. ); } Widget _renderRow(BuildContext context, int index) { if (index < list.length) { return ListTile( title: Text(list[index]), ); } return _getMoreWidget(); } /** * 下拉刷新方法,為list重新賦值 */ Future<Null> _onRefresh() async { await Future.delayed(Duration(seconds: 3), () { print('refresh'); setState(() { list = List.generate(20, (i) => '哈嘍,我是新刷新的 $i'); }); }); } /** * 上拉加載更多 */ Future _getMore() async { if (!isLoading) { setState(() { isLoading = true; }); await Future.delayed(Duration(seconds: 1), () { print('加載更多'); setState(() { list.addAll(List.generate(5, (i) => '第$_page次上拉來的數(shù)據(jù)')); _page++; isLoading = false; }); }); } } /** * 加載更多時顯示的組件,給用戶提示 */ Widget _getMoreWidget() { return Center( child: Padding( padding: EdgeInsets.all(10.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Text( '加載中...', style: TextStyle(fontSize: 16.0), ), CircularProgressIndicator( strokeWidth: 1.0, ) ], ), ), ); } @override void dispose() { // TODO: implement dispose super.dispose(); _scrollController.dispose(); } }
總結(jié):
- RefreshIndicator可以顯示下拉刷新
- 使用ScrollController可以監(jiān)聽滑動事件,判斷當前view所處的位置
- 可以根據(jù)item所處的位置來處理加載更多顯示效果
到此這篇關(guān)于Flutter listview如何實現(xiàn)下拉刷新上拉加載更多功能的文章就介紹到這了,更多相關(guān)Flutter listview下拉刷新上拉加載更多內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android解決dialog彈出時無法捕捉Activity的back事件的方法
這篇文章主要介紹了Android解決dialog彈出時無法捕捉Activity的back事件的方法,涉及Android操作Activity事件的相關(guān)技巧,需要的朋友可以參考下2015-05-05Android編程之Application設(shè)置全局變量及傳值用法實例分析
這篇文章主要介紹了Android編程之Application設(shè)置全局變量及傳值用法,結(jié)合實例形式較為詳細的分析了全局變量及傳值的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-12-12Android?Gradle?插件自定義Plugin實現(xiàn)注意事項
這篇文章主要介紹了Android?Gradle?插件自定義Plugin實現(xiàn)注意事項,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-06-06Android 中使用EditText 點擊全選再次點擊取消全選功能
這篇文章主要介紹了Android 中使用EditText 點擊全選再次點擊取消全選功能,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2016-12-12Win10下Android App安裝配置開發(fā)環(huán)境
這篇文章主要為大家詳細介紹了Win10下Android App安裝配置開發(fā)環(huán)境,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07