01_Flutter之下拉刷新和上拉加载

一.创建页面

由于我们需要请求网络,并将返回的数据渲染到页面上,所以需要继承StatefulWidget,本文涉及的接口,取自鸿神的玩android开放API

class ProjectListPage extends StatefulWidget {State<StatefulWidget> createState() => _ProjectListPageState();
}class _ProjectListPageState extends State<ProjectListPage> {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("项目列表")),body: Container());}}

二.使用FutureBuilder异步初始化页面数据

通过FutureBuilder,我们可以从互联网上获取数据的过程中显示一个加载框,等获取数据成功时再渲染页面,本文的重点不是讲FutureBuilder怎么使用,就不做过多解释了,直接上代码:

class ProjectListPage extends StatefulWidget {State<StatefulWidget> createState() => _ProjectListPageState();
}class _ProjectListPageState extends State<ProjectListPage> {late Future<PageModel<ProjectModel>> future;void initState() {// TODO: implement initStatesuper.initState();future = IndexDao.getProjectList(cid: 0, start: 1);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("项目列表")),body: FutureBuilder<PageModel<ProjectModel>>(future: future,builder: (BuildContext context, AsyncSnapshot<PageModel<ProjectModel>> snapshot) {if (snapshot.connectionState != ConnectionState.done) {//请求中,显示加载圈return const Center(child: SizedBox(width: 30,height: 30,child: CircularProgressIndicator(),),);} else {//请求结束if (snapshot.hasError) {// 请求失败,显示错误return Text("Error: ${snapshot.error}");} else {// 请求成功,显示数据return Text("data: ${snapshot.data}");}}},));}}

在这里插入图片描述

三.渲染列表

if (snapshot.hasError) {// 请求失败,显示错误return Text("Error: ${snapshot.error}");
} else {// 请求成功,显示数据List<ProjectModel> datas = snapshot.data?.records ?? [];return ListView.separated(padding: EdgeInsets.all(10),itemBuilder: (BuildContext context, int index) {return Container(padding: const EdgeInsets.all(10),decoration: const BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)),color: Colors.white,),child: IntrinsicHeight(child: Row(mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.stretch,children: [SizedBox(width: 120,height: 1,child: Image.network(datas[index].envelopePic ?? "", fit: BoxFit.cover),),SizedBox(width: 10,),Expanded(flex: 1,child: Column(mainAxisSize: MainAxisSize.min,mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.stretch,children: [Text("${datas[index]?.title}",maxLines: 2,style: const TextStyle(overflow: TextOverflow.ellipsis,fontSize: 16),),const SizedBox(height: 10,),Text("${datas[index]?.desc}",maxLines: 2,style: const TextStyle(overflow: TextOverflow.ellipsis,fontSize: 14)),],))],),),);},separatorBuilder: (BuildContext context, int index) {return const Divider(color: Colors.transparent, height: 10,);},itemCount: datas.length);
}

在这里插入图片描述

四.实现下拉刷新

直接使用Flutter内置的RefreshIndicator实现下拉刷新

int start = 1;RefreshIndicator(onRefresh: () {return _refreshData();},child: ListView.separated(...)
);Future<void> _refreshData() {start = 1;return IndexDao.getProjectList(cid: 0, start: start).then((value) {setState(() {datas.clear();datas.addAll(value.records);});});
}

在这里插入图片描述

五.上拉加载更多

重点来了,我们应该在何时去加载更多数据呢?那自然是ListView滑动到底部的时候。可以通过ScrollController监听

late ScrollController _controller;
void initState() {// TODO: implement initStatesuper.initState();future = IndexDao.getProjectList(cid: 0, start: 1);_controller = ScrollController();_controller.addListener(() {if(_controller.position.extentAfter == 0) {//划动到底部了,加载更多数据print("划动到底部了,加载更多数据");}});
}Widget build(BuildContext context) {...return RefreshIndicator(onRefresh: () {return _refreshData();},child: ListView.separated(controller: _controller,...));
}

也可以使用NotificationListener监听

late ScrollController _controller;

void initState() {// TODO: implement initStatesuper.initState();future = IndexDao.getProjectList(cid: 0, start: 1);_controller = ScrollController();
}Widget build(BuildContext context) {return NotificationListener<ScrollEndNotification>(onNotification: (ScrollEndNotification notification) {if (_controller.position.extentAfter == 0) {//滚动到底部//加载更多数据}return false;},child: RefreshIndicator(onRefresh: () {return _refreshData();},child: ListView.separated(controller: _controller,...)))
}

加载更多数据,分别对应四种加载状态,more:有更多数据,loading: 加载中,noMore: 没有更多数据了,error: 请求网络出错了

enum LoadMoreStatus { more, loading, error, noMore }

我们需要根据这四种加载状态,显示不同的footer,并且,ListView的itemCount需要在原有基础上加一,预留出一个位置,显示Footer

ListView.separated(...itemBuilder: (BuildContext context, int index) {if(index == datas.length) {if(loadMoreStatus == LoadMoreStatus.more) {return const SizedBox(height: 40,child: Center(child: Text("上拉显示更多"),),);} else if(loadMoreStatus == LoadMoreStatus.loading) {return const SizedBox(height: 40,child: Center(child: Text("正在加载..."),),);} else if(loadMoreStatus == LoadMoreStatus.noMore) {return const SizedBox(height: 40,child: Center(child: Text("没有更多数据了"),),);} else {return const SizedBox(height: 40,child: Center(child: Text("出错了-_-,上拉重新加载"),),);}} else {...}},itemCount: datas.length + 1
)

实现上拉加载更多

void _loadMoreData() {if(loadMoreStatus == LoadMoreStatus.noMore) {return;}if(loadMoreStatus == LoadMoreStatus.loading) {return;}int page = start;if(loadMoreStatus != LoadMoreStatus.error) {page += 1;}setState(() {loadMoreStatus = LoadMoreStatus.loading;});IndexDao.getProjectList(cid: 0, start: page).then((value) {start = page;setState(() {if(value.hasNextPage) {loadMoreStatus = LoadMoreStatus.more;} else {loadMoreStatus = LoadMoreStatus.noMore;}datas.addAll(value.records);});}).onError((error, stackTrace) {setState(() {loadMoreStatus = LoadMoreStatus.error;});return Future.error(error!, stackTrace);});
}_controller.addListener(() {if(_controller.position.extentAfter == 0) {//划动到底部了,加载更多数据_loadMoreData();}
});

在这里插入图片描述

六.Fixed:滑动到最后一页,下拉刷新数据,没有将加载状态重置为more

在这里插入图片描述

Future<void> _refreshData() {start = 1;setState(() {loadMoreStatus = LoadMoreStatus.more;});return IndexDao.getProjectList(cid: 0, start: start).then((value) {setState(() {datas.clear();datas.addAll(value.records);hasMore = value?.hasNextPage ?? false;if(hasMore) {loadMoreStatus = LoadMoreStatus.more;} else {loadMoreStatus = LoadMoreStatus.noMore;}});});
}

七.Fixed:第一页数据不足一屏时,不能触发下拉刷新和加载更多

这种情况属于极端情况,可根据实际情况考虑是否需要修复,可以使用CustomScrollView结合SliverList、SliverFillRemaining修复

Widget build(BuildContext context) {return RefreshIndicator(onRefresh: () {return _refreshData();},child: CustomScrollView(controller: _controller,slivers: [SliverPadding(padding: EdgeInsets.all(10),sliver: SliverList.separated(itemCount: datas.length,itemBuilder: (BuildContext context, int index) {return Container(padding: const EdgeInsets.all(10),decoration: const BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)),color: Colors.white,),child: IntrinsicHeight(child: Row(mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.stretch,children: [SizedBox(width: 120,height: 1,child: Image.network(datas[index].envelopePic ?? "", fit: BoxFit.cover),),SizedBox(width: 10,),Expanded(flex: 1,child: Column(mainAxisSize: MainAxisSize.min,mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.stretch,children: [Text("${datas[index]?.title}",maxLines: 2,style: const TextStyle(overflow: TextOverflow.ellipsis,fontSize: 16),),const SizedBox(height: 10,),Text("${datas[index]?.desc}",maxLines: 2,style: const TextStyle(overflow: TextOverflow.ellipsis,fontSize: 14)),],))],),),);},separatorBuilder: (BuildContext context, int index) {return const Divider(color: Colors.transparent, height: 10,);},),),//填充剩余空间SliverFillRemaining(hasScrollBody: false,fillOverscroll: false,child: Container(),),SliverToBoxAdapter(child: Container(padding: const EdgeInsets.only(bottom: 10),height: 40,child: Center(child: Text(tips),),),)],));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/72205.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js摄像头动态检测

利用摄像头每一秒截图一次图像。然后计算2次图像之间的相似度。 如果相似度低于98%就会报警。 var video document.getElementsByClassName(inputvideo)[0]; video.innerHTML "<video classinput_video idcamera autoplay width640px height380px></video>…

windows10使用wheel安装tensorflow2.13.0/2.10.0

安装过程 安装虚拟环境安装virtualenv安装满足要求的python版本使用virtualenv创建指定python版本的虚拟环境 安装tensorflow2.13.0安装tensorflow-docs直接下载使用wheel下载 在VSCode编辑器中使用虚拟环境下的包 安装虚拟环境 这里笔者使用的是 virtualenv进行虚拟环境搭建的…

VIRTIO-BLK代码分析(2)VIRTIO驱动分析

QEMU模拟的VIRTIO设备同时也是PCIE设备&#xff0c;Guest中VIRTIO PCIE驱动与之匹配&#xff0c;根据设备驱动模型&#xff0c;最终触发probe函数virtio_pci_probe()。该probe函数使能PCIE设备&#xff0c;并注册VIRTIO设备&#xff0c;并与VIRTIO-BLK匹配&#xff0c;触发VIRT…

通俗易懂讲解大模型:Tokenizer

Tokenizer Tokenizer 是 NLP pipeline 的核心组件之一。Tokenizer 的目标是&#xff1a;将文本转换为模型可以处理的数据。模型只能处理数字&#xff0c;因此 Tokenizer 需要将文本输入转换为数字输入。 通常而言有三种类型的 Tokenizer &#xff1a;Word-based Tokenizer、Cha…

2023国赛数学建模A题思路分析 - 定日镜场的优化设计

# 1 赛题 A 题 定日镜场的优化设计 构建以新能源为主体的新型电力系统&#xff0c; 是我国实现“碳达峰”“碳中和”目标的一项重要 措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。 定日镜是塔式太阳能光热发电站(以下简称塔式电站)收集太阳能的基本组件&…

校园二手物品交易系统微信小程序设计

系统简介 本网最大的特点就功能全面&#xff0c;结构简单&#xff0c;角色功能明确。其不同角色实现以下基本功能。 服务端 后台首页&#xff1a;可以直接跳转到后台首页。 用户信息管理&#xff1a;管理所有申请通过的用户。 商品信息管理&#xff1a;管理校园二手物品中…

华为云使用脚本初始化Linux数据盘

初始化新挂载的磁盘 登录云服务器&#xff0c;执行以下命令获取自动初始化磁盘脚本。 wget https://ecs-instance-driver.obs.cn-north-1.myhuaweicloud.com/datadisk/LinuxVMDataDiskAutoInitialize.sh 说明&#xff1a; 若回显异常&#xff0c;请检查云服务器是否绑定弹性公…

029:vue项目,勾选后今天不再弹窗提示

第029个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

豪华卧室怎么装?快来看看吧

一阵轻松的叹息&#xff0c;由柔软的质地、新鲜的空气和扎实的设计带来。只需稍微借鉴这些豪华卧室的创意&#xff0c;这一切都可以成为你的。 用华丽的四柱床提升 四柱床的柱子为床框增添了另一种维度&#xff0c;同时保持通风。长长的线条提高了房间的高度&#xff0c;吸引…

MySQL的故事——MySQL架构与历史

MySQL架构与历史 文章目录 MySQL架构与历史一、MySQL逻辑架构二、并发控制三、事务四、多版本并发控制(MVCC) 一、MySQL逻辑架构 第一层&#xff1a;连接处理、授权认证、安全等等 第二层&#xff1a;查询解析、分析、优化、缓存以及所有的内置函数。包含跨存储引擎的功能&…

Matlab图像处理-

有些时候&#xff0c;直接利用图像的灰度直方图选择阈值不是非常直观&#xff0c;这时&#xff0c;可以利用图像三个通道的直方图来进行图像分割&#xff0c;操作步骤如上文所示&#xff0c;下图为原始图片。 下图为三通道直方图。 下图将三个通道的直方图会绘制到一个图表上&a…

【完整代码】2023数学建模国赛C题代码--蔬菜类商品的自动定价与补货决策

C 题 蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c;商超通常会根据各商品的历史销售和需 求情况每天进…

c高级 day1

使用cut截取出Ubuntu用户的家目录&#xff0c;要求&#xff1a;不能使用":"作为分割 xmind&#xff1a;

从智能手机到智能机器人:小米品牌的高端化之路

原创 | 文 BFT机器人 前言 在前阵子落幕的2023世界机器人大会“合作之夜”上&#xff0c;北京经济技术开发区管委会完成了与世界机器人合作组织、小米机器人等16个重点项目签约&#xff0c;推动机器人创新链和产业链融合&#xff0c;其中小米的投资额达到20亿&#xff01; 据了…

分布式调度Elastic-job

分布式调度Elastic-job 1. 概述 1.1什么是任务调度 我们可以思考⼀下下⾯业务场景的解决⽅案: 某电商平台需要每天上午10点&#xff0c;下午3点&#xff0c;晚上8点发放⼀批优惠券某银⾏系统需要在信⽤卡到期还款⽇的前三天进⾏短信提醒某财务系统需要在每天凌晨0:10分结算前…

PostMan传时间参数一次性发送多次请求

文章目录 1. Date类型的参数&#xff0c; "date": "2023-09-07 22:01:51"格式会报错2. 在Pre-request Script预置时间3. 使用postman一次性发送多次请求 1. Date类型的参数&#xff0c; “date”: "2023-09-07 22:01:51"格式会报错 2. 在Pre-req…

算法 数据结构 斐波那契数列 递归实现斐波那契数列 斐波那契递归的优化 斐波那契数列递归求解 多路递归实现 斐波那契算法系列 数据结构(十一)

1. 什么是斐波那契数列&#xff1a; 之前的例子是每个递归函数只包含一个自身的调用&#xff0c;这称之为 single recursion 如果每个递归函数例包含多个自身调用&#xff0c;称之为 multi recursion 递推关系 下面的表格列出了数列的前几项 F0F1F2F3F4F5F6F7F8F9F10F11F12…

前端 JS 经典:上传文件

重点&#xff1a;multipart/form-data 后端识别上传类型必填 1. form 表单上传 <!-- enctype"multipart/form-data" 这个必填 --> <form action"http://127.0.0.1:8080/users/avatar" method"post" enctype"multipart/form-data…

SQL语言的分类:DDL(数据库、表的增、删、改)、DML(数据的增、删、改)

数据库管理系统&#xff08;数据库软件&#xff09;功能非常多&#xff0c;不仅仅是存储数据&#xff0c;还要包含&#xff1a;数据的管理、表的管理、库的管理、账户管理、权限管理等。 操作数据库的SQL语言&#xff0c;基于功能&#xff0c;划分为4类&#xff1a; 1、数据定…