Flutter仿京东实战项目二-首页分类

173 阅读3分钟

好久好久没写代码了,由于最近可能要找工作,所以我准备找个项目写写练练手、找找写代码的感觉。由于我又是个喜新厌旧的人,所以准备找个我不熟的Flutter来写写。

京东我用的比较多,所以我准备用Flutter仿一个,尽量做到一比一还原功能

写这个文章的原则和内容

  • 类似简单布局的东西都是一带而过、毕竟这些东西我们只要多试试就能写对了
  • 能用现成的组件和开源库,就一定不自己写
  • 我很懒、不会讲具体代码实现,但是我会把思路步骤过程记录下来
  • 会记录下我实现功能中遇到的各种坑和有意思的地方
  • 我写的很随意,想到啥写到啥,可能会忘了很多东西,后来会补上

代码地址:github.com/nppp1990/jd…

因为代码还在更新,所以现在还是半成品;我会边更新代码边更新文章的

功能点

京东 8801693210643_.pic.jpg

最终实现效果

屏幕录制2023-08-28 16.21.20.gif

  • 横向滑动的分类列表
    • 支持横滑
    • 点击item后文字变黑、变大
    • 点击item后对应item移到列表中间
  • 图片banner
  • 两行的横滑列表
    • 支持横滑
    • item右上角的mask
    • 底部指示器

横向滑动的分类列表

方案一:ListView实现

  • 横滑:scrollDirection: Axis.horizontal,
  • 选中态变化:加一个变量late int _selectedIndex;保存选中的item下标,通过setState更新index来控制选中变化
  • 点击item后对应item移到列表中间:可参考TabController(计算滑动的位置实在有点麻烦,所以我没尝试)

方案二:TabBar实现

  • 横滑、点击item自动移到中间:TabBar自带就能实现
  • 点击item后文字变黑、变大:需要分别修改下面几个属性即可
labelColor: Colors.black,
labelStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
unselectedLabelColor: grey1,
unselectedLabelStyle: const TextStyle(fontSize: 16),

TabBar的坑

  • 删除TabBar自带padding:labelPadding: EdgeInsets.zero
  • Tabbar的item如果做了监听事件的监听,那么自带的移动到中间功能就会被屏蔽掉;如果想监听点击事件、可以用TabBar的onTap来实现
  • 删除TabBar底部的Indicator:参考这个stackoverflow.com/questions/5… 上面链接的方法都不好使,试了下修改下图几个地方好使

image.png

两行的横滑列表

显然这个用最外层需要用PageView实现

Grid布局

image.png

这里有两个坑需要注意的

  • GridView自带paddingTop需要删掉
  • grid item的高度设置是不生效的,而是通过宽度和childAspectRatio计算出来的。所以假设期望的高度为itemHeight,那么childAspectRatio需要这么算
double childAspectRatio = getScreenWidth(context) / (5 * itemHeight);

grid item右上角的闪现mask动画

image.png

  • 布局就用ContainerBoxDecoration分别实现圆角和白线
  • 动画可以用ScaleTransition实现从0到1,1到0
    • 从0到1:_controller.forward();
    • 从1到0:_controller.reverse();
  • 文本通过index+1循环改变
@override
void initState() {
  super.initState();
  _controller = AnimationController(vsync: this, duration: widget.animationDuration * 0.5);
  _animation = Tween(begin: 1.0, end: 0.0).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut))
    ..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _controller.reverse();
        setState(() {
          _index = (_index + 1) % widget.data.length;
        });
      } else if (status == AnimationStatus.dismissed) {
        Future.delayed(widget.interval, () {
          _controller.forward();
        });
      }
    });
  Future.delayed(widget.interval, () {
    _controller.forward();
  });
}

底部Indicator

直接pub上搜索:”page indicator“选用的人最多的smooth_page_indicator

samples里有我们想要的例子

image.png

最终实现代码和注释如下图 image.png