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

如何在Flutter中创建类似塔罗牌的环形卡片轮UI

Flutter环形卡片轮UI实现方案

核心思路

要实现这种环形滚动的卡片轮,关键是让滚动时卡片根据位置动态调整缩放比例、透明度和Y轴偏移,模拟环形的视觉弧度。ListWheelScrollView默认是线性滚轮,很难实现这种环形效果,推荐以下两种实现方式:


方案一:纯原生自定义滚动布局

通过GestureDetector监听滚动手势,结合AnimatedBuilder和Transform控制每个卡片的视觉变换,核心逻辑是根据卡片索引与当前滚动偏移量的关系,计算每个卡片的动态参数。

代码示例

import 'package:flutter/material.dart';

class CircularCardWheel extends StatefulWidget {
  final List<Widget> cards;
  final double cardHeight;
  final double cardWidth;

  const CircularCardWheel({
    super.key,
    required this.cards,
    this.cardHeight = 200,
    this.cardWidth = 150,
  });

  @override
  State<CircularCardWheel> createState() => _CircularCardWheelState();
}

class _CircularCardWheelState extends State<CircularCardWheel> {
  final _scrollController = ScrollController();
  double _currentOffset = 0.0;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {
      setState(() => _currentOffset = _scrollController.offset);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    return SizedBox(
      height: widget.cardHeight * 1.5,
      child: ListView.builder(
        controller: _scrollController,
        scrollDirection: Axis.horizontal,
        itemCount: widget.cards.length,
        padding: EdgeInsets.symmetric(horizontal: screenWidth / 2 - widget.cardWidth / 2),
        itemBuilder: (context, index) {
          // 计算卡片中心与屏幕中心的距离
          final cardCenterOffset = index * widget.cardWidth + widget.cardWidth / 2;
          final distanceFromCenter = (cardCenterOffset - _currentOffset - screenWidth / 2).abs();

          // 根据距离动态调整视觉参数
          final scale = 1 - (distanceFromCenter / (screenWidth / 2)) * 0.3;
          final opacity = 1 - (distanceFromCenter / (screenWidth / 2)) * 0.5;
          final yOffset = -distanceFromCenter * 0.1;

          return Transform(
            transform: Matrix4.identity()
              ..translate(0, yOffset)
              ..scale(scale),
            alignment: Alignment.center,
            child: Opacity(
              opacity: opacity.clamp(0.5, 1.0),
              child: SizedBox(
                height: widget.cardHeight,
                width: widget.cardWidth,
                child: widget.cards[index],
              ),
            ),
          );
        },
      ),
    );
  }
}

// 使用示例
class DemoPage extends StatelessWidget {
  const DemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    final cards = List.generate(
      10,
      (index) => Card(
        elevation: 8,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
        child: Center(child: Text('卡片 $index', style: const TextStyle(fontSize: 20))),
      ),
    );

    return Scaffold(
      appBar: AppBar(title: const Text('环形卡片轮')),
      body: Center(child: CircularCardWheel(cards: cards)),
    );
  }
}

说明

  • 通过滚动偏移量计算卡片与屏幕中心的距离,动态调整缩放、透明度和Y轴偏移,模拟环形弧度
  • 横向ListView配合左右padding,让卡片滚动时始终以屏幕中心为视觉焦点
  • 可根据需求调整scale、opacity、yOffset的系数,优化环形效果的强弱

方案二:使用第三方包快速实现

如果不想手动处理复杂的变换逻辑,可以借助成熟的第三方包:

  • carousel_slider:支持自定义Transformer,快速实现卡片的环形排列
  • flutter_carousel_widget:提供预设的环形滚动配置

以carousel_slider为例,自定义环形Transformer:

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

class CircularTransformer extends CarouselTransformer {
  @override
  Widget transformItem(Widget child, int index, int pageIndex) {
    final offset = (index - pageIndex).abs();
    final scale = 1 - offset * 0.2;
    final opacity = 1 - offset * 0.3;
    final yOffset = -offset * 20;

    return Transform(
      transform: Matrix4.identity()
        ..translate(0, yOffset)
        ..scale(scale),
      alignment: Alignment.center,
      child: Opacity(opacity: opacity.clamp(0.5, 1.0), child: child),
    );
  }
}

// 使用示例
class CarouselDemo extends StatelessWidget {
  const CarouselDemo({super.key});

  @override
  Widget build(BuildContext context) {
    final items = List.generate(10, (index) => Card(
      elevation: 8,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Center(child: Text('卡片 $index', style: const TextStyle(fontSize: 20))),
    ));

    return Scaffold(
      appBar: AppBar(title: const Text('环形卡片轮')),
      body: Center(
        child: CarouselSlider(
          items: items,
          options: CarouselOptions(
            height: 300,
            viewportFraction: 0.6,
            enableInfiniteScroll: true,
            transform: CircularTransformer(),
          ),
        ),
      ),
    );
  }
}

优化建议

  • 给滚动控制器添加阻尼效果,让滚动更顺滑
  • 给中间选中的卡片增加额外阴影或边框,强化选中状态
  • 修改轴方向和计算逻辑,可实现垂直方向的环形滚动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:27:33