flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

news2024/12/23 12:50:49

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

在之前的开发过程中,需要实现卡片轮播效果,但是卡片轮播需要中间大、两边小一些的效果,这里就使用到了Swiper。具体效果如视频所示
添加链接描述
这里需要的效果是中间大、两边小一些,中间的卡片在最上层,两边的卡片会被中间的卡片挡住一部分。所以需要处理一下Custom_layout样式中Widget层级关系。
在这里插入图片描述

一、引入Swiper

在工程的pubspec.yaml中引入swiper

  # 轮播图
  flutter_swiper_null_safety: ^1.0.2
    

二、Swiper使用

Swiper无限轮播

通过Swiper()来构建轮播图控件,可以同步不同的属性搭配不同的效果

默认效果

Container(
    height: 200,
    child: new Swiper(
        itemBuilder: (BuildContext context,int index){
            return new Image.network(imgs[index],fit: BoxFit.cover,);
        },
        itemCount: imgs.length,
        pagination: new SwiperPagination(),//如果不填则不显示指示点
        control: new SwiperControl(),//如果不填则不显示左右按钮
    ),
),
    

3D卡片滚动

Container(
    height:  200,
    child: new Swiper(
        itemBuilder: (BuildContext context, int index) {
            return new Image.network(imgs[index],fit: BoxFit.cover,);
        },
        itemCount: imgs.length,
        viewportFraction: 0.8,
        scale: 0.9,
    ),
),
    

无限卡片堆叠

Container(
    height: 200,
    child: new Swiper(
        itemBuilder: (BuildContext context, int index) {
            return new Image.network(imgs[index],fit: BoxFit.cover,);
        },
        itemCount: imgs.length,
        itemWidth: 300.0,
        layout: SwiperLayout.STACK,
    ),
),
    

无限卡片堆叠2

Container(
    height: 200,
    child: new Swiper(
        itemBuilder: (BuildContext context, int index) {
            return new Image.network(imgs[index],fit: BoxFit.cover,);
        },
        itemCount: imgs.length,
        itemWidth: 300.0,
        itemHeight: 300.0,
        layout: SwiperLayout.TINDER,
    ),
),
    

自定义效果

Container(
    height: 200,
    child: new Swiper(
    layout: SwiperLayout.CUSTOM,
    customLayoutOption: new CustomLayoutOption(
        startIndex: -1,
        stateCount: 3
    ).addRotate([
        -45.0/180,
        0.0,
        45.0/180
    ]).addTranslate([
        new Offset(-370.0, -40.0),
        new Offset(0.0, 0.0),
        new Offset(370.0, -40.0)
    ]),
    itemWidth: 300.0,
    itemHeight: 200.0,
    itemBuilder: (context, index) {
        return new Image.network(imgs[index],fit: BoxFit.cover,);
    },
    itemCount: imgs.length),
)
    

三、更改Custom_layout样式中Widget层级

需要的效果是中间大、两边小一些,中间的卡片在最上层,两边的卡片会被中间的卡片挡住一部分

这里使用的是SwiperLayout.CUSTOM,

这里就需要查看源码,更改Custom_layout样式中Widget层级关系,更改Stack的子Widget层级关系,需要调整中间的卡片在最上层。

找到Custom_layout.dart的源码,找到Widget _buildAnimation(BuildContext context, Widget? w)。

需要更改list,重新排列list

if (list.isNotEmpty) {
      int length = list.length;
      int mid = length~/2;

      List<Widget> transList = [];
      for (int i = mid; i >= 0; i--) {
        List<Widget> subList = [];
        for (int index = 0; index < length; index++) {
          int abs = (index - mid).abs();
          if (abs == i) {
            subList.add(list[index]);
          }
        }
        transList.addAll(subList);
      }

      print("transList:${transList}");
      if (transList.isNotEmpty && transList.length == list.length) {
        list = transList;
      }
    }
    

更改后的_buildAnimation代码如下

Widget _buildAnimation(BuildContext context, Widget? w) {
    List<Widget> list = [];

    if (_animationCount != null) {
      double? animationValue = _animation?.value;
      for (int i = 0; i < _animationCount!; ++i) {
        int realIndex = _currentIndex + i + (_startIndex ?? 0);
        realIndex = realIndex % widget.itemCount;
        if (realIndex < 0) {
          realIndex += widget.itemCount;
        }
        if (animationValue != null) {
          list.add(_buildItem(i, realIndex, animationValue));
        }
      }
    }

    if (list.isNotEmpty) {
      int length = list.length;
      int mid = length~/2;

      List<Widget> transList = [];
      for (int i = mid; i >= 0; i--) {
        List<Widget> subList = [];
        for (int index = 0; index < length; index++) {
          int abs = (index - mid).abs();
          if (abs == i) {
            subList.add(list[index]);
          }
        }
        transList.addAll(subList);
      }

      print("transList:${transList}");
      if (transList.isNotEmpty && transList.length == list.length) {
        list = transList;
      }
    }

    return new GestureDetector(
      behavior: HitTestBehavior.opaque,
      onPanStart: _onPanStart,
      onPanEnd: _onPanEnd,
      onPanUpdate: _onPanUpdate,
      child: new ClipRect(
        child: new Center(
          child: _buildContainer(list),
        ),
      ),
    );
  }
    

四、实现中间大、两边小一些,中间的卡片在最上层,两边的卡片会被中间的卡片挡住的效果

需要实现效果的时候,我们需要使用Swiper的custom,使用CustomLayoutOption添加addScale和addOpacity以及addTranslate来确定不同的卡片的缩放大小、透明度、以及offset

代码如下

Swiper(
              autoplay: true,
              layout: SwiperLayout.CUSTOM,
              customLayoutOption:
                  CustomLayoutOption(startIndex: 0, stateCount: 5)
                    ..addScale([
                      0.6,
                      0.8,
                      1.0,
                      0.8,
                      0.6,
                    ], Alignment.center)
                    ..addOpacity([
                      1.0,
                      1.0,
                      1.0,
                      1.0,
                      1.0,
                    ])
                    ..addTranslate([
                      Offset(-180.0, 0),
                      Offset(-80.0, 0),
                      Offset(0.0, 0.0),
                      Offset(80.0, 0),
                      Offset(180.0, 0),
                    ]),
              itemWidth: 230.0,
              itemHeight: 230.0,
              itemBuilder: (context, index) {
                return SwiperCard(imageUrl: imageUrls[index]);
              },
              itemCount: imageUrls.length,
            )
    

页面的完整代码如下

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';

class SwiperPage extends StatefulWidget {
  const SwiperPage({super.key});

  @override
  State<SwiperPage> createState() => _SwiperPageState();
}

class _SwiperPageState extends State<SwiperPage> {
  List<String> imageUrls = [];

  @override
  void initState() {
    // TODO: implement initState
    imageUrls = [
      "https://d-ssl.dtstatic.com/uploads/blog/202301/08/20230108192142_ff632.thumb.1000_0.jpeg_webp",
      "https://d-ssl.dtstatic.com/uploads/blog/202301/08/20230108192143_f4355.thumb.1000_0.jpeg_webp",
      "https://d-ssl.dtstatic.com/uploads/blog/202301/08/20230108192146_0aaf2.thumb.1000_0.jpeg_webp",
      "https://d-ssl.dtstatic.com/uploads/blog/202301/08/20230108192148_357ff.thumb.1000_0.jpeg_webp",
      "https://d-ssl.dtstatic.com/uploads/blog/202301/08/20230108192149_92c71.thumb.1000_0.jpeg_webp"
    ];
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        title: const Text('SwiperPage'),
      ),
      body: Container(
        width: screenSize.width,
        height: screenSize.height,
        child: Stack(
          alignment: Alignment.center,
          children: [
            Swiper(
              autoplay: true,
              layout: SwiperLayout.CUSTOM,
              customLayoutOption:
                  CustomLayoutOption(startIndex: 0, stateCount: 5)
                    ..addScale([
                      0.6,
                      0.8,
                      1.0,
                      0.8,
                      0.6,
                    ], Alignment.center)
                    ..addOpacity([
                      1.0,
                      1.0,
                      1.0,
                      1.0,
                      1.0,
                    ])
                    ..addTranslate([
                      Offset(-180.0, 0),
                      Offset(-80.0, 0),
                      Offset(0.0, 0.0),
                      Offset(80.0, 0),
                      Offset(180.0, 0),
                    ]),
              itemWidth: 230.0,
              itemHeight: 230.0,
              itemBuilder: (context, index) {
                return SwiperCard(imageUrl: imageUrls[index]);
              },
              itemCount: imageUrls.length,
            )
          ],
        ),
      ),
    );
  }
}

class SwiperCard extends StatelessWidget {
  const SwiperCard({
    super.key,
    required this.imageUrl,
  });

  final String imageUrl;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 230,
      height: 230,
      clipBehavior: Clip.hardEdge,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.all(
          Radius.circular(10),
        ),
        border: Border.all(
          color: Color(0xFF5C6BC0),
          style: BorderStyle.solid,
          width: 3,
        ),
        boxShadow: [
          BoxShadow(
            color: Color(0xFFE8EAF6),
            offset: Offset(0, -5),
            blurRadius: 10,
          )
        ],
      ),
      child: Stack(alignment: Alignment.center, children: [
        Positioned(
          top: 0,
          child: Image.network(
            imageUrl,
            width: 230,
            height: 230,
          ),
        ),
      ]),
    );
  }
}

    

最终实现了效果。

五、小结

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

golang 函数选项模式

一 什么是函数选项模式 函数选项模式允许你使用接受零个或多个函数作为参数的可变构造函数来构建复杂结构。我们将这些函数称为选项&#xff0c;由此得名函数选项模式。 例子&#xff1a; 有业务实体Animal结构体&#xff0c;构造函数NewAnimal&#xff08;&#xff09;&…

Liunx配置Tomcat自启动

Liunx配置Tomcat自启动 Tomcat安装配置Tomcat开机启动 Tomcat安装 下载tomcat软件安装包&#xff0c;上传软件包到Liunx服务器。 解压软件包到opt目录下 tar -xvf apache-tomcat-9.0.76.tar.gz -c /opt配置Tomcat开机启动 &#xff08;1&#xff09;修改Tomcat bin目录下的ca…

vue项目和wx小程序

wx:key 的值以两种形式提供&#xff1a; 1、字符串&#xff0c;代表在 for 循环的 array 中 item 的某个 property&#xff0c;该 property 的值需要是列表中唯一的字符串或数字&#xff0c;且不能动态改变。 2、保留关键字 this 代表在 for 循环中的 item 本身&#xff0c;这种…

学习笔记:Pytorch 搭建自己的Faster-RCNN目标检测平台

B站学习视频 up主的csdn博客 1、什么是Faster R-CNN 2、pytorch-gpu环境配置&#xff08;跳过&#xff09; 3、Faster R-CNN整体结构介绍 Faster-RCNN可以采用多种的主干特征提取网络&#xff0c;常用的有VGG&#xff0c;Resnet&#xff0c;Xception等等。 Faster-RCNN对输入…

ArkTS-列表选择弹窗

调用 每一个sheet中的action对应其点击事件 Button(列表选择弹窗).onClick(() > {ActionSheet.show({title: 列表选择弹窗标题,message: 内容,autoCancel: true,confirm: {value: 确认,action: () > {console.log(Get Alert Dialog handled)}},cancel: () > {console.…

INFINI Easysearch 与华为鲲鹏完成产品兼容互认证

何为华为鲲鹏认证 华为鲲鹏认证是华为云围绕鲲鹏云服务&#xff08;含公有云、私有云、混合云、桌面云&#xff09;推出的一项合作伙伴计划&#xff0c;旨在为构建持续发展、合作共赢的鲲鹏生态圈&#xff0c;通过整合华为的技术、品牌资源&#xff0c;与合作伙伴共享商机和利…

视频分割方法:批量剪辑高效分割视频,提取m3u8视频技巧

随着互联网的快速发展&#xff0c;视频已成为获取信息、娱乐、学习等多种需求的重要载体。然而&#xff0c;很多时候&#xff0c;需要的只是视频的一部分&#xff0c;这就要对视频进行分割。而m3u8视频是一种常见的流媒体文件格式&#xff0c;通常用于在线视频播放。本文将分享…

unity学习笔记13

一、常用物理关节 Unity中的物理关节&#xff08;Physics Joints&#xff09;是用于在游戏中模拟和控制物体之间的连接。物理关节允许你在对象之间应用各种约束&#xff0c;例如旋转、移动或固定连接&#xff0c;以模拟真实世界中的物理交互。 物理关节类型&#xff1a; 1.F…

同旺科技 分布式数字温度传感器 -- OPC Servers测试

内附链接 1、数字温度传感器 主要特性有&#xff1a; ● 支持PT100 / PT1000 两种铂电阻&#xff1b; ● 支持 2线 / 3线 / 4线 制接线方式&#xff1b; ● 支持5V&#xff5e;17V DC电源供电&#xff1b; ● 支持电源反接保护&#xff1b; ● 支持通讯波特率1200bps、2…

spring日志输出到elasticsearch

1.maven <!--日志elasticsearch--><dependency><groupId>com.agido</groupId><artifactId>logback-elasticsearch-appender</artifactId><version>3.0.8</version></dependency><dependency><groupId>net.l…

koa2项目中封装log4js日志输出

1.日志输出到控制台 npm i log4js -D 封装log4js文件&#xff1a; 注意&#xff1a;每次都要重新获取log4js.getLogger(debug)级别才能生效 const log4js require("log4js");const levels {trace: log4js.levels.TRACE,debug: log4js.levels.DEBUG,info: log4js.…

通过C#获取Windows设置的夏令时开关

C#获取Windows夏令时开关 // 获取所有的时区信息 var allTimeZones TimeZoneInfo.GetSystemTimeZones().ToList();通过接口可以看到, 字段SupportsDaylightSavingTime代表是否支持配置夏令时 // 获取当前Window设置的时区 var tmpLocal TimeZoneInfo.Local;但是取Local 信息…

python+pytest接口自动化(5)-requests发送post请求

简介 在HTTP协议中&#xff0c;与get请求把请求参数直接放在url中不同&#xff0c;post请求的请求数据需通过消息主体(request body)中传递。 且协议中并没有规定post请求的请求数据必须使用什么样的编码方式&#xff0c;所以其请求数据可以有不同的编码方式&#xff0c;服务…

文件格式扩展名转换:将图片png扩展名批量改为jpg的方法

在处理大量图片文件时&#xff0c;可能会遇到需要将文件格式扩展名进行转换的情况。比如&#xff0c;将图片文件从PNG格式转换为JPG格式。这不仅可以节省存储空间&#xff0c;还可以提高图片加载速度&#xff0c;特别是在网页设计中。本文详解如何将PNG图片批量转换为JPG格式的…

java设计模式学习之【建造者模式】

文章目录 引言建造者模式简介定义与用途实现方式&#xff1a; 使用场景优势与劣势建造者模式在spring中的应用CD&#xff08;光盘&#xff09;的模拟示例UML 订单系统的模拟示例UML 代码地址 引言 建造者模式在创建复杂对象时展现出其强大的能力&#xff0c;特别是当这些对象需…

智跃人力资源管理系统 SQL注入漏洞复现

0x01 产品简介 智跃人力资源管理系统是基于B/S网页端广域网平台&#xff0c;一套考勤系统即可对全国各地多个分公司进行统一管控&#xff0c;成本更低。信息共享更快。跨平台&#xff0c;跨电子设备 0x02 漏洞概述 智跃人力资源管理系统GenerateEntityFromTable.aspx接口处存在…

基于单片机的排队叫号系统设计

1&#xff0e;设计任务 利用AT89C51单片机为核心控制元件,设计一个节日彩灯门&#xff0c;设计的系统实用性强、操作简单&#xff0c;实现了智能化、数字化。 基本要求&#xff1a;利用单片机AT89C51设计排队叫号机&#xff0c;能实现叫号功能。 创新&#xff1a;能显示叫号…

【Redis】Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

目录 Redis的慢查询 慢查询配置 慢查询操作命令 慢查询建议 Pipeline 事务 Redis的事务原理 Redis的watch命令 Pipeline和事务的区别 Lua Lua入门 安装Lua Lua基本语法 注释 标示符 关键词 全局变量 Lua中的数据类型 Lua 中的函数 Lua 变量 Lua中的控制语句…

Ubuntu22.04 使用Docker部署Neo4j出错 Exited(70)

项目场景&#xff1a; 最近需要使用Neo4j图数据库&#xff0c;因此打算使用docker部署 环境使用WSL Ubuntu22.04 问题描述 拉下最新Neo4j镜像&#xff0c;执行命令部署 启动容器脚本 docker run -d -p 7474:7474 -p 7687:7687 \ --name neo4j \ --env "NEO4J_AUTHneo…

【Web】NewStarCTF Week4 个人复现

目录 ①逃 ②More Fast ③midsql ④InjectMe ⑤PharOne ⑥flask disk ①逃 一眼字符串逃逸 bad 替换为 good 字符增加一位 先构造一下试试 <?php class GetFlag {public $key;public $cmd "ls /";} $a new GetFlag(); echo serialize($a); 得到O:7:…