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

Flutter如何实现支持左右双向滑动的可滑动卡片堆叠效果

嘿,这个双向滑动的卡片堆叠效果在Flutter里实现起来并不复杂,我给你分享两种实用方案,你可以根据自己的需求选:

方案一:用第三方库快速落地

如果不想花太多时间写自定义逻辑,推荐用flutter_card_swiper这个专门做卡片滑动堆叠的库,它原生支持左右双向滑动,还自带了堆叠动画效果。

步骤1:添加依赖

在你的pubspec.yaml文件里加上这个依赖:

dependencies:
  flutter_card_swiper: ^1.0.1 # 记得替换成最新版本

然后执行flutter pub get拉取包。

步骤2:编写核心代码

这里给你一个简单的示例,直接实现可左右滑的卡片堆叠:

import 'package:flutter/material.dart';
import 'package:flutter_card_swiper/flutter_card_swiper.dart';

class SwipeCardStack extends StatefulWidget {
  const SwipeCardStack({super.key});

  @override
  State<SwipeCardStack> createState() => _SwipeCardStackState();
}

class _SwipeCardStackState extends State<SwipeCardStack> {
  final List<Widget> _cards = [
    _buildCard('旅行卡片1', Colors.orangeAccent),
    _buildCard('美食卡片2', Colors.greenAccent),
    _buildCard('电影卡片3', Colors.blueAccent),
    _buildCard('读书卡片4', Colors.purpleAccent),
  ];

  static Widget _buildCard(String text, Color color) {
    return Container(
      width: 320,
      height: 450,
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(20),
        boxShadow: const [
          BoxShadow(blurRadius: 10, color: Colors.black26, offset: Offset(0, 6))
        ],
      ),
      child: Center(
        child: Text(text, style: const TextStyle(fontSize: 28, color: Colors.white, fontWeight: FontWeight.bold)),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CardSwiper(
          cards: _cards,
          onSwipeLeft: (index) => print('向左滑走了「${_cards[index].toString()}」'),
          onSwipeRight: (index) => print('向右滑走了「${_cards[index].toString()}」'),
          onEnd: () => ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content: Text('所有卡片都滑完啦~'))
          ),
        ),
      ),
    );
  }
}

这个示例里,你只需要传入卡片列表,就能自动获得堆叠布局和双向滑动功能,还能监听滑动方向和全部滑完的事件。

方案二:自定义实现(高度可控)

如果需要更个性化的动画或者交互逻辑——比如自定义堆叠偏移量、滑动时的旋转角度,那可以自己用GestureDetector+Stack来实现。

核心思路

  1. 用Stack组件堆叠卡片,通过Transform让后面的卡片稍微缩小并偏移,形成堆叠层次感;
  2. 用GestureDetector监听拖拽事件,实时更新当前顶部卡片的位置和旋转角度;
  3. 拖拽结束时判断滑动距离,超过阈值就移除顶部卡片,否则回弹到原位。

核心代码示例

import 'package:flutter/material.dart';

class CustomSwipeCardStack extends StatefulWidget {
  const CustomSwipeCardStack({super.key});

  @override
  State<CustomSwipeCardStack> createState() => _CustomSwipeCardStackState();
}

class _CustomSwipeCardStackState extends State<CustomSwipeCardStack> with SingleTickerProviderStateMixin {
  late AnimationController _reboundController;
  List<Color> _cardColors = [Colors.orangeAccent, Colors.greenAccent, Colors.blueAccent, Colors.purpleAccent];
  double _offsetX = 0;
  double _rotation = 0;
  final double _swipeThreshold = 120; // 滑动触发移除的阈值

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

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

  void _onPanUpdate(DragUpdateDetails details) {
    setState(() {
      _offsetX += details.delta.dx;
      // 根据水平偏移量计算旋转角度,模拟自然拖拽的倾斜效果
      _rotation = _offsetX / 120;
    });
  }

  void _onPanEnd(DragEndDetails details) {
    if (_offsetX.abs() > _swipeThreshold) {
      // 滑动超过阈值,移除当前顶部卡片
      setState(() {
        _cardColors.removeAt(0);
        _offsetX = 0;
        _rotation = 0;
      });
    } else {
      // 回弹到初始位置
      _reboundController.reset();
      _reboundController.forward().then((_) {
        setState(() {
          _offsetX = 0;
          _rotation = 0;
        });
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Stack(
          alignment: Alignment.center,
          children: List.generate(_cardColors.length, (index) {
            // 后面的卡片缩小并向下偏移,营造堆叠层次感
            final scale = 1 - (index * 0.06);
            final offsetY = index * 12.0;
            return index == 0
                ? GestureDetector(
                    onPanUpdate: _onPanUpdate,
                    onPanEnd: _onPanEnd,
                    child: Transform(
                      transform: Matrix4.translationValues(_offsetX, 0, 0)
                        ..rotateZ(_rotation)
                        ..scale(scale),
                      child: _buildCard(_cardColors[index]),
                    ),
                  )
                : Transform(
                    transform: Matrix4.translationValues(0, offsetY, 0)
                      ..scale(scale),
                    child: _buildCard(_cardColors[index]),
                  );
          }).reversed.toList(), // 反转列表,确保第一个卡片在最上层
        ),
      ),
    );
  }

  Widget _buildCard(Color color) {
    return Container(
      width: 320,
      height: 450,
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(20),
        boxShadow: const [
          BoxShadow(blurRadius: 10, color: Colors.black26, offset: Offset(0, 6))
        ],
      ),
    );
  }
}

这个自定义实现里,你可以调整_swipeThreshold(滑动阈值)、scale(卡片缩放比例)、offsetY(堆叠偏移量)来适配你的设计需求,滑动时的旋转效果也可以根据自己的喜好调整参数。

额外优化点

  • 可以给卡片添加滑出动画,比如当卡片被滑走时,让它快速滑出屏幕并消失;
  • 支持双击卡片、长按卡片等额外交互;
  • 动态加载更多卡片,避免一次性加载太多导致性能问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:02:31