做网站公司教程,湖南长信建设集团网站,中国有没有做的好的网站,集团酒店网站建设一.创建页面
由于我们需要请求网络#xff0c;并将返回的数据渲染到页面上#xff0c;所以需要继承StatefulWidget#xff0c;本文涉及的接口#xff0c;取自鸿神的玩android开放API
class ProjectListPage extends StatefulWidget {overrideStateStatefulWidget…一.创建页面
由于我们需要请求网络并将返回的数据渲染到页面上所以需要继承StatefulWidget本文涉及的接口取自鸿神的玩android开放API
class ProjectListPage extends StatefulWidget {overrideStateStatefulWidget createState() _ProjectListPageState();
}class _ProjectListPageState extends StateProjectListPage {overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(项目列表)),body: Container());}}二.使用FutureBuilder异步初始化页面数据
通过FutureBuilder我们可以从互联网上获取数据的过程中显示一个加载框等获取数据成功时再渲染页面本文的重点不是讲FutureBuilder怎么使用就不做过多解释了直接上代码:
class ProjectListPage extends StatefulWidget {overrideStateStatefulWidget createState() _ProjectListPageState();
}class _ProjectListPageState extends StateProjectListPage {late FuturePageModelProjectModel future;overridevoid initState() {// TODO: implement initStatesuper.initState();future IndexDao.getProjectList(cid: 0, start: 1);}overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text(项目列表)),body: FutureBuilderPageModelProjectModel(future: future,builder: (BuildContext context, AsyncSnapshotPageModelProjectModel 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 {// 请求成功显示数据ListProjectModel 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(...)
);Futurevoid _refreshData() {start 1;return IndexDao.getProjectList(cid: 0, start: start).then((value) {setState(() {datas.clear();datas.addAll(value.records);});});
}五.上拉加载更多
重点来了我们应该在何时去加载更多数据呢那自然是ListView滑动到底部的时候。可以通过ScrollController监听
late ScrollController _controller;override
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;
override
void initState() {// TODO: implement initStatesuper.initState();future IndexDao.getProjectList(cid: 0, start: 1);_controller ScrollController();
}Widget build(BuildContext context) {return NotificationListenerScrollEndNotification(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 Futurevoid _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),),),)],));
}