Flutter实战视频-移动电商-35.列表页_上拉加载更多制作
35.列表页_上拉加载更多制作
右侧列表上拉加载配合类别的切换
上拉加载需要一个page参数,当点击大类或者小类的时候,这个page就要变成1
provide内定义参数
首先我们需要定义一个page的变量
下图是我们之前在首页的时候做的上拉加载代码,之前属性noMoreText我们没有设置值,这里我也需要把这个属性加入到provide里面去。
在大类和小类的初始化的方法内,都需要把page设置为1,然后把提示信息设置为空
然后我们需要做page增加的方法,上拉刷新的时候,这个page值是不断的增加的
再增加改变我们的noMoreText的值的方法
引入fresh插件
category_page.dart页面引入上拉刷新的插件
import 'package:flutter_easyrefresh/easy_refresh.dart';
我们在右侧的列表类里面,在列表的地方外层嵌套easy_refresh
我们复制首页之前写好的代码过来。
中间红框内是和原来的代码,上面refreshFooter部分是复制首页的代码过来的。下面的loadMore事件是自己写的
再复制首页的footerKey
GlobalKey<RefreshFooterState> _footerkey=new GlobalKey<RefreshFooterState>();
这样key这里就不报错了。
noMoreText用状态 管理里面的值
noMoreText: Provide.value<ChildCategory>(context).noMoreText,
效果展示
loadMore回调函数补充完整
上拉加载更多和我们加载大类和小类是一样的 我们复制右侧的列表的数据获取的方法
下图是复制的右侧加载列表数据的方法
下面这里,因为我们是不断的累计list的数据的 所以不能再使用等号了。
修改为使用addAll的方法
调用我们获取数据的方法
修正一个地方,这个方法添加的地方是在最后一个花括号结束的上方 复制这个getMoreList的方法
有了上拉刷新的效果。但是点击小类的时候列表数据,不变化了
修正一个错误的地方
上拉刷新的效果
page需要++
我们在调用上拉刷险的时候,没有调用page++
存在问题
当我们一个类别上拉刷新了几次后,滚动条滚动到最下面了。当我们再去点击别的大类的时候,滚动条还是在这个位置上。滚动条没有滚动到最上面
只要切换大类的是时候,就返回我们的顶部,好用scrollController的jumpTo方法 跳转到0.0的位置
最终代码:
provide/child_category.dart
import 'package:flutter/material.dart'; import '../model/category.dart'; class ChildCategory with ChangeNotifier{ List<BxMallSubDto> childCategoryList=[]; int childIndex=0;//子类高亮索引 String categoryId='4';//大类ID 白酒的id 默认为4 String subId='';//小类ID int page=1; String noMoreText='';//显示没有数据的文字 //大类切换逻辑 getChildCategory(List<BxMallSubDto> list,String id){ page=1; noMoreText=''; childIndex=0;//每次点击大类,小类的索引都要清空掉 categoryId=id; BxMallSubDto all=BxMallSubDto(); all.mallCategoryId="00"; all.mallCategoryId="00"; all.comments="null"; all.mallSubName='全部'; childCategoryList=[all]; //childCategoryList=list; childCategoryList.addAll(list); notifyListeners();//监听 } //改变子类索引,indexs是从哪里来的呢?从我们具体的类中进行传递 changeChildIndex(index,String id){ page=1; noMoreText=''; childIndex=index;//把传递过来的index赋值给我们的childIndex subId=id; notifyListeners();//通知 } //增加Page的方法 addPage(){ page++; //notifyListeners();//这里不需要通知,因为我们只是page+1了并没有页面数据上的变化 } //改变noMore的方法 changeNoMore(String text){ noMoreText=text; notifyListeners();//通知 } }
provide/category_goods_list.dart
import 'package:flutter/material.dart'; import '../model/categoryGoodsList.dart'; class CategoryGoodsListProvide with ChangeNotifier{ List<CategoryListData> goodsList=[]; //点击大类时候更换商品列表 getGoodsList(List<CategoryListData> list){ goodsList=list; notifyListeners(); } getMoreList(List<CategoryListData> list){ goodsList.addAll(list); notifyListeners(); } }
import 'package:flutter/material.dart'; import '../service/service_method.dart'; import 'dart:convert'; import '../model/category.dart'; import '../model/categoryGoodsList.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provide/provide.dart'; import '../provide/child_category.dart'; import '../provide/category_goods_list.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart'; class CategoryPage extends StatefulWidget { @override _CategoryPageState createState() => _CategoryPageState(); } class _CategoryPageState extends State<CategoryPage> { @override Widget build(BuildContext context) { //_getCategory(); return Scaffold( appBar: AppBar(title: Text('商品分类'),), body: Container( child: Row( children: <Widget>[ LeftCategoryNav(), Column( children: <Widget>[ RightCategoryNav(), CategoryGoodsList() ], ) ], ), ), ); } } //左侧大类导航 class LeftCategoryNav extends StatefulWidget { @override _LeftCategoryNavState createState() => _LeftCategoryNavState(); } class _LeftCategoryNavState extends State<LeftCategoryNav> { List list=[]; var listIndex=0; @override void initState() { super.initState(); _getCategory();//请求接口的数据 _getGoodsList();//参数是可选的默认是4 所以这里可以不用传值 } @override Widget build(BuildContext context) { return Container( width: ScreenUtil().setWidth(180), decoration: BoxDecoration( border: Border( right: BorderSide(width:1.0,color: Colors.black12),//有边框 ) ), child: ListView.builder( itemCount: list.length, itemBuilder: (contex,index){ return _leftInkWell(index); }, ), ); } Widget _leftInkWell(int index){ bool isClick=false; isClick=(index==listIndex)?true:false; return InkWell( onTap: (){ setState(() { listIndex=index; }); var childList=list[index].bxMallSubDto;//当前大类的子类的列表 var categoryId=list[index].mallCategoryId;//大类的id Provide.value<ChildCategory>(context).getChildCategory(childList,categoryId); _getGoodsList(categoryId:categoryId); }, child: Container( height: ScreenUtil().setHeight(100), padding: EdgeInsets.only(left:10.0,top:10.0), decoration: BoxDecoration( color: isClick?Color.fromRGBO(236, 236, 236, 1.0): Colors.white, border: Border( bottom: BorderSide(width: 1.0,color: Colors.black12) ) ), child: Text( list[index].mallCategoryName, style: TextStyle(fontSize: ScreenUtil().setSp(28)),//设置字体大小,为了兼容使用setSp ), ), ); } void _getCategory() async{ await request('getCategory').then((val){ var data=json.decode(val.toString()); //print(data); CategoryModel category= CategoryModel.fromJson(data); setState(() { list=category.data; }); Provide.value<ChildCategory>(context).getChildCategory(list[0].bxMallSubDto,list[0].mallCategoryId); }); } void _getGoodsList({String categoryId}) { var data={ 'categoryId':categoryId==null?'4':categoryId,//白酒的默认类别 'categorySubId':"", 'page':1 }; request('getMallGoods',formData: data).then((val){ var data=json.decode(val.toString()); CategoryGoodsListModel goodsList=CategoryGoodsListModel.fromJson(data);//这样就从json'转换成了model类 //print('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>:${goodsList.data[0].goodsName}'); // setState(() { // list=goodsList.data; // }); Provide.value<CategoryGoodsListProvide>(context).getGoodsList(goodsList.data); }); } } class RightCategoryNav extends StatefulWidget { @override _RightCategoryNavState createState() => _RightCategoryNavState(); } class _RightCategoryNavState extends State<RightCategoryNav> { //List list = ['名酒','宝丰','北京二锅头','舍得','五粮液','茅台','散白']; @override Widget build(BuildContext context) { return Provide<ChildCategory>( builder: (context,child,childCategory){ return Container( height: ScreenUtil().setHeight(80), width: ScreenUtil().setWidth(570),//总的宽度是750 -180 decoration: BoxDecoration( color: Colors.white,//白色背景 border: Border( bottom: BorderSide(width: 1.0,color: Colors.black12)//边界线 ) ), child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: childCategory.childCategoryList.length, itemBuilder: (context,index){ return _rightInkWell(index,childCategory.childCategoryList[index]); }, ), ); } ); } Widget _rightInkWell(int index,BxMallSubDto item){ bool isClick=false; isClick=(index==Provide.value<ChildCategory>(context).childIndex)?true:false; return InkWell( onTap: (){ Provide.value<ChildCategory>(context).changeChildIndex(index,item.mallSubId); _getGoodsList(item.mallSubId); },//事件留空 child: Container(//什么都加一个container,这样好布局 padding: EdgeInsets.fromLTRB(5.0, 10.0, 5.0, 10.0),//上下是10 左右是5.0 child: Text( item.mallSubName, style:TextStyle( fontSize: ScreenUtil().setSp(28), color: isClick?Colors.pink:Colors.black ), ), ), ); } void _getGoodsList(String categorySubId) { var data={ 'categoryId':Provide.value<ChildCategory>(context).categoryId,//大类ID 'categorySubId':categorySubId, 'page':1 }; request('getMallGoods',formData: data).then((val){ var data=json.decode(val.toString()); CategoryGoodsListModel goodsList=CategoryGoodsListModel.fromJson(data);//这样就从json'转换成了model类 if(goodsList.data==null){ Provide.value<CategoryGoodsListProvide>(context).getGoodsList([]); }else{ Provide.value<CategoryGoodsListProvide>(context).getGoodsList(goodsList.data); } }); } } //商品列表 ,可以上拉加载 class CategoryGoodsList extends StatefulWidget { @override _CategoryGoodsListState createState() => _CategoryGoodsListState(); } class _CategoryGoodsListState extends State<CategoryGoodsList> { GlobalKey<RefreshFooterState> _footerkey=new GlobalKey<RefreshFooterState>(); var scrollController=new ScrollController(); @override void initState() { //_getGoodsList(); super.initState(); } @override Widget build(BuildContext context) { return Provide<CategoryGoodsListProvide>( builder: (context,child,data){ try { if(Provide.value<ChildCategory>(context).page==1){ //列表位置,放到最上边 scrollController.jumpTo(0.0); } } catch (e) { print('进入页面第一次初始化:${e}'); } if(data.goodsList.length>0){ return Expanded( child: Container( width: ScreenUtil().setWidth(570), //height: ScreenUtil().setHeight(974), child: EasyRefresh( refreshFooter: ClassicsFooter( key: _footerkey, bgColor: Colors.white,//背景颜色 textColor: Colors.pink,//粉红色 moreInfoColor: Colors.white, showMore: true, noMoreText: Provide.value<ChildCategory>(context).noMoreText,//具体也不知道到没到底 所以这里直接设置为空就不再显示了 moreInfo: '加载中', loadReadyText: '上拉加载......',//网上拉 显示的文字 ), child: ListView.builder( controller: scrollController, itemCount: data.goodsList.length, itemBuilder: (contex,index){ return _listWidget(data.goodsList,index); }, ), loadMore: () async{ print('上拉加载更多......'); _getMoreList(); }, ) ), ); }else{ return Text('暂时没有数据'); } }, ); } void _getMoreList() { Provide.value<ChildCategory>(context).addPage(); var data={ 'categoryId':Provide.value<ChildCategory>(context).categoryId,//大类ID 'categorySubId':Provide.value<ChildCategory>(context).subId, 'page':Provide.value<ChildCategory>(context).page }; request('getMallGoods',formData: data).then((val){ var data=json.decode(val.toString()); CategoryGoodsListModel goodsList=CategoryGoodsListModel.fromJson(data);//这样就从json'转换成了model类 if(goodsList.data==null){ Provide.value<ChildCategory>(context).changeNoMore('没有更多了'); }else{ Provide.value<CategoryGoodsListProvide>(context).getMoreList(goodsList.data); } }); } //} Widget _goodsImage(List newList,index){ return Container( width: ScreenUtil().setWidth(200),//设置200的宽度 限制 child: Image.network(newList[index].image), ); } Widget _goodsName(List newList,index){ return Container( padding: EdgeInsets.all(5.0),//上下左右都是5.0的内边距 width: ScreenUtil().setWidth(370),//370是一个大约的值 child: Text( newList[index].goodsName, maxLines: 2,//最多显示2行内容 overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: ScreenUtil().setSp(28)),//字体大小 ), ); } Widget _goodsPrice(List newList,index){ return Container( margin: EdgeInsets.only(top:20.0),//和上面的外间距 width: ScreenUtil().setWidth(370),//370是一个大约的值 child: Row( children: <Widget>[ Text( '价格¥${newList[index].presentPrice}', style: TextStyle(color: Colors.pink,fontSize: ScreenUtil().setSp(30)), ), Text( '价格¥${newList[index].oriPrice}', style: TextStyle( color: Colors.black26, decoration: TextDecoration.lineThrough ),//删除线的样式 ) ], ), ); } Widget _listWidget(List newList,int index){ return InkWell( onTap: (){}, child: Container( padding: EdgeInsets.only(top:5.0,bottom:5.0), decoration: BoxDecoration( color: Colors.white, border: Border( bottom: BorderSide(width: 1.0,color: Colors.black12) ) ), child: Row( children: <Widget>[ _goodsImage(newList,index), Column( children: <Widget>[ _goodsName(newList,index), _goodsPrice(newList,index) ], ) ], ), ), ); } }