本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用。 逛丢官方网址:https://guangdiu.com/
flutter 项目实战三 json数据解析以及Gson格式化flutter 项目实战二 网络请求
flutter 项目实战五 item 点击跳转,webview加载
flutter 项目实战七 bottomNavigationBar
在实战四中我们展示了数据列表,但是由于数据量的以及新数据的问题,一般我们都会给列表加上下拉刷新以及上拉加载的功能,来实现加载更多数据以及刷新新的数据。
flutter 提供了 ScrollController 来监听 listview 的滑动状态,我们未listview.build添加属性
controller: _scrollController,
在 IndexWidget 的构造方法中初始化 _scrollController,增加监听
_scrollController.addListener((){
var pix=_scrollController.position.pixels;
var max=_scrollController.position.maxScrollExtent;
if(pix==max){
//已下拉到底部,调用加载更多方法
_loadMoreData();
}
});
_scrollController.position.pixels指的是当前列表滑动到底部的长度,_scrollController.position.maxScrollExtent为列表的最大长度,当两者相等时,即可认为列表滑动到了底部。为了在滑动到底部时展示一个加载的伪动画效果,我们在列表最下面添加一个圆形的进度条,让其模拟正在加载动画。修改 listview.build的内容:
ListView.builder(
itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
itemBuilder: (content,index){
return _listItem(index);
},
controller: _scrollController,)
_listItem 方法代码为:
Widget _listItem(int index){
if(index==_listData.length){//最后一个item为加载条
return Center(
child: Container(
height: 20,
width: 20,
margin: EdgeInsets.all(5),
child: CircularProgressIndicator(//加载条
backgroundColor: Colors.green,
),
),
);
}else{
RstData data=_listData[index];
return GestureDetector(
child: IndexItem(rstData: data,),
onTap:(){ _itemClick(data);},
);
}
}
上拉的方法已经完成了,下面开始添加下拉刷新的部分。
flutter 提供 RefreshIndicator 来帮助我们实现这个功能。RefreshIndicator 必须要实现 onRefresh 属性方法,注意此处的onRefresh 方法是有返回值的,我们用 Future<Null> 作为返回值即可。
RefreshIndicator(
child: ListView.builder(
itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
itemBuilder: (content,index){
return _listItem(index);
},
controller: _scrollController,
),
onRefresh: _onRefresh
),
当使用上拉下拉时,就会涉及到页码的问题,即当前加载到了第几页,所以我们需要定义一个 int 类型的pager 来记录,并对获取到的数据进行一定的操作。
void _getGuangdiuIndexData(FormData formData) async{
String url="getlist.php";
Response resp=await HttpUtil().post(url,data: formData).then((resp){
RespResult respResult=RespResult.fromJson(resp.data);
var data=_listData;
if(pager==1){// 只加载获取到的数据
data=respResult.data;
}else{
data.addAll(respResult.data);//将获取的数据追加到原有数据的后面
}
setState(() {
_listData=data;
});
});
}
由于侧边栏的存在以及侧边栏的点击涉及到列表数据的分类,所以需要增加 type以及typeValue对数据类型加以记录。
下面给出整个修改后的代码:
import 'package:flutter/material.dart';
import 'table/resp_result.dart';
import 'util/http_util.dart';
import 'package:dio/dio.dart';
import 'widget/index_item.dart';
import 'ItemInfoDetail.dart';
import 'configure/type.dart';
class HomeIndex extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return IndexWidget();
}
}
class IndexWidget extends State<HomeIndex>{
List<RstData> _listData;//设置列表数据
GlobalKey<ScaffoldState> _globalKeyState=GlobalKey();
String title="首页";
ScrollController _scrollController=ScrollController();
String type="";
String typeValue="";
int pager=1;
IndexWidget(){
_scrollController.addListener((){
var pix=_scrollController.position.pixels;
var max=_scrollController.position.maxScrollExtent;
if(pix==max){
//已下拉到底部,调用加载更多方法
_loadMoreData();
}
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text(title,style: TextStyle(color: Colors.white),),
leading: IconButton(
icon: Icon(Icons.menu,color: Colors.white,),
onPressed: (){
_changeDrawer();//切换drawer的关闭打开
}),
backgroundColor: Colors.green[500],
),
body: _listInfos(),
);
}
//list列表
Widget _listInfos(){
return Scaffold(
key: _globalKeyState,
body: RefreshIndicator(
child: ListView.builder(
itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
itemBuilder: (content,index){
return _listItem(index);
},
controller: _scrollController,
),
onRefresh: _onRefresh
),
drawer: _setDrawerList(),
);
}
//item
Widget _listItem(int index){
if(index==_listData.length){
return Center(
child: Container(
height: 20,
width: 20,
margin: EdgeInsets.all(5),
child: CircularProgressIndicator(
backgroundColor: Colors.green,
),
),
);
}else{
RstData data=_listData[index];
return GestureDetector(
child: IndexItem(rstData: data,),
onTap:(){ _itemClick(data);},
);
}
}
//切换侧边栏的打开与关闭
void _changeDrawer(){
if(_globalKeyState.currentState.isDrawerOpen){
_globalKeyState.currentState.openEndDrawer();
}else{
_globalKeyState.currentState.openDrawer();
}
}
//侧边栏列表
Widget _setDrawerList(){
var type=index_type;
return Container(
width: 200,
color: Colors.white,
child: ListView.builder(
itemCount: type.length,
itemBuilder: (context,index){
Map<String,String> map=type[index];
return _drawerItem(map);
},
),
);
}
//侧边栏 item
Widget _drawerItem(Map<String,String> map){
if(map["title"]=="=="){
return Container(
height: 1,
decoration: BoxDecoration(
color: Colors.black54,
),
);
}else{
return GestureDetector(
child: Container(
height: 35,
color: Colors.white,
padding: EdgeInsets.only(left: 5),
alignment: Alignment.centerLeft,
child: Text(map["title"]),
),
onTap: (){
_drawerItemClick(map);
},
);
}
}
//侧边栏点击事件
void _drawerItemClick(Map<String,String> map){
FormData formData=FormData();
if(_listData!=null && _listData.length>0){
formData.add("markid", "${_listData[0].id}");
}else {
formData.add("markid", "5685521");
}
if(map["type"]=="all"){
setState(() {
title = "首页";
});
type="";
}else {
setState(() {
title = "首页-${map["title"]}";
});
type=map["type"];
if (type == "hot") {
typeValue="1";
formData.add("onlyhots", typeValue);
} else if (type == "mall") {
typeValue=map["mall"];
formData.add("mall", typeValue);
} else if (type == "cate") {
typeValue=map["cate"];
formData.add("cate", typeValue);
}
}
pager=1;//页码归一
_getGuangdiuIndexData(formData);
_changeDrawer();
}
void _itemClick(RstData data){
Navigator.push(context, MaterialPageRoute(builder: (cx)=>ItemInfoDetail(id: data.id,title: data.title,)));
}
@override
void initState() {
// TODO: implement initState
super.initState();
_onRefresh();
}
//加载更多
void _loadMoreData(){
FormData formData=FormData();
if(_listData!=null && _listData.length>0){
formData.add("sinceid", _listData[_listData.length-1].id);
}else{
formData.add("sinceid", "5685521");
}
if(type!=""){
formData.add(type, typeValue);
}
pager++;
_getGuangdiuIndexData(formData);
}
//下拉刷新
Future<Null> _onRefresh() async {
pager=1;
FormData formData=FormData();
if(_listData!=null && _listData.length>0){
formData.add("markid", _listData[0].id);
}else {
formData.add("markid", "5685521");
}
_getGuangdiuIndexData(formData);
return null;
}
void _getGuangdiuIndexData(FormData formData) async{
String url="getlist.php";
Response resp=await HttpUtil().post(url,data: formData).then((resp){
RespResult respResult=RespResult.fromJson(resp.data);
var data=_listData;
if(data==null){
data=respResult.data;
}else {
if (pager == 1) {
data.clear();
}
data.addAll(respResult.data);
}
setState(() {
_listData=data;
});
if(pager==1){
_scrollController.position.moveTo(0);//滑动到最顶端
}
});
}
}
app运行效果图:
哈哈 gif 没做好,有点糊。
码云 git 下载
kotlin版本的源码下载:git下载地址