经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Flutter学习笔记(25)--ListView实现上拉刷新下拉加载
来源:cnblogs  作者:CurtisWgh  时间:2019/9/2 9:10:27  对本文有异议

如需转载,请注明出处: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给列表项之间添加一个分割组件

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(DemoApp());
  3. class DemoApp extends StatelessWidget {
  4. //初始化数据源
  5. final List<String> items = new List<String>.generate(200, (i)=>"Item $i");
  6. @override
  7. Widget build(BuildContext context) {
  8. return new MaterialApp(
  9. title: 'SingleChildScrollView Demo',
  10. home: new Scaffold(
  11. appBar: AppBar(
  12. title: new Text('SingleChildScrollView Demo'),
  13. ),
  14. body:new ListView.separated(
  15. //列表滑动到边界时,显示iOS的弹出效果
  16. physics: BouncingScrollPhysics(),
  17. itemCount: items.length,
  18. //列表项构造器
  19. itemBuilder: (context,index){
  20. return ListTile(title: new Text('${items[index]}'),);
  21. },
  22. //分割构造器
  23. separatorBuilder: (context,index){
  24. //分割组件
  25. return new Divider(color: Colors.blue,);
  26. },
  27. ),
  28. ),
  29. );
  30. }
  31. }

  • 下拉刷新

 Flutter给我们提供了下拉刷新功能RefreshIndicator的组件,先整体说明一下下面Demo的代码逻辑,其实很简单,body返回一个RefreshIndicator组件,在该组件内的子组件是一个ListView,重点说一下RefreshIndicator的下拉回调方法onRefresh,在回调方法内延迟2秒中后将list内容清空,并且重新给list列表添加新的数据。

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(DemoApp());
  3. class DemoApp extends StatefulWidget {
  4. @override
  5. State<StatefulWidget> createState() {
  6. return new _DemoAppState();
  7. }
  8. }
  9. class _DemoAppState extends State<DemoApp> {
  10. var _items = new List<String>();
  11. @override
  12. void initState() {
  13. super.initState();
  14. getData();
  15. }
  16. @override
  17. Widget build(BuildContext context) {
  18. return new MaterialApp(
  19. title: 'ListView Demo',
  20. home: new Scaffold(
  21. appBar: new AppBar(
  22. title: new Text('ListView Demo'),
  23. ),
  24. body: new RefreshIndicator(
  25. onRefresh: _onRefresh,
  26. child: new ListView.separated(
  27. physics: BouncingScrollPhysics(),
  28. itemBuilder: (context,index){
  29. return ListTile(title:new Text('${_items[index]}'));
  30. },
  31. //分割线构造器
  32. separatorBuilder: (context,index){
  33. return new Divider(color: Colors.blue,);
  34. },
  35. //_items.length + 1是为了给最后一行的加载loading留出位置
  36. itemCount: _items.length
  37. ),
  38. ),
  39. ),
  40. );
  41. }
  42. void getData() {
  43. //初始数据源
  44. for (int i=0;i<20;i++){
  45. _items.insert(_items.length, "第${_items.length}条原始数据");
  46. print(_items[i]);
  47. }
  48. }
  49. Future<void> _onRefresh() async {
  50. await Future.delayed(Duration(seconds: 2)).then((e){
  51. setState(() {
  52. _items.clear();
  53. for (int i=0;i<20;i++){
  54. _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
  55. }
  56. });
  57. });
  58. }
  59. @override
  60. void dispose() {
  61. super.dispose();
  62. }
  63. }

这里需要注意的是,onRefresh回调方法要增加async....await,不然会出现下拉刷新的loading不会消失的问题:

  1. Future<void> _onRefresh() async {
  2. await Future.delayed(Duration(seconds: 2)).then((e){
  3. setState(() {
  4. _items.clear();
  5. for (int i=0;i<20;i++){
  6. _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
  7. }
  8. });
  9. });
  10. }

 

  • 上拉加载

 先缕一下实现的思路,我们想要实现的效果是每页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通知页面重新构建。

 

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(DemoApp());
  3. class DemoApp extends StatefulWidget {
  4. @override
  5. State<StatefulWidget> createState() {
  6. return new _DemoAppState();
  7. }
  8. }
  9. class _DemoAppState extends State<DemoApp> {
  10. ScrollController _controller = new ScrollController();
  11. var _items = new List<String>();
  12. var _mPage = 0;
  13. @override
  14. void initState() {
  15. super.initState();
  16. getData();
  17. //给_controller添加监听
  18. _controller.addListener((){
  19. //判断是否滑动到了页面的最底部
  20. if(_controller.position.pixels == _controller.position.maxScrollExtent){
  21. //如果不是最后一页数据,则生成新的数据添加到list里面
  22. if(_mPage < 4){
  23. _retrieveData();
  24. }
  25. }
  26. });
  27. }
  28. @override
  29. Widget build(BuildContext context) {
  30. return new MaterialApp(
  31. title: 'ListView Demo',
  32. home: new Scaffold(
  33. appBar: new AppBar(
  34. title: new Text('ListView Demo'),
  35. ),
  36. body: new RefreshIndicator(
  37. onRefresh: _onRefresh,
  38. child: new ListView.separated(
  39. controller: _controller,
  40. physics: BouncingScrollPhysics(),
  41. itemBuilder: (context,index){
  42. //判断是否构建到了最后一条item
  43. if(index == _items.length){
  44. //判断是不是最后一页
  45. if(_mPage < 4){
  46. //不是最后一页,返回一个loading窗
  47. return new Container(
  48. padding: EdgeInsets.all(16.0),
  49. alignment: Alignment.center,
  50. child: SizedBox(
  51. width: 24.0,
  52. height: 24.0,
  53. child: CircularProgressIndicator(strokeWidth: 2.0,),
  54. ),
  55. );
  56. }else{
  57. //是最后一页,显示我是有底线的
  58. return new Container(
  59. padding: EdgeInsets.all(16.0),
  60. alignment: Alignment.center,
  61. child: new Text('我是有底线的!!!',style:TextStyle(color: Colors.blue),),
  62. );
  63. }
  64. }else{
  65. return ListTile(title:new Text('${_items[index]}'));
  66. }
  67. },
  68. //分割线构造器
  69. separatorBuilder: (context,index){
  70. return new Divider(color: Colors.blue,);
  71. },
  72. //_items.length + 1是为了给最后一行的加载loading留出位置
  73. itemCount: _items.length + 1
  74. ),
  75. ),
  76. ),
  77. );
  78. }
  79. void getData() {
  80. //初始数据源
  81. for (int i=0;i<20;i++){
  82. _items.insert(_items.length, "第${_items.length}条原始数据");
  83. print(_items[i]);
  84. }
  85. }
  86. void _retrieveData() {
  87. //上拉加载新的数据
  88. _mPage++;
  89. Future.delayed(Duration(seconds: 2)).then((e){
  90. for (int i=0;i<20;i++){
  91. _items.insert(_items.length, "这是新加载的第${_items.length}条数据");
  92. }
  93. setState(() {
  94. });
  95. });
  96. }
  97. Future<void> _onRefresh() async {
  98. await Future.delayed(Duration(seconds: 2)).then((e){
  99. setState(() {
  100. _mPage = 0;
  101. _items.clear();
  102. for (int i=0;i<20;i++){
  103. _items.insert(_items.length, "第${_items.length}条下拉刷新后的数据");
  104. }
  105. });
  106. });
  107. }
  108. @override
  109. void dispose() {
  110. //移除监听,防止内存泄漏
  111. _controller.dispose();
  112. super.dispose();
  113. }
  114. }

 

 

 

 

 

以上就是今天下拉刷新和上拉加载的全部内容了,如果有错误的地方或者有任何疑问,欢迎留言!!!

 

原文链接:http://www.cnblogs.com/upwgh/p/11435721.html

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

本站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号