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

如何为单个Dismissible组件实现多组onDismissed触发逻辑?

Flutter 实现分段拖动触发不同操作的 Dismissible 效果

完全可以实现这种根据拖动距离触发不同操作的需求,原生Dismissible组件仅支持达到固定阈值后触发单一操作,因此我们可以通过GestureDetector结合动画控制来实现自定义逻辑,以下是具体方案:

核心思路

  1. 获取屏幕宽度,计算25%的阈值作为分界点
  2. 用GestureDetector监听拖动过程中的偏移量
  3. 拖动结束时,根据偏移量与阈值的对比执行对应操作
  4. 可选:配合动画控制器实现平滑的滑动、回弹或滑出效果

基础实现代码

import 'package:flutter/material.dart';

class CustomDismissible extends StatefulWidget {
  final Widget child;
  final VoidCallback onShortDrag;
  final VoidCallback onLongDrag;

  const CustomDismissible({
    super.key,
    required this.child,
    required this.onShortDrag,
    required this.onLongDrag,
  });

  @override
  State<CustomDismissible> createState() => _CustomDismissibleState();
}

class _CustomDismissibleState extends State<CustomDismissible> {
  double _dragOffset = 0;

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final threshold = screenWidth * 0.25;

    return GestureDetector(
      onPanUpdate: (details) {
        // 以向左拖动为例,可根据需求改为向右
        setState(() {
          _dragOffset += details.delta.dx;
          // 限制拖动范围,避免过度偏移
          _dragOffset = _dragOffset.clamp(-screenWidth, 0.0);
        });
      },
      onPanEnd: (details) {
        if (_dragOffset.abs() < threshold) {
          // 拖动距离小于屏幕25%,触发短距离操作
          widget.onShortDrag();
        } else {
          // 拖动距离超过屏幕25%,触发长距离操作
          widget.onLongDrag();
        }
        // 重置偏移量
        setState(() => _dragOffset = 0);
      },
      child: Transform.translate(
        offset: Offset(_dragOffset, 0),
        child: widget.child,
      ),
    );
  }
}

带分段背景的增强实现

如果需要像示例图那样显示红色(短距离区域)和灰色(长距离区域)的背景,可以用Stack结合背景容器实现:

import 'package:flutter/material.dart';

class CustomDismissibleWithBackground extends StatefulWidget {
  final Widget child;
  final VoidCallback onShortDrag;
  final VoidCallback onLongDrag;

  const CustomDismissibleWithBackground({
    super.key,
    required this.child,
    required this.onShortDrag,
    required this.onLongDrag,
  });

  @override
  State<CustomDismissibleWithBackground> createState() => _CustomDismissibleWithBackgroundState();
}

class _CustomDismissibleWithBackgroundState extends State<CustomDismissibleWithBackground> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _dragOffset = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 200),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final threshold = screenWidth * 0.25;

    return Stack(
      children: [
        // 分段背景:左侧25%红色,右侧75%灰色
        Positioned.fill(
          child: Row(
            children: [
              Expanded(flex: 25, child: Container(color: Colors.red)),
              Expanded(flex: 75, child: Container(color: Colors.grey)),
            ],
          ),
        ),
        GestureDetector(
          onPanUpdate: (details) {
            setState(() {
              _dragOffset += details.delta.dx;
              _dragOffset = _dragOffset.clamp(-screenWidth, 0.0);
            });
          },
          onPanEnd: (details) {
            if (_dragOffset.abs() < threshold) {
              // 回弹动画+触发短距离操作
              _controller.animateTo(0).then((_) {
                setState(() => _dragOffset = 0);
                widget.onShortDrag();
              });
            } else {
              // 滑出动画+触发长距离操作
              _controller.animateTo(-screenWidth).then((_) {
                setState(() => _dragOffset = 0);
                widget.onLongDrag();
              });
            }
          },
          child: AnimatedBuilder(
            animation: _controller,
            builder: (context, child) {
              return Transform.translate(
                offset: Offset(_dragOffset, 0),
                child: child,
              );
            },
            child: widget.child,
          ),
        ),
      ],
    );
  }
}

使用方式

在页面中直接调用自定义组件即可:

CustomDismissibleWithBackground(
  child: Container(
    height: 80,
    color: Colors.white,
    alignment: Alignment.center,
    child: const Text('拖动测试'),
  ),
  onShortDrag: () {
    print('触发短距离拖动操作');
  },
  onLongDrag: () {
    print('触发长距离拖动操作');
  },
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:25:30