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

Flutter_card_swiper卡片滑动时如何重置扩展容器状态?

解决方案:切换卡片时保持容器收缩状态

当前问题源于卡片的展开状态由自身内部管理,切换卡片时无法主动重置状态,导致新卡片出现不必要的展开动画。通过状态提升将展开状态移到父组件管理,即可在卡片切换时主动重置状态,实现“切换后保持收缩,仅用户主动操作才展开”的需求。

步骤1:父组件添加展开状态管理

在_SwipeCardsWidgetState中新增状态映射表,记录每个卡片的展开状态,初始化时全部设为收缩:

class _SwipeCardsWidgetState extends State<SwipeCardsWidget> {
  final CardSwiperController controller = CardSwiperController();
  // 记录每个卡片的展开状态,初始全为收缩
  final Map<int, bool> _cardExpansionStates = {};

  @override
  void initState() {
    super.initState();
    // 初始化所有卡片为收缩状态
    for (int i = 0; i < candidates.length; i++) {
      _cardExpansionStates[i] = false;
    }
  }

  // ... 原有代码
}

步骤2:修改卡片切换回调,重置展开状态

在onSwipe和onUndo回调中,将新显示的卡片状态强制重置为收缩:

bool _onSwipe(
  int previousIndex,
  int? currentIndex,
  CardSwiperDirection direction,
) {
  debugPrint(
    'The card $previousIndex was swiped to the ${direction.name}. Now the card $currentIndex is on top',
  );
  // 重置当前显示的卡片为收缩状态
  if (currentIndex != null) {
    setState(() {
      _cardExpansionStates[currentIndex] = false;
    });
  }
  return true;
}

bool _onUndo(
  int? previousIndex,
  int currentIndex,
  CardSwiperDirection direction,
) {
  debugPrint(
    'The card $currentIndex was undod from the ${direction.name}',
  );
  // 重置当前显示的卡片为收缩状态
  setState(() {
    _cardExpansionStates[currentIndex] = false;
  });
  return true;
}

步骤3:重构ExampleCard为无状态组件

将ExampleCard改为StatelessWidget,从父组件接收展开状态和状态变更回调:

class ExampleCard extends StatelessWidget {
  final Person candidate;
  final bool isExpanded;
  final void Function(bool) onExpandedChanged;

  const ExampleCard({
    super.key,
    required this.candidate,
    required this.isExpanded,
    required this.onExpandedChanged,
  });

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onVerticalDragUpdate: (details) {
        if (details.delta.dy < 0) {
          onExpandedChanged(true);
        }
        if (details.delta.dy > 0) {
          onExpandedChanged(false);
        }
      },
      child: Container(
        clipBehavior: Clip.hardEdge,
        decoration: BoxDecoration(
          borderRadius: const BorderRadius.all(Radius.circular(10)),
          color: Colors.white,
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.2),
              spreadRadius: 3,
              blurRadius: 7,
              offset: const Offset(0, 3),
            ),
          ],
        ),
        alignment: Alignment.center,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Flexible(
              child: Container(
                decoration: BoxDecoration(
                  image: DecorationImage(
                      image: NetworkImage(candidate.imageUrl), fit: BoxFit.cover),
                  gradient: const LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [Color(0xFFD7E9F7), Color(0xFFD7E9F7)],
                  ),
                ),
              ),
            ),
            AnimatedContainer(
              duration: const Duration(milliseconds: 300),
              curve: Curves.easeInOut,
              height: isExpanded ? MediaQuery.of(context).size.height * 0.30 : MediaQuery.of(context).size.height * 0.15,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Center(
                      child: CustomPaint(
                        painter: MyLinePainter(),
                        size: const Size(40, 3),
                      ),
                    ),
                    Text(
                      '${candidate.name}, ${candidate.age}',
                      style: const TextStyle(
                        color: Colors.black,
                        fontWeight: FontWeight.bold,
                        fontSize: 20,
                      ),
                    ),
                    const SizedBox(height: 5),
                    Text(
                      'Preferred drink: ${candidate.preferredDrink.name}',
                      style: const TextStyle(
                        color: Colors.grey,
                        fontSize: 15,
                      ),
                    ),
                    const SizedBox(height: 5),
                    Text(
                      'City: ${candidate.location}',
                      style: const TextStyle(color: Colors.grey),
                    ),
                    const SizedBox(height: 10),
                    if (isExpanded)
                      Expanded(
                        child: Text(
                          'About me: ${candidate.aboutMe}',
                          style: const TextStyle(color: Colors.grey, fontSize: 15),
                        ),
                      ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

步骤4:修改CardSwiper的卡片构建逻辑

更新cardBuilder,直接基于candidates创建卡片并传递状态和回调:

Flexible(
  child: CardSwiper(
    allowedSwipeDirection: const AllowedSwipeDirection.only(left: true, right: true),
    controller: controller,
    cardsCount: candidates.length,
    onSwipe: _onSwipe,
    onUndo: _onUndo,
    numberOfCardsDisplayed: 3,
    backCardOffset: const Offset(40, 40),
    padding: const EdgeInsets.all(24.0),
    cardBuilder: (
      context,
      index,
      horizontalThresholdPercentage,
      verticalThresholdPercentage,
    ) =>
        ExampleCard(
          candidate: candidates[index],
          isExpanded: _cardExpansionStates[index]!,
          onExpandedChanged: (value) {
            setState(() {
              _cardExpansionStates[index] = value;
            });
          },
        ),
  ),
),

步骤5:清理无用代码

删除_SwipeCardsWidgetState中的final cards = candidates.map(ExampleCard.new).toList();,现在卡片直接在cardBuilder中创建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 14:25:54