You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter如何实现支持左右滑动返回布尔值的滑动确认按钮

左右滑动状态返回按钮实现方案

你可以通过修改原有代码的回调逻辑、滑动限制实现双向滑动并返回布尔状态的需求:

核心修改点

  • 替换原有无返回值的回调类型为可传递布尔值的ValueChanged<bool>
  • 移除滑块滑到右侧后禁止滑动的限制,支持双向滑动切换
  • 滑动结束时根据滑块位置自动归位到对应端点,同时向外传递当前状态(右滑激活返回true,左滑复位返回false)

修改后的完整代码

import 'package:flutter/material.dart';

class SwipeOnOffButton extends StatefulWidget {
  final String? timer;
  final double? timerSize;
  final TextStyle? timerStyle;
  final double height;
  final double width;
  final Color? backgroundColor;
  final Color? disableBackgroundColor;
  final Color? replaceBackgroundColor;
  final Color? foregroundColor;
  final Color? iconColor;
  final IconData? leftIcon;
  final IconData? rightIcon;
  final double? iconSize;
  final String? text;
  final TextStyle? textStyle;
  // 替换原有VoidCallback为支持传递布尔值的回调
  required final ValueChanged<bool> onStatusChanged;
  final BorderRadius? foregroundRadius;
  final BorderRadius? backgroundRadius;
  final bool disable;
  // 可选:添加初始状态参数
  final bool initialValue;

  const SwipeOnOffButton({
    Key? key,
    this.timer,
    this.timerSize,
    this.timerStyle,
    this.height = 48.0,
    this.width = 300,
    this.backgroundColor,
    this.disableBackgroundColor,
    this.foregroundColor,
    this.iconColor,
    this.leftIcon,
    this.text,
    this.textStyle,
    required this.onStatusChanged,
    this.foregroundRadius,
    this.backgroundRadius,
    this.rightIcon,
    this.iconSize,
    this.replaceBackgroundColor,
    this.disable = false,
    this.initialValue = false,
  }) : super(key: key);

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

class SwipeOnOffButtonState extends State<SwipeOnOffButton> {
  double _position = 0;
  int _duration = 0;
  late bool _isSwipe;

  @override
  void initState() {
    super.initState();
    _isSwipe = widget.initialValue;
    // 初始状态如果为激活,滑块位置直接到最右侧
    _position = _isSwipe ? widget.width - widget.height : 0;
  }

  double getPosition() {
    if (widget.disable) _position = 0;
    if (_position < 0) {
      return 0;
    } else if (_position > widget.width - widget.height) {
      return widget.width - widget.height;
    } else {
      return _position;
    }
  }

  Color getColor() {
    if (!widget.disable) {
      if (_position > 0) {
        return widget.replaceBackgroundColor ?? Colors.green;
      } else {
        return widget.backgroundColor ?? Colors.orange;
      }
    } else {
      return widget.disableBackgroundColor ?? Colors.black;
    }
  }

  void updatePosition(details) {
    if (!widget.disable) {
      if (details is DragEndDetails) {
        setState(() {
          _duration = 100;
          // 滑动结束判断位置:超过一半宽度则到最右,否则回到最左
          if (_position > (widget.width - widget.height) / 2) {
            _position = widget.width - widget.height;
            _isSwipe = true;
          } else {
            _position = 0;
            _isSwipe = false;
          }
          // 向外传递当前状态
          widget.onStatusChanged(_isSwipe);
        });
      } else if (details is DragUpdateDetails) {
        setState(() {
          _duration = 0;
          // 移除原来的_isSwipe判断限制,支持双向滑动
          _position = details.localPosition.dx - (widget.height / 2);
        });
      }
    } else {
      _position = 0;
    }
  }

  void swipeReleased(details) {
    updatePosition(details);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: widget.height,
      width: widget.width,
      decoration: BoxDecoration(
        borderRadius: widget.backgroundRadius ??
            BorderRadius.all(Radius.circular(widget.height)),
        color: !widget.disable
            ? widget.backgroundColor ?? Colors.orange
            : Colors.black,
      ),
      child: Stack(
        children: <Widget>[
          AnimatedContainer(
            height: widget.height,
            width: getPosition() + widget.height,
            duration: Duration(milliseconds: _duration),
            curve: Curves.bounceOut,
            decoration: BoxDecoration(
                borderRadius: widget.backgroundRadius ??
                    BorderRadius.all(Radius.circular(widget.height)),
                color: getColor()),
          ),
          Center(
            child: Text(
              widget.text ?? "Start",
              style: widget.textStyle ??
                  Theme.of(context).textTheme.labelLarge?.copyWith(color: Colors.white) ??
                  TextStyle(color: Colors.white),
            ),
          ),
          AnimatedPositioned(
            duration: Duration(milliseconds: _duration),
            curve: Curves.bounceOut,
            left: getPosition(),
            child: GestureDetector(
              onPanUpdate: (details) => updatePosition(details),
              onPanEnd: (details) => swipeReleased(details),
              child: !_isSwipe
                  ? Container(
                      height: widget.height,
                      width: widget.height,
                      decoration: BoxDecoration(
                        borderRadius: widget.foregroundRadius ??
                            BorderRadius.all(
                              Radius.circular(widget.height / 2),
                            ),
                        color: widget.foregroundColor ??
                            Colors.white.withOpacity(0.24),
                      ),
                      child: Icon(
                        widget.leftIcon ?? Icons.arrow_circle_right_rounded,
                        color: widget.iconColor ?? Colors.white,
                        size: widget.iconSize ?? 20.0,
                      ),
                    )
                  : Container(
                      height: widget.height,
                      width: widget.height,
                      decoration: BoxDecoration(
                        borderRadius: widget.foregroundRadius ??
                            BorderRadius.all(
                              Radius.circular(widget.height / 2),
                            ),
                        color: widget.foregroundColor ??
                            Colors.white.withOpacity(0.24),
                      ),
                      child: Icon(
                        widget.rightIcon ?? Icons.check_circle_rounded,
                        color: widget.iconColor ?? Colors.white,
                        size: widget.iconSize ?? 20.0,
                      ),
                    ),
            ),
          ),
        ],
      ),
    );
  }
}

使用示例

SwipeOnOffButton(
  width: 250,
  height: 50,
  text: "滑动解锁",
  backgroundColor: Colors.grey[300],
  replaceBackgroundColor: Colors.greenAccent,
  onStatusChanged: (bool status) {
    // 右滑完成返回true,左滑复位返回false
    debugPrint("当前滑动状态:$status");
    // 这里可以写对应状态的业务逻辑
  },
)

内容的提问来源于stack exchange,提问作者Afdal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:45:03