Flutter移动电商实战 --(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();
} }
category_page.dart
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)
],
)
],
),
),
);
}
}
.
Flutter移动电商实战 --(35)列表页_上拉加载更多制作的更多相关文章
- Flutter实战视频-移动电商-35.列表页_上拉加载更多制作
35.列表页_上拉加载更多制作 右侧列表上拉加载配合类别的切换 上拉加载需要一个page参数,当点击大类或者小类的时候,这个page就要变成1 provide内定义参数 首先我们需要定义一个page的 ...
- Flutter移动电商实战 --(20)首页上拉加载更多功能的制作
这节课学习一下上拉加载效果,其实现在上拉加载的插件有很多,但是还没有一个插件可以说完全一枝独秀,我也找了一个插件,这个插件的优点就是服务比较好,作者能及时回答大家的问题.我觉的选插件也是选人,人对了, ...
- 移动端h5列表页上拉加载更多
背景 上星期公司要求做一个回收书籍的h5给安卓用,里面有一个功能是回收记录列表.设计师那边出的稿子是没有要求分页或者是上拉刷新的,但是众所周知,列表页数据很多的情况下,h5加载是很慢的.所以我一开始是 ...
- Flutter: 下拉刷新,上拉加载更多
import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main ...
- iOS MJRefresh的使用 (列表上拉加载更多)
pod 'MJRefresh' import MJRefresh 加载更多 let footView = MJRefreshAutoNormalFooter(refreshingBlock:{ //去 ...
- Flutter实战视频-移动电商-32.列表页_小类高亮交互效果制作
32.列表页_小类高亮交互效果制作 点击大类右侧的横向的小类红色显示当前的小类别 解决之前溢出的问题: 先解决一个bug,之前右侧的这里设置的高度是1000,但是有不同的虚拟机和手机设别的问题造成了溢 ...
- Flutter移动电商实战 --(28)列表页_商品列表后台接口调试
主要调试商品列表页的接口 这个接口是最难的因为有大类.小类还有上拉加载 先配置接口 config/service_url.dart //const serviceUrl='http://test.ba ...
- Flutter移动电商实战 --(34)列表页_小BUG的修复
当高粱酒的子类没有数据返回的时候就会报错. 解决接口空数据报错的问题 没有数据的时候,给用户一个友好的提示, 我们没有数据的时候还要告诉用户,提示一下他没有数据,在我们的右侧列表的build方法内去判 ...
- Flutter移动电商实战 --(33)列表页_子类和商品列表交互效果
主要实现点击小类下面的列表跟着切换 获取右侧下面的列表信息,即要传递大类的id也要传递小类的,所以需要把左侧的大类的id也要Provide化 可以看下网站上的接口说明: https://jspang. ...
随机推荐
- 结对编程作业(python实现)
一.Github项目地址:https://github.com/asswecanfat/git_place/tree/master/oper_make 二.PSP2.1表格: PSP2.1 Perso ...
- tornado 常见问题处理
1 怎么获取从页面中的传值 使用 self.get_body_argument tornado的参数存储在self.request.body内,通过json以后就可以直接取值,当初我在前端使用angu ...
- 新版mysql的配置文件my.ini位置
在网上的博客上找了好久的my.ini,一直找不到.最后发现原来新版本的mysql已经不把my.ini放在原始的安装目录了.而是放在了C:/ProgramData下.
- 论文笔记:Deformable ConvNets v2: More Deformable, Better Results
概要 MSRA在目标检测方向Beyond Regular Grid的方向上越走越远,又一篇大作推出,相比前作DCN v1在COCO上直接涨了超过5个点,简直不要太疯狂.文章的主要内容可大致归纳如下: ...
- PP 各种快捷键
内容识别 Shitf + F5 (留白填充) 内容识别比例 Alt + Shift +Ctrl +C 取消选区 Ctrl + D Alpha通道 左击 + Ctrl 锐化 先换成Lab颜色 在无颜色的 ...
- Nginx,Haproxy/lvs负载均衡的优缺点分析
PS:Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,本人都在多个项目中实施过,参考了一些资料,结合自己的一些使用经验,总结一下. 一般对负载均衡的使用是随着网站规模的提升根据不 ...
- STM32 ID (转)
STM32唯一ID(Unique Device ID)的读取方法 (转) 每一个STM32微控制器都自带一个96位的唯一ID,也就是Unique Device ID或称为UID,这个唯一ID在任何 ...
- asp.net中数据库事务管理
英文搜索关键字: 文章地址:https://stackoverflow.com/questions/313199/sql-transactions-best-way-to-implement-in-a ...
- 使用raw input 代替全局键盘钩子
//关于raw input 请查看msdn https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536%28v=vs.85%29 ...
- MySql忘记密码了咋办
对内 忘记密码终端修改操作: #停止mysql服务 sudo /opt/lampp/lampp stopmysql #参数启动mysqld sudo /opt/lampp/sbin/mysqld -- ...