先看效果
一开始 用的PageView 做的, 然后重写PageScrollPhysics一顿魔改, 最后发现还是有一些小bug。 后面又想到pageview 能做,listview肯定也能做,最后用ListView加GridView 把功能实现了。
listview 实现pageview 的分页滑动效果只需要给ListView设置 physics: const PageScrollPhysics()。
做一个功能最重要的是拆分。
1:第一页最多展示5个半,(其实那半个是第二页的一部分)第二页后最多展示 15个
2:高度随着滑动动态变化
高度变化很简单只要最外层的widget 高度是动态的就行
Container(
height: state.height, // 动态变化
decoration: const BoxDecoration(
color: Colors.white,
// borderRadius:
// BorderRadius.only(bottomLeft: Radius.circular(8), bottomRight: Radius.circular(8)),
),
child: _buildSubcategory(context),
)
_buildSubcategory 这里主要有一些数学计算,因为数据多少是不确定的,第一页只显示五个,默认第二页最多显示15个,后面第三页等等都是默认最多显示15个,当然子分类可能没那么多,基本就两页,但是公式可以通用,想分几页就几页,能支持就支持呗。
/// 构建子分类
Widget _buildSubcategory(BuildContext context) => Stack(
children: <Widget>[
SizedBox(
width: ScreenUtil.width,
child: ListView.builder(
key: ValueKey("ListView$id"),
addAutomaticKeepAlives: true,
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(),
// physics: const NoInertiaScrollPhysics(),
itemCount: state.listSubcategoryEntity.length < 6
? 1
: ((state.listSubcategoryEntity.length - 5) / 15).ceil() + 1,
controller: controller.scrollController,
itemBuilder: (context, index) {
List<SubcategoryEntity> list = [];
if (index == 0) {
if(state.listSubcategoryEntity.length>5){
list.addAll(state.listSubcategoryEntity.sublist(0, 5));
}else{
list.addAll(state.listSubcategoryEntity);
}
} else {
list.addAll(state.listSubcategoryEntity.sublist(
5 + (index - 1) * 15,
state.listSubcategoryEntity.length > (5 + index * 15)
? 5 + index * 15
: state.listSubcategoryEntity.length));
}
return _buildSubcategoryWidget(context, index, list);
},),
),
Align(
alignment: Alignment.bottomCenter,
child: widgetBuilder(
id: DemandListPageWidgetId.subcategoryIndicator,
builder: () => _buildSubcategoryIndicatorWidget(
state.listSubcategoryEntity.length < 6
? 1
: ((state.listSubcategoryEntity.length - 5) / 15).ceil() +
1,
state.index),
),
)
],
);
至于第一页怎么漏出第二页的数据,很简单 把第一页的width宽度设置小一点就能漏出第二页。
为什么是ScreenUtil.width-40?你想减多少都可以。。。我只是觉得减40漏出的部分更好看一些。
_buildSubcategoryWidget(
BuildContext context, int indexs, List<SubcategoryEntity> list) {
return SizedBox(
width: state.listSubcategoryEntity.length >5&&indexs==0?ScreenUtil.width-40:ScreenUtil.width,
child: GridView.builder(
key: ValueKey("GridView$id"),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding:
state.listSubcategoryEntity.length >5&&indexs==0?const EdgeInsets.only(left: 10 , top: 8):
const EdgeInsets.only(left: 0, top: 8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
// crossAxisSpacing: 5,
childAspectRatio:1.1,
// mainAxisSpacing: 2
),
itemBuilder: (BuildContext context, int index) {
return Ripple(
onTap: () {
controller.push(url: list[index].jumpUrl ?? "");
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ImageBuild.network(list[index].image?.small ?? '',
fit: BoxFit.fill,
size: const Size(35, 35),
placeholder: AppImage.commonPlaceholderIconSmall),
const SizedBox(
height: 2.0,
),
TextBuild.text(
list[index].title?.content ?? "",
style: AppTextStyleData(
fontWeight: AppTextStyle.regular,
fontSize: 14,
color: AppColorData.hex(
list[index].title?.fontColor ?? "#1E1E1E"),
),
textAlign: TextAlign.center,
),
],
));
},
itemCount: indexs == 0
? (state.listSubcategoryEntity.length < 5
? state.listSubcategoryEntity.length
: 5)
: (state.listSubcategoryEntity.length > 5 + indexs * 15
? 15
: (state.listSubcategoryEntity.length - (5 + (indexs - 1) * 15))),
),
);
}
主要是监听滑动事件,然后动态改变高度
NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification sn) {
if (sn.metrics.axis == Axis.horizontal) {
if (sn is ScrollEndNotification) {
// var currentPage = (math.max(0.0, clampDouble(sn.metrics.pixels, sn.metrics.minScrollExtent, sn.metrics.maxScrollExtent)) /
// math.max(1.0, ScreenUtil.width)).round();
// state.index = currentPage;
// controller.widgetRebuild(DemandListPageWidgetId.subcategoryIndicator);
} else {
double progress = sn.metrics.pixels;
if (0 < progress && progress <= ScreenUtil.width) {
var multiple = state.listSubcategoryEntity.length>15 ? 1:0;
if(state.listSubcategoryEntity.length<11){
state.height = 80;
}else{
state.height = progress/ScreenUtil.width * ((multiple+1)*78)+80;
}
} else if (progress == 0) {
state.height = 80;
}
var currentPage = (math.max(0.0, clampDouble(sn.metrics.pixels, sn.metrics.minScrollExtent, sn.metrics.maxScrollExtent)) /
math.max(1.0, ScreenUtil.width)).round();
state.index = currentPage; // 记录当前页数
controller.widgetRebuild(DemandListPageWidgetId.subcategory); //刷新页面,自行替换
}
}
return false;
},
child: child );