flutter 仿淘宝推荐二级分类效果

先看效果

一开始 用的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 );
相关推荐
Lanren的编程日记26 分钟前
Flutter鸿蒙应用开发:基础UI组件库设计与实现实战
flutter·ui·harmonyos
西西学代码35 分钟前
Flutter---波形动画
flutter
于慨4 小时前
flutter基础组件用法
开发语言·javascript·flutter
恋猫de小郭6 小时前
Android CLI ,谷歌为 Android 开发者专研的 AI Agent,提速三倍
android·前端·flutter
火柴就是我7 小时前
flutter pushAndRemoveUntil 的一次小疑惑
flutter
于慨7 小时前
flutter doctor问题解决
flutter
唔668 小时前
flutter 图片加载类 图片的安全使用
安全·flutter
Nathan202406169 小时前
Flutter - InheritedWidget
flutter·dart
恋猫de小郭9 小时前
JetBrains Amper 0.10 ,期待它未来替代 Gradle
android·前端·flutter
Lanren的编程日记10 小时前
Flutter鸿蒙应用开发:实时聊天功能集成实战
flutter·华为·harmonyos