Flutter自定义彩虹式弧形水平滚动ListView开发需求
Flutter 实现弧形彩虹式水平滚动 ListView
要实现选中元素平直、相邻元素呈弧形排列的水平滚动列表,可以通过滚动监听+动态变换的方式实现,核心思路是根据每个item与屏幕中心的偏移量,为其应用不同的旋转变换,模拟彩虹弧形效果。
完整实现代码
import 'package:flutter/material.dart'; class CurvedRainbowListView extends StatefulWidget { final List<String> items; const CurvedRainbowListView({super.key, required this.items}); @override State<CurvedRainbowListView> createState() => _CurvedRainbowListViewState(); } class _CurvedRainbowListViewState extends State<CurvedRainbowListView> { final ScrollController _scrollController = ScrollController(); double _currentOffset = 0.0; final double _itemWidth = 120.0; // 单个item宽度 final double _curveRadius = 200.0; // 弧形半径,值越小弯曲越明显 @override void initState() { super.initState(); _scrollController.addListener(() { setState(() => _currentOffset = _scrollController.offset); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } // 计算item的变换矩阵 Matrix4 _getItemTransform(int index) { // 计算item中心与屏幕中心的偏移距离 final itemCenter = index * _itemWidth + _itemWidth / 2; final screenCenter = _currentOffset + MediaQuery.of(context).size.width / 2; final offsetFromCenter = itemCenter - screenCenter; // 处于屏幕中心的item(选中态)保持无变换 if (offsetFromCenter.abs() < _itemWidth / 2) { return Matrix4.identity(); } // 根据偏移距离计算弯曲角度和位移 final normalizedOffset = offsetFromCenter / _curveRadius; final bendAngle = normalizedOffset.abs() * 0.3; // 控制弯曲角度(弧度) if (offsetFromCenter > 0) { // 右侧item:向下弯曲 return Matrix4.rotationX(bendAngle)..translate(0, normalizedOffset * 20, 0); } else { // 左侧item:向上弯曲 return Matrix4.rotationX(-bendAngle)..translate(0, normalizedOffset * 20, 0); } } @override Widget build(BuildContext context) { return SizedBox( height: 200, // 容器高度需足够容纳弧形item,避免截断 child: ListView.builder( controller: _scrollController, scrollDirection: Axis.horizontal, // 左右padding让第一个/最后一个item能滚动到屏幕中心 padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width / 2 - _itemWidth / 2), itemCount: widget.items.length, itemBuilder: (context, index) { return Transform( transform: _getItemTransform(index), origin: Offset(_itemWidth / 2, 100), // 变换原点,控制弧形的旋转中心 child: Container( width: _itemWidth, margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( color: Colors.primaries[index % Colors.primaries.length], borderRadius: BorderRadius.circular(12), ), child: Center( child: Text( widget.items[index], style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), ); }, ), ); } } // 使用示例 void main() { runApp(const MaterialApp( home: Scaffold( body: Center( child: CurvedRainbowListView( items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7', 'Item 8'], ), ), ), )); }
关键实现细节
- 滚动监听:通过
ScrollController实时获取滚动偏移量,触发UI更新 - 动态变换:
_getItemTransform方法根据item与屏幕中心的距离,为左右两侧item应用不同方向的旋转变换,模拟彩虹弧形 - 变换原点:
Transform的origin参数设置为item底部中心,让item围绕该点旋转,更贴合彩虹的弧形效果 - 参数调整:修改
_curveRadius(弧形半径)、bendAngle(弯曲角度)可以调整弧形的弯曲程度,修改_itemWidth可以适配不同大小的item
注意事项
- 容器高度需要设置足够大,避免弧形item被截断
- 若需要更顺滑的滚动体验,可以添加自定义
ScrollPhysics - 可根据需求扩展item的样式,比如添加选中态高亮、阴影等效果
内容的提问来源于stack exchange,提问作者ADARSH K VINOD
相关产品推荐
相关产品推荐

