Flutter-仿携程首页类型切换

效果

唠叨

闲来无事,不小心下载了携程app,还幻想可以去旅游一番,奈何自己运气不好,自从高考时第一次吹空调导致自己拉肚子考试,物理,数学考了一半就交卷,英语2B铅笔除了问题,导致原来120多分变成50分,本来能上一本的我只能上个大专,家里没钱也不允许留级,只能去了最近比较火的宿迁学院,哈哈,只能自己在这里瞎写找理由了,哎,现在因为当初的运气不好导致现在专科找工作难的要死,虽然自考了本,奈何人家不要啊,不承认啊,简历只要被学习的本科前面加上自考2字压根没有面试,不加上到了最后交流水学历资料的时候又被卡死,这种情况遇到的太多太多,我现在的心情只能用一个诗来描述,那就是: 一句诗:”哎“

啥都不说了,直接上代码:

import 'package:flutter/material.dart';

import '../../widgets/xy_app_bar.dart';


class XieChengHomePage extends StatefulWidget {
  const XieChengHomePage({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return XieChengHomePageState();
  }
}

class XieChengHomePageState extends State<XieChengHomePage> with TickerProviderStateMixin {
  late TabController tabController;

  List<String> tabs = [
    "飞机票",
    "火车高铁票",
    "公交车",
  ];

  @override
  void initState() {
    super.initState();
    tabController = TabController(length: 3, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          backgroundColor: Colors.red,
          appBar: XYAppBar(
            title: "Trapezoid Indicator Example",
            onBack: () {
              Navigator.pop(context);
            },
          ),
          body: Column(
            children: [
              Padding(
                padding:
                    const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
                child: CustomTabbarWidget(
                  tabController: tabController,
                  tabs: tabs,
                ),
              ),
              Expanded(
                child: TabBarView(
                  controller: tabController,
                  children: const [
                    Center(child: Text('Tab 1 Content')),
                    Center(child: Text('Tab 2 Content')),
                    Center(child: Text('Tab 3 Content')),
                  ],
                ),
              ),
            ],
          )),
    );
  }
}

class CustomTabbarWidget extends StatefulWidget {
  final TabController tabController;
  final List<String> tabs;

  const CustomTabbarWidget({
    Key? key,
    required this.tabController,
    required this.tabs,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return CustomTabbarState();
  }
}

class CustomTabbarState extends State<CustomTabbarWidget> {
  @override
  void initState() {
    super.initState();
    widget.tabController.addListener(() {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      var tabWidth = constraints.maxWidth / widget.tabs.length;
      var currentWidth = tabWidth * 1.5;
      var offset =
          (constraints.maxWidth - currentWidth) / (widget.tabs.length - 1);
      return Container(
        width: constraints.maxWidth,
        child: Stack(
          children: [
            SizedBox(
              width: constraints.maxWidth,
              height: 60,
            ),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: Container(
                height: 50,
                decoration: BoxDecoration(
                  color: Colors.white.withAlpha(80),
                  borderRadius: const BorderRadius.only(
                    topLeft: Radius.circular(8),
                    topRight: Radius.circular(8),
                  ),
                ),
                child: Row(
                  children: widget.tabs.asMap().keys.map((index) {
                    return AnimatedContainer(
                      duration: const Duration(milliseconds: 200),
                      width: index == widget.tabController.index
                          ? currentWidth
                          : offset,
                      child: InkWell(
                        key: ObjectKey(index),
                        onTap: () {
                          widget.tabController.animateTo(index);
                          setState(() {});
                        },
                        child: Container(
                          alignment: Alignment.center,
                          child: Text(
                            widget.tabs[index],
                            style: const TextStyle(fontSize: 14),
                          ),
                        ),
                      ),
                    );
                  }).toList(),
                ),
              ),
            ),
            AnimatedPositioned(
              duration: const Duration(milliseconds: 300),
              left: widget.tabController.index * offset,
              bottom: 0,
              child: IgnorePointer(
                child: ClipPath(
                  clipper:
                      TrapezoidClipper(tabController: widget.tabController),
                  child: Container(
                      height: 60,
                      alignment: Alignment.center,
                      width: currentWidth,
                      decoration: const BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(8),
                            topRight: Radius.circular(8)),
                      ),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Expanded(
                            child: Center(
                              child: Text(
                                widget.tabs[widget.tabController.index],
                                style: const TextStyle(
                                    fontSize: 18, color: Colors.blue),
                              ),
                            ),
                          ),
                          TextUnderline(
                              text: widget.tabs[widget.tabController.index],
                              style: const TextStyle(
                                  fontSize: 18, color: Colors.blue),
                              lineColor: Colors.blue,
                              height: 4),
                        ],
                      )),
                ),
              ),
            ),
          ],
        ),
      );
    });
  }
}

class TrapezoidClipper extends CustomClipper<Path> {
  TrapezoidClipper({required this.tabController});

  TabController tabController;

  @override
  Path getClip(Size size) {
    var isLeft = tabController.index == 0;
    var isRight = tabController.index == tabController.length - 1;
    double inset = size.width * 0.1;
    double radius = 8.0;
    // path.moveTo(isLeft ? 0 : inset, 0);
    // path.lineTo(isRight ? size.width : size.width - inset, 0);
    // path.lineTo(size.width, size.height);
    // path.lineTo(0, size.height);

    final path = Path()
      ..moveTo(radius, 0) // 移动到起始点
      ..lineTo(size.width - radius, 0) // 顶边线
      ..quadraticBezierTo(size.width, 0, size.width, radius) // 右上角圆角
      ..lineTo(size.width, size.height - radius) // 右边线
      ..quadraticBezierTo(
          size.width, size.height, size.width - radius, size.height) // 右下角圆角
      ..lineTo(radius, size.height) // 底边线
      ..quadraticBezierTo(0, size.height, 0, size.height - radius) // 左下角圆角
      ..lineTo(0, radius) // 左边线
      ..quadraticBezierTo(0, 0, radius, 0); // 左上角圆角

    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}

class TextUnderline extends StatelessWidget {
  const TextUnderline({
    Key? key,
    required this.text,
    required this.style,
    required this.lineColor,
    required this.height,
  }) : super(key: key);
  final String text;
  final TextStyle style;
  final Color lineColor;
  final double height;

  @override
  Widget build(BuildContext context) {
    final textPainter = TextPainter(
      text: TextSpan(
        text: text,
        style: style,
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    var textWidth = textPainter.width;
    return Container(
      width: textWidth,
      height: height,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(
          Radius.circular(height),
        ),
        color: lineColor,
      ),
    );
  }
}

github.com/yixiaolunhui/flutter_xy

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

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

相关文章

MySQL数据库的基本概念与安装

目录 引言 一、数据库的基本概念 &#xff08;一&#xff09;数据、表与数据库 1.数据(Data) 2.表 3.数据库 &#xff08;二&#xff09;数据库管理系统 &#xff08;三&#xff09;数据库系统 二、数据库的发展 三、主流数据库的介绍 &#xff08;一&#xff09;关…

一命通关深度优先遍历

前言 在解释深度优先遍历之前&#xff0c;我想先让大家去思考一个可能从未想过的问题&#xff1a; 为什么我们在学习基础数据结构的时候&#xff0c;都没有出现dfs和bfs这两个词&#xff0c;而在学习二叉树和图的时候&#xff0c;突然蹦出了深度优先遍历和广度优先遍历这两个…

力扣 第 126 场双周赛 解题报告 | 珂学家 | 贡献法思维场 + 贪心构造 + 0-1背包

前言 整体评价 T3是道好题&#xff0c;一开始思路偏了往按字母前缀和和DP去想了&#xff0c;但是感觉很难下手&#xff0c;后来发现从贡献的角度&#xff0c;其实和位子无关系&#xff0c;只需要贪心即可。 T4也是一道贡献思路题&#xff0c;理清核心的点&#xff0c;就能简单…

Vue项目使用process.env关键字及Vue.config.js配置解决前端跨域问题

1.process.env 是Node.js 中的一个环境 1.打开命令行查看环境: 2.process.env与Vue CLI 项目 Vue Cli 有以下三种运行模式 development 模式用于 vue-cli-service serve test 模式用于 vue-cli-service test:unit production 模式用于 vue-cli-service build 和 vue-cli-se…

每日一练:LeeCode-167. 两数之和 II - 输入有序数组【双指针】

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < numbers.…

如何本地部署1Panel面板

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

YOLOv8改进 | 主干篇 | 利用图像分割网络UNetV2改善图像分割检测性能(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是利用图像分割网络UNetV2的主干来改进我们的YOLOv8分割模型&#xff08;本文的内容虽然YOLOv8所有的功能的用户都能使用&#xff0c;但是还是建议分割的用户使用&#xff09;&#xff0c;U-Net v2 旨在改进医学图像分割的性能&#xff…

C# 方法(函数)

文章目录 C# 方法&#xff08;函数&#xff09;简单示例程序代码运行效果 值传递和引用传递示例程序 运行效果按输出传递参数运行结果 C# 方法&#xff08;函数&#xff09; 简单示例 程序代码 访问的限制符 using System; using System.Collections.Generic; using Syste…

GO-初识包管理

初识包管理&#xff0c;知道项目中文件和文件夹之间的关系 输出&#xff0c;代码&#xff0c;在go编译器运行时会显示在屏幕中 初识数据类型 整型&#xff0c;数字。例如&#xff1a;1、2、3、4 字符串类型&#xff0c;表示文本信息的。例如:“张三”“李四” 布尔类型&#x…

Day51-Nginx多实例知识与大厂企业级实战

Day51-Nginx多实例知识与大厂企业级实战 1. 什么是nginx多实例&#xff1f;2. 为什么要用多实例&#xff1f;3. 大厂数百个业务项目&#xff0c;如何来管理&#xff1f;4. 大厂上百项目web分用户解决方案4.1 编译nginx环境实践&#xff1a;4.2 zuma实例(利用普通用户权限将不同…

Arduino 开发 野火ESP8266模块的串口通信

一、硬件连接 Arduino ESP8266 串口通信是一个常见的任务&#xff0c;允许通过串行接口与其他设备或计算机进行通信。 连接硬件&#xff1a;将野火ESP8266模块通过USB连接到电脑。注意电源为3.3V。 选择开发板和端口&#xff0c;在Arduino IDE中&#xff0c;选择ESP8266开发板…

图神经网络实战(5)——常用图数据集

图神经网络实战&#xff08;5&#xff09;——常用图数据集 0. 前言0. 图数据集介绍2. Cora 数据集3. Facebook Page-Page 数据集小结系列链接 0. 前言 图数据集往往比单纯的连接集合更丰富&#xff0c;节点和边也可以具有表示分数、颜色、单词等的特征。在输入数据中包含这些…

NLP---Bert分词

目录&#xff1a; Q&#xff1a;bert分词步骤1&#xff1a;构建N * N 的相关性矩阵&#xff0c;计算相邻两个字的相关性&#xff0c;低的话&#xff08;<阈值&#xff09;就切割。2&#xff1a;将A词进行mask计算出A的embedding&#xff0c;然后将AB两个词一起mask&#xff…

【滑动窗口】长度最小的子数组|无重复字符的最长子串|最大连续1的个数 III|将 x 减到 0 的最小操作数

1. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 1.题目解析&#xff1a; 2.算法原理 &#xff08;1&#xff09;方法一&#xff1a;暴力列举出所有的子数组的和 时间复杂度&#xff1a;O&#xff08;n**2&#xff09;&#xff1a;枚举所有子数组O&#xff08;…

使用 Redux 管理 React 应用状态

使用 Redux 管理 React 应用状态 在复杂的 React 应用中&#xff0c;管理组件状态变得越来越复杂&#xff0c;这时候引入 Redux 可以帮助我们更好地管理状态。Redux 是一个可预测状态容器&#xff0c;它可以帮助我们统一管理应用的状态&#xff0c;使得状态变化更加可控。本文…

机器学习基本算法(一)

1.线性回归算法 Linear Regression 线性回归算法&#xff08;Linear Regression&#xff09;是一种预测性的建模技术&#xff0c;它研究的是因变量&#xff08;目标&#xff09;和自变量&#xff08;预测器&#xff09;之间的关系。线性回归假设目标值与特征之间线性相关&…

MacOS Xcode 使用LLDB调试Qt的 QString

环境&#xff1a; MacOS&#xff1a; 14.3Xcode&#xff1a; Version 15.0Qt&#xff1a;Qt 6.5.3 前言 Xcode 中显示 预览 QString 特别不方便, 而Qt官方的 lldb 脚本debugger/lldbbridge.py一直加载失败&#xff0c;其他第三方的脚本都 不兼容当前的 环境。所以自己研究写…

【how2j练习题】JS部分阶段练习

练习一 <!--练习&#xff0c;做一个简单的加法计算器--><html><input type"text" size "2" id "num1" ><input type"text" size "2" id "num2" ><input type"text" siz…

Density Profile Tool 程序(1D):通过 VMD 可计算 LAMMPS 轨迹的数密度分布(二)

​ 给大家推荐一个结构轨迹后处理程序 Density Profile Tool&#xff0c;目前尝试过的轨迹文件只有LAMMPS文件&#xff0c;感兴趣的大家可以试试其他轨迹文件。这个后处理软件可以计算数密度、质量、电荷和电子分布等性质。 感谢论文的原作者&#xff01; VMD 插件&#xff1…

stm32之GPIO电路介绍

文章目录 1 GPIO介绍2 GPIO的工作模式2.1 浮空输入2.2 上拉输入2.3 下拉输入2.4 模拟输入2.5 开漏输出2.6 推挽输出2.7 复用开漏输出2.8 复用推挽输出2.9 其他 3 应用方式4 常用库函数 1 GPIO介绍 保护二极管&#xff1a;保护引脚&#xff0c;让引脚的电压位于正常的范围施密特…