flutter开发实战-自定义Switch开关控件Widget

news2024/9/23 19:28:03

flutter开发实战-自定义Switch开关控件
在flutter中实现自定义Switch,主要实现类似IOS的UISwitch样式的开关控件

一、效果图

在这里插入图片描述

二、实现Switch开关的Widget

实现自定义Switch的Widget,主要实现交织动画。
交织动画
有些时候我们可能会需要一些复杂的动画,这些动画可能由一个动画序列或重叠的动画组成。一个动画组合在不同阶段包含了多种动画,要实现这种效果,需要使用交织动画(Stagger Animation)实现会比较方法。

Stagger Animation

  • 1、使用多个动画对象(Animation)。
  • 2、多个Animation使用同一个AnimationController控制。
  • 3、需要设置每一个动画对象指定时间间隔(Interval)

这里实现自定义Switch的Widget用到了colorAnimation,positionAnimation,更改颜色动画及位置动画。多个动画的时候需要在Widget中添加TickerProviderStateMixin。通过TickerProviderStateMixin实现TickerProvider获取对象的通知。TickerProvider来控制Ticker的通知,Ticker可以应用在Flutter中的每个对象上,一旦某个对象实现了Ticker的功能,每次动画帧改变,屏幕重绘时就会通知这个对象。

自定义Switch定义了onChanged实现将开关callback到使用的Widget上。

具体代码实现如下

/// 定制switch
class CustomSwitch extends StatefulWidget {
  const CustomSwitch({
    Key? key,
    required this.value,
    this.bgColor,
    this.bgBorderColor,
    this.bgOpenBorderColor,
    this.bgBorderWidth,
    this.openBgColor,
    this.color,
    this.openColor,
    this.width,
    this.height,
    this.borderColor,
    this.openBorderColor,
    this.borderWidth,
    required this.onChanged,
  }) : super(key: key);

  final bool value;
  final double? width;
  final double? height;
  final Color? bgBorderColor;
  final Color? bgOpenBorderColor;
  final double? bgBorderWidth;
  final Color? bgColor;
  final Color? openBgColor;
  final Color? color;
  final Color? openColor;

  final Color? borderColor;
  final Color? openBorderColor;
  final double? borderWidth;

  final ValueChanged<bool>? onChanged;

  
  State<CustomSwitch> createState() => _CustomSwitchState();
}

class _CustomSwitchState extends State<CustomSwitch>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _positionAnimation;
  late Animation<Color?> _colorAnimation;
  late Animation<Color?> _bgColorAnimation;
  late Animation<Color?> _bgBorderColorAnimation;
  late Animation<Color?> _borderColorAnimation;

  bool _switchOpen = false;

  Color _bgColor = Colors.black12;
  Color _openBgColor = Colors.lightBlueAccent;
  Color _color = Colors.black26;
  Color _openColor = Colors.lightBlue;

  Color _bgBorderColor = Colors.black12;
  Color _bgOpenBorderColor = Colors.lightBlueAccent;

  Color _borderColor = Colors.black12;
  Color _openBorderColor = Colors.lightBlue;

  double _width = 50.0;
  double _height = 30.0;
  double _minSize = 30.0;

  bool _isAnimating = false; // 动画中

  double _space = 2.0;

  bool _isStartAnimating = false;

  
  void initState() {
    // TODO: implement initState
    _switchOpen = widget.value;

    _bgColor = widget.bgColor ?? Colors.black12;
    _openBgColor = widget.openBgColor ?? Colors.lightBlueAccent;
    _color = widget.color ?? Colors.blueGrey;
    _openColor = widget.openColor ?? Colors.lightBlue;

    _bgBorderColor = widget.bgBorderColor ?? Colors.black12;
    _bgOpenBorderColor = widget.bgOpenBorderColor ?? Colors.lightBlueAccent;

    _borderColor = widget.borderColor ?? Colors.black12;
    _openBorderColor = widget.openBorderColor ?? Colors.lightBlue;

    if (widget.width != null && widget.height != null) {
      _width = widget.width!;
      _height = widget.height!;
    }

    _minSize = min(_width, _height) - _space;

    super.initState();

    runAnimation();
  }

  void runAnimation() {
    Color _bgBeginColor;
    Color _bgEndColor;

    Color _beginColor;
    Color _endColor;

    double _beginP;
    double _endP;

    Color _bgBorderBeginColor;
    Color _bgBorderEndColor;

    Color _borderBeginColor;
    Color _borderEndColor;

    if (_switchOpen) {
      _bgBeginColor = _openBgColor;
      _bgEndColor = _bgColor;

      _beginColor = _openColor;
      _endColor = _color;

      _bgBorderBeginColor = _bgOpenBorderColor;
      _bgBorderEndColor = _bgBorderColor;

      _borderBeginColor = _openBorderColor;
      _borderEndColor = _borderColor;

      _beginP = _width - _minSize - _space;
      _endP = _space;
    } else {
      _bgBeginColor = _bgColor;
      _bgEndColor = _openBgColor;

      _beginColor = _color;
      _endColor = _openColor;

      _bgBorderBeginColor = _bgBorderColor;
      _bgBorderEndColor = _bgOpenBorderColor;

      _borderBeginColor = _borderColor;
      _borderEndColor = _openBorderColor;

      _beginP = _space;
      _endP = _width - _minSize - _space;
    }

    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 200));

    // 移动位置
    _positionAnimation = Tween<double>(
      begin: _beginP,
      end: _endP,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(
          0.0, 1.0, //间隔,后20%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    _colorAnimation = ColorTween(
      begin: _beginColor,
      end: _endColor,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(
          0.0, 1.0, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    _bgColorAnimation = ColorTween(
      begin: _bgBeginColor,
      end: _bgEndColor,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(
          0.0, 1.0, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    _bgBorderColorAnimation = ColorTween(
      begin: _bgBorderBeginColor,
      end: _bgBorderEndColor,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(
          0.0, 1.0, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    _borderColorAnimation = ColorTween(
      begin: _borderBeginColor,
      end: _borderEndColor,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(
          0.0, 1.0, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    _controller.addListener(() {
      if (mounted) {
        setState(() {});
      }
    });

    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _isAnimating = false;
        _isStartAnimating = true;

        // 完成
        if (widget.onChanged != null) {
          widget.onChanged!(!_switchOpen);
        }
      }
    });
  }

  void animationDispose() {
    _controller.dispose();
  }

  void onSwitchPressed() {
    if (_isAnimating) {
      return;
    }

    _isAnimating = true;

    if (_isStartAnimating) {
      _switchOpen = !_switchOpen;
    }
    runAnimation();
    _controller.forward();
  }

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

  
  Widget build(BuildContext context) {
    double radius = _minSize / 2.0;
    double bgRadius = _height / 2.0;
    return GestureDetector(
      onTap: () {
        onSwitchPressed();
      },
      child: Container(
        width: _width,
        height: _height,
        child: Stack(
          alignment: Alignment.center,
          children: [
            Container(
              width: _width,
              height: _height,
              decoration: BoxDecoration(
                color: _bgColorAnimation.value,
                borderRadius: BorderRadius.circular(bgRadius),
                border: Border.all(
                  color: _bgBorderColorAnimation.value ?? Colors.transparent,
                  width: widget.bgBorderWidth ?? 0,
                  style: BorderStyle.solid,
                ),
              ),
            ),
            Positioned(
              left: _positionAnimation.value,
              child: Container(
                width: _minSize,
                height: _minSize,
                decoration: BoxDecoration(
                  color: _colorAnimation.value,
                  borderRadius: BorderRadius.circular(radius),
                  border: Border.all(
                    color: _borderColorAnimation.value ?? Colors.transparent,
                    width: widget.borderWidth ?? 0,
                    style: BorderStyle.solid,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

三、小结

flutter开发实战-自定义Switch开关控件,主要交织动画(Stagger Animation),通过控制不同的动画来实现类似iOS中的UISwitch控件样式。

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

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

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

相关文章

apache 安装配置 基础篇(二)

在使用Apache时&#xff0c;配置虚拟主机可以允许一个单一的Apache服务器提供不同的网站、域名或IP地址。 Apache虚拟主机的一个重要作用是更好的网站管理能力。当一个虚拟主机被创建时&#xff0c;您可以轻松地分配不同的域名和IP地址&#xff0c;设置不同的目录、文件和权限…

【电影推荐系统】基于 ALS 的协同过滤推荐算法

目录 目的 用户电影推荐矩阵主要思路如下 1 UserId 和 MovieID 做笛卡尔积&#xff0c;产生&#xff08;uid&#xff0c;mid&#xff09;的元组 2 通过模型预测&#xff08;uid&#xff0c;mid&#xff09;的元组。 3 将预测结果通过预测分值进行排序。 4 返回分值最大的 …

js debugger的两种方式

第一种&#xff1a;在js代码中加上debugger class ReactiveEffect {constructor(fn, scheduler) {this.fn fn;this.scheduler scheduler;this.active true;this.deps [];console.log("创建 ReactiveEffect 对象");}run() {debugger; console.log("run…

Spring高手之路8——Spring Bean模块装配的艺术:@Import详解

文章目录 1. Spring手动装配基础2. Spring框架中的模块装配2.1 Import注解简单使用 3. Import模块装配的四种方式3.1 Import注解的功能介绍3.2 导入普通类与自定义注解的使用3.3 导入配置类的策略3.4 使用ImportSelector进行选择性装配3.5 使用ImportBeanDefinitionRegistrar进…

指针进阶详解

目录 指针基本概念 1.字符指针 2.指针数组 3.数组指针 对数组名的理解 小结 指针基本概念 在初阶指针中我们了解到一些指针的基本概念: 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块内存 2.指针的大小是固定的4/8个字节&#xff08;32位/64位平台&…

详解c++---哈希封装

目录标题 哈希桶的代码哈希桶的修改迭代器的实现const迭代器 哈希桶的代码 通过前面的学习大家应该能够很容易的写出下面的代码&#xff1a; #pragma once #include<iostream> #include<vector> using namespace std; template<class K,class V> struct Ha…

2023年互联网行业研究报告

第一章 行业概况 互联网行业是一个广泛的领域&#xff0c;包括所有利用互联网技术进行商业活动的企业和组织。这个行业的核心是互联网&#xff0c;一个全球性的网络&#xff0c;连接着数以亿计的计算设备和用户&#xff0c;使他们可以共享信息、资源和服务。 互联网行业包括网…

apache 安装配置 基础篇(-)

download 地址 apache下载 ApacheHaus是免安装的&#xff0c; 然后解压上面的文件&#xff0c;把里面 因apache 默认端口是80&#xff0c;如果这个端口被占用&#xff0c;apache服务是启动不起来的 netstat -ano|findstr 80 apache 修改端口号 创建apache服务 在apa…

ESP32-H2 固件烧录需满足的硬件环境整理

ESP32-H2 默认通过 UART0 &#xff08;即 TXD&#xff08;GPIO24&#xff09;和 RXD&#xff08;GPIO23&#xff09;&#xff09;下载固件。 Windows 下可使用 Flash download tool 工具来下载编译后的 bin 文件&#xff1b; 运行 flash_download_tool.exe 的文件 选择开发…

SkyEye处理器仿真系列:龙芯2K1000处理器

​SkyEye简介&#xff1a; 天目全数字实时仿真软件SkyEye作为基于可视化建模的硬件行为级仿真平台&#xff0c;能够为嵌入式软件提供虚拟化运行环境&#xff0c;开发、测试人员可在该虚拟运行环境上进行软件开发、软件测试和软件验证活动。小到芯片&#xff0c;大到系统&#…

win10 DBeaver (升级)下载、安装、彻底卸载

DBeaver &#xff08;升级&#xff09;下载及安装 一、DBeaver 下载二、安装三、DBeaver 的基本使用 - mysql连接四、DBeaver 彻底卸载 DBeaver是一种通用数据库管理工具&#xff0c;适用于需要以专业方式使用数据的每个人&#xff1b;适用于开发人员&#xff0c;数据库管理员&…

苹果笔买原装的还是随便买?便宜好用的手写笔推荐

自从ipad和其他的平板电脑都搭配上了电容笔以后&#xff0c;电容笔很好地取代了我们的手指&#xff0c;书写的效率就大大提升了&#xff0c;但由于苹果原装电容笔的价格不够人性化&#xff0c;一直高居不下给普通人带来了很大的负担&#xff0c;特别是对于学生们来说&#xff0…

QT DAY1

做一个窗口界面 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);//设置窗口标题、图标this->setWindowTitle("Fly_Chat")…

6、Flume安装部署

按照采集通道规划&#xff0c;需在hadoop102&#xff0c;hadoop103&#xff0c;hadoop104三台节点分别部署一个Flume。可参照以下步骤先在hadoop102安装&#xff0c;然后再进行分发。 1、Flume入门 1.1、 Flume安装部署 1.1.1、 安装地址 &#xff08;1&#xff09; Flume官…

全网最牛,Web自动化测试Selenium八大元素定位实战(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 安装Selenium和下…

BFT 最前线|OpenAI暂时下线ChatGPT”浏览“功能;Stability AI CEO:5年内,人类程序员将不复存在

原创 | 文 BFT机器人 AI视界 TECHNOLOGY NEWS 01 Open AI暂时下线ChatGPT“浏览”功能 日前OpenAI方面宣布&#xff0c;面向ChatGPT Plus用户的"浏览"功能会在某些情况下出现故障&#xff0c;因此已于7月3日暂时禁用了这一功能。该功能是为了提高ChatGPT的搜索体验…

威胁检测和取证日志分析

在网络中&#xff0c;威胁是指可能影响其平稳运行的恶意元素。因此&#xff0c;对于任何希望搁置任何财政损失或生产力下降机会的组织来说&#xff0c;威胁检测都是必要的。为了先发制人地阻止来自各种来源的任何此类攻击&#xff0c;需要有效的威胁检测情报。 威胁检测可以是…

mmap函数

参考 https://blog.csdn.net/bhniunan/article/details/104105153void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);参数 addr&#xff1a;出参&#xff0c; 指定映射的起始地址&#xff0c;通常设为NULL&#xff0c;由内核来分配 len&#x…

网络编程3——TCP Socket实现的客户端服务器通信完整代码(详细注释帮你快速理解)

文章目录 前言一、理论准备Socket套接字是什么TCP协议的特点 二、TCP 流套接字提供的APIServerSocket APISocket API 三、代码实现请求响应式 客户端服务器服务器客户端疑惑解答为什么服务器进程需要手动指定端口号而客户端进程不需要为什么客户端中的服务器IP与端口号是"…

Mysql架构篇--Mysql 主从同步方案

文章目录 前言一、传统的主从复制&#xff1a;1 原理&#xff1a;2 缺点&#xff1a; 二、半同步复制&#xff08;Semi-Synchronous Replication&#xff09;&#xff1a;三、组复制&#xff1a;1 原理&#xff1a;2 实现&#xff1a;2.1 myql 实例安装&#xff1a;2.1 myql 实…