flutter开发实战-实现首页分类目录入口切换功能

在这里插入图片描述

在开发中经常遇到首页的分类入口,如美团的美食团购、打车等入口,左右切换还可以分页更多展示。

一、使用flutter_swiper_null_safety

在pubspec.yaml引入

  # 轮播图
  flutter_swiper_null_safety: ^1.0.2

二、实现swiper分页代码

由于我这里按照一页8条展示,两行四列展示格式。

当列表list传入的控件时候,一共的页数为

int getSwiperPageNumber() {
    int allLength = widget.list.length;
    int aPageNum = getSwiperOnePageNumber();
    if (allLength % aPageNum == 0) {
      return (allLength / aPageNum).toInt();
    }

    return (allLength / aPageNum).toInt() + 1;
  }

通过列表,一页数量计算每一页应该展示多少个按钮。

List getSwiperPagerItems(int pagerIndex) {
    List pagerItems = [];
    int allLength = widget.list.length;
    int aPageNum = getSwiperOnePageNumber();
    int start = pagerIndex * aPageNum;
    int end = (pagerIndex + 1) * aPageNum;
    if (end > allLength) {
      end = allLength;
    }
    pagerItems = widget.list.sublist(start, end);

    return pagerItems;
  }

一共pages的列表

int pagerNumber = getSwiperPageNumber();
    for (int index = 0; index < pagerNumber; index++) {
      CategorySwiperPagerItem swiperPagerItem = CategorySwiperPagerItem();
      swiperPagerItem.pagerIndex = index;
      swiperPagerItem.pagerItems = getSwiperPagerItems(index);
      swiperPagers.add(swiperPagerItem);
    }

通过使用flutter_swiper_null_safety来显示

Swiper(
        // 横向
        scrollDirection: Axis.horizontal,
        // 布局构建
        itemBuilder: (BuildContext context, int index) {
          CategorySwiperPagerItem swiperPagerItem = swiperPagers[index];
          return HomeCategoryPager(
            pagerIndex: swiperPagerItem.pagerIndex,
            pageItems: swiperPagerItem.pagerItems,
            width: itemWidth,
            height: itemHeight,
            containerHeight: showHeight,
            containerWidth: width,
          );
        },
        //条目个数
        itemCount: swiperPagers.length,
        // 自动翻页
        autoplay: false,
        // pagination: _buildSwiperPagination(),
        // pagination: _buildNumSwiperPagination(),
        //点击事件
        // onTap: (index) {
        //   LoggerManager().debug(" 点击 " + index.toString());
        // },
        // 相邻子条目视窗比例
        viewportFraction: 1,
        // 用户进行操作时停止自动翻页
        autoplayDisableOnInteraction: true,
        // 无限轮播
        loop: false,
        //当前条目的缩放比例
        scale: 1,
      ),

实现比较简单,

完整代码如下

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';
import 'package:flutter_app_dfaceintl/config/resource_manager.dart';
import 'package:flutter_app_dfaceintl/utils/color_util.dart';
import 'package:flutter_app_dfaceintl/widget/common/show_gesture_container.dart';
import 'package:one_context/one_context.dart';
import 'package:flutter_app_dfaceintl/config/logger_manager.dart';

class CategorySwiperPagerItem {
  int pagerIndex = 0;
  List pagerItems = [];
}

class HomeCategoryWidget extends StatefulWidget {
  const HomeCategoryWidget({
    Key? key,
    required this.rowNumber,
    required this.numberOfPerRow,
    required this.list,
    required this.screenWidth,
  }) : super(key: key);

  // 多少行
  final int rowNumber;

  // 一行几个
  final int numberOfPerRow;

  final List list;

  final double screenWidth;

  
  State<HomeCategoryWidget> createState() => _HomeCategoryWidgetState();
}

class _HomeCategoryWidgetState extends State<HomeCategoryWidget> {
  double showHeight = 0.0;
  double containVPadding = 5.0;
  double containHPadding = 10.0;

  List<CategorySwiperPagerItem> swiperPagers = [];

  bool showPagination = false;

  
  void initState() {
    // TODO: implement initState

    double containerHeight = getContainerMaxHeight(widget.screenWidth);
    showHeight = containerHeight + 2 * containVPadding;

    int pagerNumber = getSwiperPageNumber();
    for (int index = 0; index < pagerNumber; index++) {
      CategorySwiperPagerItem swiperPagerItem = CategorySwiperPagerItem();
      swiperPagerItem.pagerIndex = index;
      swiperPagerItem.pagerItems = getSwiperPagerItems(index);
      swiperPagers.add(swiperPagerItem);
    }

    if (swiperPagers.length > 1) {
      showPagination = true;
    }

    super.initState();
  }

  
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    double width = widget.screenWidth;
    double itemWidth = (width - 2 * containHPadding) / widget.numberOfPerRow;
    double itemHeight = itemWidth;
    return Container(
      width: width,
      height: showHeight,
      margin: EdgeInsets.symmetric(vertical: 5.0),
      padding: EdgeInsets.symmetric(
        vertical: containVPadding,
        horizontal: containHPadding,
      ),
      color: Colors.white,
      child: Swiper(
        // 横向
        scrollDirection: Axis.horizontal,
        // 布局构建
        itemBuilder: (BuildContext context, int index) {
          CategorySwiperPagerItem swiperPagerItem = swiperPagers[index];
          return HomeCategoryPager(
            pagerIndex: swiperPagerItem.pagerIndex,
            pageItems: swiperPagerItem.pagerItems,
            width: itemWidth,
            height: itemHeight,
            containerHeight: showHeight,
            containerWidth: width,
          );
        },
        //条目个数
        itemCount: swiperPagers.length,
        // 自动翻页
        autoplay: false,
        // pagination: _buildSwiperPagination(),
        // pagination: _buildNumSwiperPagination(),
        //点击事件
        // onTap: (index) {
        //   LoggerManager().debug(" 点击 " + index.toString());
        // },
        // 相邻子条目视窗比例
        viewportFraction: 1,
        // 用户进行操作时停止自动翻页
        autoplayDisableOnInteraction: true,
        // 无限轮播
        loop: false,
        //当前条目的缩放比例
        scale: 1,
      ),
    );
  }

  int getSwiperOnePageNumber() {
    return widget.numberOfPerRow * widget.rowNumber;
  }

  int getSwiperPageNumber() {
    int allLength = widget.list.length;
    int aPageNum = getSwiperOnePageNumber();
    if (allLength % aPageNum == 0) {
      return (allLength / aPageNum).toInt();
    }

    return (allLength / aPageNum).toInt() + 1;
  }

  List getSwiperPagerItems(int pagerIndex) {
    List pagerItems = [];
    int allLength = widget.list.length;
    int aPageNum = getSwiperOnePageNumber();
    int start = pagerIndex * aPageNum;
    int end = (pagerIndex + 1) * aPageNum;
    if (end > allLength) {
      end = allLength;
    }
    pagerItems = widget.list.sublist(start, end);

    return pagerItems;
  }

  double getContainerMaxHeight(double screenWidth) {
    double width = screenWidth;
    double itemSize = (width - 2 * containHPadding) / widget.numberOfPerRow;
    double maxHeight = itemSize * widget.rowNumber;

    int allLength = widget.list.length;
    if (allLength <= widget.numberOfPerRow) {
      maxHeight = itemSize;
    }

    return maxHeight;
  }
}

class HomeCategoryPager extends StatelessWidget {
  const HomeCategoryPager({
    Key? key,
    required this.pagerIndex,
    required this.pageItems,
    required this.width,
    required this.height,
    this.containerWidth,
    this.containerHeight,
  }) : super(key: key);

  final int pagerIndex;

  final List pageItems;

  final double width;
  final double height;

  final double? containerWidth;
  final double? containerHeight;

  
  Widget build(BuildContext context) {
    return Container(
      width: containerWidth,
      height: containerHeight,
      child: Wrap(
        children: pageItems
            .map((e) => HomeCategoryItem(
                  width: width,
                  height: height,
                ))
            .toList(),
      ),
    );
  }
}

class HomeCategoryItem extends StatelessWidget {
  const HomeCategoryItem({
    Key? key,
    required this.width,
    required this.height,
  }) : super(key: key);

  final double width;
  final double height;

  
  Widget build(BuildContext context) {
    return ShowGestureContainer(
      padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 5.0),
      width: width,
      height: height,
      highlightedColor: ColorUtil.hexColor(0xf0f0f0),
      onPressed: () {
        LoggerManager().debug("ShowGestureContainer");
      },
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          buildIcon(),
          Padding(
            padding: EdgeInsets.only(top: 5.0),
            child: buildTitle(),
          ),
        ],
      ),
    );
  }

  Widget buildTitle() {
    return Text(
      "栏目",
      textAlign: TextAlign.left,
      maxLines: 2,
      overflow: TextOverflow.ellipsis,
      softWrap: true,
      style: TextStyle(
        fontSize: 12,
        fontWeight: FontWeight.w500,
        fontStyle: FontStyle.normal,
        color: ColorUtil.hexColor(0x444444),
        decoration: TextDecoration.none,
      ),
    );
  }

  Widget buildIcon() {
    return Icon(
      Icons.access_alarm,
      size: 32.0,
      color: Colors.brown,
    );
  }
}

三、小结

flutter开发实战-实现首页分类目录入口切换功能。Swiper实现如美团的美食团购、打车等入口,左右切换还可以分页更多展示。

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/60851.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

shell指令的应用

整理思维导图判断家目录下&#xff0c;普通文件的个数和目录文件的个数输入一个文件名&#xff0c;判断是否为shell脚本文件&#xff0c;如果是脚本文件&#xff0c;判断是否有可执行权限&#xff0c;如果有可执行权限&#xff0c;运行文件&#xff0c;如果没有可执行权限&…

从URL取值传给后端

从URL传值给后端 http://127.0.0.1:8080/blog_content.html?id8点击浏览文章详情&#xff0c;跳转至详情页面 从 url 中拿出文章 id&#xff0c;传给后端 首先拿到url然后判断是否有值&#xff0c;从问号后面取值params.split(&) 以 & 作为分割然后遍历字符数组 param…

一百四十六、Xmanager——Xmanager5连接Xshell7并控制服务器桌面

一、目的 由于kettle安装在Linux上&#xff0c;Xshell启动后需要Xmanager。而Xmanager7版本受限、没有免费版&#xff0c;所以就用Xmanager5去连接Xshell7 二、Xmanager5安装包来源 &#xff08;一&#xff09;注册码 注册码&#xff1a;101210-450789-147200 &#xff08…

跑步蓝牙耳机哪种好、推荐几款专业跑步耳机

近年来&#xff0c;全民运动热潮逐渐兴起&#xff0c;运动耳机也成为各个年龄段的运动爱好者追捧的对象。作为一个热爱跑步的人&#xff0c;我可以负责任地告诉你&#xff0c;戴上耳机跑步会更加愉快。很多时候&#xff0c;运动的单调可能会让你产生放弃锻炼的想法&#xff0c;…

思科模拟器配置静态路由(下一跳使用IP)

Router0配置代码&#xff1a;##端口配置 Router(config)#int fastEthernet 0/0 Router(config-if)#ip address 192.168.10.254 255.255.255.0 Router(config-if)#no shutdown Router(config-if)#int fastEthernet 0/1 Router(config-if)#ip address 192.168.20.1 255.255.255.2…

ruoyi-cloud-notes02

1、Validated RequestBody 配合使用 Validated 和 RequestBody 都是 Spring Boot 中用于在请求中验证数据的注解。但是&#xff0c;它们的作用和使用方式略有不同。 Validated 用于在方法参数、URL、请求体、Map中的数据上进行验证&#xff0c;确保数据的有效性。它会在验证失…

vivo全球商城:电商交易平台设计

一、背景 vivo官方商城经过了七年的迭代&#xff0c;从单体架构逐步演进到微服务架构&#xff0c;我们的开发团队沉淀了许多宝贵的技术与经验&#xff0c;对电商领域业务也有相当深刻的理解。 去年初&#xff0c;团队承接了O2O商城的建设任务&#xff0c;还有即将成立的礼品中…

Java的变量与常量

目录 变量 声明变量 变量的声明类型 变量的声明方式&#xff1a;变量名 变量名的标识符 初始化变量 常量 关键字final 类常量 总结 变量和常量都是用来存储值和数据的基本数据类型存储方式&#xff0c;但二者之间有一些关键差别。 变量 在Java中&#xff0c;每个变…

人脸识别场景下Faiss大规模向量检测性能测试评估分析

在前面的两篇博文中&#xff0c;主要是考虑基于之前以往的人脸识别项目经历结合最近使用到的faiss来构建更加高效的检索系统&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《基于facenetfaiss开发构建人脸识别系统》 Facenet算法的优点&#xff1a;高准确率&#…

Mac提示文件:已损坏,无法打开。你应该把它移到废纸篓

文章目录 一、电脑信息二、打开任何来源设置三、更改应用程序拓展属性 一、电脑信息 我的是新版的Venture 13的系统。UI改的比较多。与之前的配置还是有很大的区别的。 打开下载的软件&#xff0c;显示已经损坏&#xff0c;打不开。抛开软件本身的问题外&#xff0c;一般是Ma…

第一百二十二天学习记录:C++提高:STL-vector容器(上)(黑马教学视频)

vector基本概念 功能&#xff1a; vector数据结构和数组非常相似&#xff0c;也称为单端数组 vector与普通数组区别&#xff1a; 不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a; 并不是在原空间之后续接新的空间&#xff0c;而是找更大的内…

PHP国外在线教育系统源码 在线课程系统源码 直播课程系统源码提供在线课程,现场课程,测验

Proacademy是在线教育一体化的解决方案&#xff0c;用于创建类似于Udemy、Skillshare、Coursera这种在线教育市场。 这个平台提供在线课程&#xff0c;现场课程&#xff0c;测验等等&#xff0c;并有一个基于实际业务需要的高级认证插件&#xff0c;程序基于Laravel强大的安全框…

Spring Boot 中自动装配机制的原理

问题描述 自动装配&#xff0c;简单来说就是自动把第三方组件的 Bean 装载到 Spring IOC 器里面&#xff0c;不需 要开发人员再去写 Bean 的装配配置。 在 Spring Boot 应用里面&#xff0c;只需要在启动类加上SpringBootApplication 注解就可以实现自动装配。 SpringBootAppli…

小程序学习(四):WXML模板语法

WXML模板语法-数据绑定 1.数据绑定的基本原则 ①在data中定义数据 ②在WXML中使用数据 2.动态绑定属性 WXML模板语法-事件绑定 3.什么是事件 4.小程序中常用的事件 5.事件对象的属性列表 6.target和currentTarget的区别 7.bindtap的语法格式 8.在事件处理函数中为data中的数据…

11.物联网操作系统内存管理

一。STM32编译过程及程序组成 STM32编译过程 程序的组成、存储与运行 MDK生成的主要文件分析 1.STM32编译过程 1.源文件&#xff08;Source code&#xff09;--》目标文件&#xff08;Object code&#xff09; .c(C语言)通过armcc生成.o&#xff0c;.s&#xff08;汇编&…

xcode 的app工程与ffmpeg 4.4版本的静态库联调,ffmpeg内下的断点无法暂停。

先阐述一下我的业务场景&#xff0c;我有一个iOS的app sdk项目&#xff0c;下面简称 A &#xff0c;以及运行 A 的 app 项目&#xff0c;简称 A demo 。 引用关系为 A demo 引用了 A &#xff0c;而 A 引用了 ffmpeg 的静态库&#xff08;.a文件&#xff09;。此时业务出现了 b…

Android 从LibVLC-android到自编译ijkplayer播放H265 RTSP

概述 ijkplayer: Android/iOS video player based on FFmpeg n3.4, with MediaCodec, VideoToolbox support. 官方的描述就这么简单的一句话&#xff0c;但丝毫都不影响它的强大。 从LibVLC 到 ijkplayer 截止到2023.7.20 LibVLC-Android 最大的问题在与OOM&#xff0c;测试了…

【Spring框架】Spring AOP

目录 什么是AOP&#xff1f;AOP组成Spring AOP 实现步骤Spring AOP实现原理JDK Proxy VS CGLIB 什么是AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的集中处理。⽐如…

打开的idea项目maven不生效

方法一&#xff1a;CtrlshiftA&#xff08;或者help---->find action&#xff09;&#xff0c; 输入maven&#xff0c; 点击add maven projects&#xff0c;选择本项目中的pom.xml配置文件&#xff0c;等待加载........ 方法二&#xff1a;view->tools windows->mave…

SpringBoot 日志文件

一、日志的作用 日志是程序的重要组成部分&#xff0c;想象一下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看日志&#xff0c;那么你能找到报错的原因吗 答案是否定的&#xff0c;写程序不是买彩票&#xff0c;不能完全靠猜&#xff0c;因此日志对于我们来说&a…