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

这篇具有很好参考价值的文章主要介绍了flutter开发实战-自定义Switch开关控件Widget。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、效果图

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

二、实现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控件样式。

学习记录,每天不停进步。文章来源地址https://www.toymoban.com/news/detail-539106.html

到了这里,关于flutter开发实战-自定义Switch开关控件Widget的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • flutter开发实战-Camera自定义相机拍照功能实现

    flutter开发实战-Camera自定义相机拍照功能实现 在项目中使用image_picker插件时候,在android设备上使用无法默认设置前置摄像头(暂时不清楚什么原因),由于项目默认需要使用前置摄像头,所以最终采用自定义相机实现拍照功能。 在工程的iOS的info.plist文件中添加相机、麦克风

    2024年02月21日
    浏览(37)
  • Android 之开关按钮 ToggleButton 和开关 Switch

    本节给大家介绍的Android基本UI控件是:开关按钮ToggleButton和开关Switch,可能大家对着两个组件 并不熟悉,突然想起笔者的第一间外包公司,是否在wifi下联网的开关,竟然用的TextView,然后叫美工 且两个切换前后的图,然后代码中进行设置,当然点击TextView的时候判断状态,然

    2024年02月07日
    浏览(34)
  • element——switch接口成功后赋值打开开关

    应用场景 **点击switch,出弹窗,点击弹窗保存按钮调接口成功后再赋值(row.orderButtonValue=“1”)打开switch开的状态变颜色。 在vue 中使用 :value动态的绑定值。 (在Vue中,冒号(:)被称为v-bind指令的缩写。v-bind指令是Vue中非常强大的一个指令,它用于动态绑定数据到HTML标签的属

    2024年02月11日
    浏览(25)
  • 微信小程序switch开关组件修改样式(大小,颜色)

    以上尺寸根据你的具体情况来调整

    2024年02月11日
    浏览(39)
  • OpenHarmony开发实战:switch、chart组件的使用(JS)

    本篇文章基于switch组件和chart组件,实现线形图、占比图、柱状图,并通过switch切换chart组件数据的动静态显示。要求实现以下功能: 实现静态数据可视化图表。 打开开关,实现静态图切换为动态可视化图表。 相关概念 switch组件:开关选择器,通过开关,开启或关闭某个功

    2024年04月11日
    浏览(60)
  • Flutter实战-自定义键盘(一)

    用了两年的flutter,有了一些心得,从今天开始陆续更新一些案例,不虚头巴脑,只求实战有用,以供学习或使用flutter的小伙伴参考,学习尚浅,如有不正确的地方还望各路大神指正,以免误人子弟,在此拜谢~(原创不易,转发请标注来源和作者) 注意:无特殊说明,flutt

    2024年02月09日
    浏览(22)
  • Android应用开发-Flutter的LongPressDraggable控件回调函数onDraggableCanceled使用

    以下是如何使用 onDraggableCanceled 的示例: velocity 参数表示拖动被取消时的速度信息。 offset 参数表示拖动被取消时的偏移量信息。 这个回调通常用于在拖动被取消时执行一些清理工作或展示一些反馈。例如,你可能想要将拖动对象返回到原始位置,或者显示一个提示,告诉用

    2024年03月08日
    浏览(29)
  • Android开发之自定义控件-组合控件的开发与实现

    最终实现的效果展示图:   类似支付宝微信,底部分隔线对齐标题效果:       完整渲染显示效果(包含三个条目右边不同颜色的文字): 立体效果:  隐藏资产总额条目右边更多箭头  隐藏中国历史条目右边的文字: 隐藏中国历史条目下边的分隔线: 隐藏条目2中国历史左

    2024年02月10日
    浏览(28)
  • lvgl 笔记 按钮部件 (lv_btn) 和 开关部件 (lv_switch)

    lv_btn 和 lb_obj 使用方法一样,只是外表并不相同,基础创建方法只需一行代码。 在 lv_obj_set_style_bg_color() 配置为, LV_STATE_PRESSED 即可配置为按下改变颜色。 其中和 lv_obj 一样,共有以下可供选择的参数: lv_obj_add_event_cb(); 可以添加事件。 开关部件共有三个部分可以单独配置颜

    2024年01月21日
    浏览(36)
  • Android开发基础——自定义控件

    Android中常用控件和布局的继承结构如下图所示:  从上面可以看出,所有控件都是直接或间接继承自View的,所用的所有布局都是直接或间接继承自ViewGroup的。View是Android中最基本的一种UI组件,其可以在屏幕上绘制一块矩形区域,并能够响应这块区域的各种事件,因此,用户

    2023年04月10日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包