经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Flutter » 查看文章
Flutter listview如何实现下拉刷新上拉加载更多功能
来源:jb51  时间:2021/8/5 17:20:31  对本文有异议

下拉刷新

在Flutter中系统已经为我们提供了google material design的刷新功能 , 样式与原生Android一样.

我们可以使用RefreshIndicator组件来实现Flutter中的下拉刷新,下面们还是先来看下如何使用吧

RefreshIndicator

构造方法:

  1. const RefreshIndicator({
  2. Key key,
  3. @required this.child,
  4. this.displacement: 40.0, //触发下拉刷新的距离
  5. @required this.onRefresh, //下拉回调方法
  6. this.color, //进度指示器前景色 默认为系统主题色
  7. this.backgroundColor, //背景色
  8. this.notificationPredicate: defaultScrollNotificationPredicate,
  9. })

然后我们看一下效果以及实现方式:

然后我们看一下代码:

  1. class _MyHomePageState extends State<MyHomePage> {
  2. List list = new List(); //列表要展示的数据
  3.  
  4. @override
  5. void initState() {
  6. // TODO: implement initState
  7. super.initState();
  8. getData();
  9. }
  10.  
  11. /**
  12. * 初始化list数据 加延时模仿网络请求
  13. */
  14. Future getData() async {
  15. await Future.delayed(Duration(seconds: 2), () {
  16. setState(() {
  17. list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
  18. });
  19. });
  20. }
  21.  
  22. @override
  23. Widget build(BuildContext context) {
  24. return new Scaffold(
  25. appBar: new AppBar(
  26. // Here we take the value from the MyHomePage object that was created by
  27. // the App.build method, and use it to set our appbar title.
  28. title: new Text(widget.title),
  29. ),
  30. body: RefreshIndicator(
  31. onRefresh: _onRefresh,
  32. child: ListView.builder(
  33. itemBuilder: _renderRow,
  34. itemCount: list.length,
  35. ),
  36. ),
  37. );
  38. }
  39.  
  40. Widget _renderRow(BuildContext context, int index) {
  41. return ListTile(
  42. title: Text(list[index]),
  43. );
  44. }
  45.  
  46. /**
  47. * 下拉刷新方法,为list重新赋值
  48. */
  49. Future<Null> _onRefresh() async {
  50. await Future.delayed(Duration(seconds: 3), () {
  51. print('refresh');
  52. setState(() {
  53. list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
  54. });
  55. });
  56. }
  57. }

代码不复杂,我们一步步分析:

MyHomePage 只是返回一个State,这里省略了.

首先body里我们返回了一个RefreshIndicator,这个组件自带下拉回调,然后里面我们包裹了一个listview,

然后使用List.generate()方法来创建了一个长度为15的List,并把List里的值赋值给ListView Item中的ListTile。

下拉回调onRefresh 我们返回了一个改变list的方法 .

在上面的代码中我们使用_onRefresh()方法来处理下拉刷新的回调

  1. /**
  2. * 下拉刷新方法,为list重新赋值
  3. */
  4. Future<Null> _onRefresh() async {
  5. await Future.delayed(Duration(seconds: 3), () {
  6. print('refresh');
  7. setState(() {
  8. list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
  9. });
  10. });
  11. }

其中 Future.delayed()方法可以选择延迟处理任务,这里我们假设网络的延迟是3秒.

这样一个简单的下拉刷新就实现了.

上拉加载更多

对于加载更多的组件在Flutter中是没有提供的,所以在这里我们就需要考虑如何实现的。

在ListView中有一个ScrollController属性,它就是专门来控制ListView滑动事件,在这里我们可以根据ListView的位置来判断是否滑动到了底部来做加载更多的处理。

在这里我们可以使用如下代码来判断ListView 是否滑动到了底部

  1. @override
  2. void initState() {
  3. // TODO: implement initState
  4. super.initState();
  5. getData();
  6. _scrollController.addListener(() {
  7. if (_scrollController.position.pixels ==
  8. _scrollController.position.maxScrollExtent) {
  9. print('滑动到了最底部');
  10. _getMore();
  11. }
  12. });
  13. }

_scrollController是我们初始化的ScrollController对象,通过监听我们可以判断现在的位置是否是最大的下滑位置来判断是否下滑到了底部。

看一下代码和效果:

  1. class _MyHomePageState extends State<MyHomePage> {
  2. List list = new List(); //列表要展示的数据
  3. ScrollController _scrollController = ScrollController(); //listview的控制器
  4. int _page = 1; //加载的页数
  5. bool isLoading = false; //是否正在加载数据
  6.  
  7. @override
  8. void initState() {
  9. // TODO: implement initState
  10. super.initState();
  11. getData();
  12. _scrollController.addListener(() {
  13. if (_scrollController.position.pixels ==
  14. _scrollController.position.maxScrollExtent) {
  15. print('滑动到了最底部');
  16. _getMore();
  17. }
  18. });
  19. }
  20.  
  21. /**
  22. * 初始化list数据 加延时模仿网络请求
  23. */
  24. Future getData() async {
  25. await Future.delayed(Duration(seconds: 2), () {
  26. setState(() {
  27. list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
  28. });
  29. });
  30. }
  31.  
  32. @override
  33. Widget build(BuildContext context) {
  34. return new Scaffold(
  35. appBar: new AppBar(
  36. // Here we take the value from the MyHomePage object that was created by
  37. // the App.build method, and use it to set our appbar title.
  38. title: new Text(widget.title),
  39. ),
  40. body: RefreshIndicator(
  41. onRefresh: _onRefresh,
  42. child: ListView.builder(
  43. itemBuilder: _renderRow,
  44. itemCount: list.length,
  45. controller: _scrollController,
  46. ),
  47. ),
  48. // This trailing comma makes auto-formatting nicer for build methods.
  49. );
  50. }
  51.  
  52. Widget _renderRow(BuildContext context, int index) {
  53. return ListTile(
  54. title: Text(list[index]),
  55. );
  56. }
  57.  
  58. /**
  59. * 下拉刷新方法,为list重新赋值
  60. */
  61. Future<Null> _onRefresh() async {
  62. await Future.delayed(Duration(seconds: 3), () {
  63. print('refresh');
  64. setState(() {
  65. list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
  66. });
  67. });
  68. }
  69.  
  70. /**
  71. * 上拉加载更多
  72. */
  73. Future _getMore() async {
  74. if (!isLoading) {
  75. setState(() {
  76. isLoading = true;
  77. });
  78. await Future.delayed(Duration(seconds: 1), () {
  79. print('加载更多');
  80. setState(() {
  81. list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
  82. _page++;
  83. isLoading = false;
  84. });
  85. });
  86. }
  87. }
  88.  
  89. @override
  90. void dispose() {
  91. // TODO: implement dispose
  92. super.dispose();
  93. _scrollController.dispose();
  94. }
  95. }

滑动到底部的时候,我们执行加载更多的方法,给list数据多加5条,这次我们把延迟改到了1秒:

  1. /**
  2. * 上拉加载更多
  3. */
  4. Future _getMore() async {
  5. if (!isLoading) {
  6. setState(() {
  7. isLoading = true;
  8. });
  9. await Future.delayed(Duration(seconds: 1), () {
  10. print('加载更多');
  11. setState(() {
  12. list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
  13. _page++;
  14. isLoading = false;
  15. });
  16. });
  17. }
  18. }

是的,看着上面的效果我们已经实现了下拉加载更多,但是因为我们是滑动到底部触发的,如果在正在请求的过程中多次下拉就会造成多次加载更多的情况,所以我们还得对这个做下处理为了避免多次触发,我们加了一个isLoading,在上拉方法执行的过程中不会再次执行.

可以看到,我们仅仅在上面代码的基础上加上了一个isLoading的变量,当这个变量的值为true时,就不会触发加载更多的操作。

而因为是网络请求,可能需要分页,所以我们加了个page参数来查看是第几次触发上拉加载.

因为我们加了个监听,在组件卸载掉的时候记得移除这个监听,所以:

  1. @override
  2. void dispose() {
  3. // TODO: implement dispose
  4. super.dispose();
  5. _scrollController.dispose();
  6. }

这个一定不要忘记,养成好习惯,每次加了监听都跑到这个方法里移除掉.

这样,我们一个简单的上拉加载更多的功能就实现了.

但是还有个问题,没有用户交互啊,加载的时候要有个提示,于是我们尝试上拉的时候展示一个加载中的组件给用户:

首先我们创建加载更多时显示的Vidget

  1. /**
  2. * 加载更多时显示的组件,给用户提示
  3. */
  4. Widget _getMoreWidget() {
  5. return Center(
  6. child: Padding(
  7. padding: EdgeInsets.all(10.0),
  8. child: Row(
  9. mainAxisAlignment: MainAxisAlignment.center,
  10. crossAxisAlignment: CrossAxisAlignment.center,
  11. children: <Widget>[
  12. Text(
  13. '加载中... ',
  14. style: TextStyle(fontSize: 16.0),
  15. ),
  16. CircularProgressIndicator(strokeWidth: 1.0,)
  17. ],
  18. ),
  19. ),
  20. );
  21. }
  22.  

然后我们在listview的itemcount那里把count+1,相当于我们给listview加了个尾部的组件.

  1. body: RefreshIndicator(
  2. onRefresh: _onRefresh,
  3. child: ListView.builder(
  4. itemBuilder: _renderRow,
  5. itemCount: list.length + 1, //这里!这里!这里!
  6. controller: _scrollController,
  7. ),

看一下效果是否满意:

嗯,基本符合要求,感觉那个刷新图标加的有点丑,画蛇添足了,不过功能都是ok了的.

当然, 大家可以根据自己的需要去自己实现想要的样式

看一下全部的代码:

  1. /*
  2. * Created by 李卓原 on 2018/9/13.
  3. * email: zhuoyuan93@gmail.com
  4. *
  5. */
  6. class MyHomePage extends StatefulWidget {
  7. MyHomePage({Key key, this.title}) : super(key: key);
  8. final String title;
  9.  
  10. @override
  11. _MyHomePageState createState() => new _MyHomePageState();
  12. }
  13.  
  14. class _MyHomePageState extends State<MyHomePage> {
  15. List list = new List(); //列表要展示的数据
  16. ScrollController _scrollController = ScrollController(); //listview的控制器
  17. int _page = 1; //加载的页数
  18. bool isLoading = false; //是否正在加载数据
  19.  
  20. @override
  21. void initState() {
  22. // TODO: implement initState
  23. super.initState();
  24. getData();
  25. _scrollController.addListener(() {
  26. if (_scrollController.position.pixels ==
  27. _scrollController.position.maxScrollExtent) {
  28. print('滑动到了最底部');
  29. _getMore();
  30. }
  31. });
  32. }
  33.  
  34. /**
  35. * 初始化list数据 加延时模仿网络请求
  36. */
  37. Future getData() async {
  38. await Future.delayed(Duration(seconds: 2), () {
  39. setState(() {
  40. list = List.generate(15, (i) => '哈喽,我是原始数据 $i');
  41. });
  42. });
  43. }
  44.  
  45. @override
  46. Widget build(BuildContext context) {
  47. return new Scaffold(
  48. appBar: new AppBar(
  49. // Here we take the value from the MyHomePage object that was created by
  50. // the App.build method, and use it to set our appbar title.
  51. title: new Text(widget.title),
  52. ),
  53. body: RefreshIndicator(
  54. onRefresh: _onRefresh,
  55. child: ListView.builder(
  56. itemBuilder: _renderRow,
  57. itemCount: list.length + 1,
  58. controller: _scrollController,
  59. ),
  60. ),
  61. // This trailing comma makes auto-formatting nicer for build methods.
  62. );
  63. }
  64.  
  65. Widget _renderRow(BuildContext context, int index) {
  66. if (index < list.length) {
  67. return ListTile(
  68. title: Text(list[index]),
  69. );
  70. }
  71. return _getMoreWidget();
  72. }
  73.  
  74. /**
  75. * 下拉刷新方法,为list重新赋值
  76. */
  77. Future<Null> _onRefresh() async {
  78. await Future.delayed(Duration(seconds: 3), () {
  79. print('refresh');
  80. setState(() {
  81. list = List.generate(20, (i) => '哈喽,我是新刷新的 $i');
  82. });
  83. });
  84. }
  85.  
  86. /**
  87. * 上拉加载更多
  88. */
  89. Future _getMore() async {
  90. if (!isLoading) {
  91. setState(() {
  92. isLoading = true;
  93. });
  94. await Future.delayed(Duration(seconds: 1), () {
  95. print('加载更多');
  96. setState(() {
  97. list.addAll(List.generate(5, (i) => '第$_page次上拉来的数据'));
  98. _page++;
  99. isLoading = false;
  100. });
  101. });
  102. }
  103. }
  104.  
  105. /**
  106. * 加载更多时显示的组件,给用户提示
  107. */
  108. Widget _getMoreWidget() {
  109. return Center(
  110. child: Padding(
  111. padding: EdgeInsets.all(10.0),
  112. child: Row(
  113. mainAxisAlignment: MainAxisAlignment.center,
  114. crossAxisAlignment: CrossAxisAlignment.center,
  115. children: <Widget>[
  116. Text(
  117. '加载中...',
  118. style: TextStyle(fontSize: 16.0),
  119. ),
  120. CircularProgressIndicator(
  121. strokeWidth: 1.0,
  122. )
  123. ],
  124. ),
  125. ),
  126. );
  127. }
  128.  
  129. @override
  130. void dispose() {
  131. // TODO: implement dispose
  132. super.dispose();
  133. _scrollController.dispose();
  134. }
  135. }
  136.  

总结:

  • RefreshIndicator可以显示下拉刷新
  • 使用ScrollController可以监听滑动事件,判断当前view所处的位置
  • 可以根据item所处的位置来处理加载更多显示效果

到此这篇关于Flutter listview如何实现下拉刷新上拉加载更多功能的文章就介绍到这了,更多相关Flutter listview下拉刷新上拉加载更多内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号