如何在Flutter中为two_dimensional_scrollables的列宽添加动画效果?
实现two_dimensional_scrollables列宽的平滑动画
针对你用two_dimensional_scrollables包实现点击列展开/收缩动画的需求,无需循环重建整个表格,通过增强状态管理+局部响应式更新就能高效实现,具体方案如下:
1. 增强TableSizeController的动画管理能力
在你的tableSizeControllerProvider对应的控制器类中,添加动画逻辑,为每个列单独维护动画状态,避免全局重建:
class TableSizeController extends ChangeNotifier { final Map<int, AnimationController> _animationControllers = {}; final Map<int, Animation<double>> _widthAnimations = {}; final Map<int, double> _baseWidths = {}; // 存储列的基准宽度 // 初始化列的初始宽度 void initColumnWidth(int index, double initialWidth) { _baseWidths[index] = initialWidth; } // 获取当前动画中的列宽(优先取动画值,无动画时取基准宽度) double getAnimatedCellWidth(int index) { return _widthAnimations[index]?.value ?? _baseWidths[index] ?? 100.0; } // 触发列宽动画:从当前宽度过渡到目标宽度 void animateColumnWidth(int index, double targetWidth, TickerProvider vsync) { // 清理旧的动画控制器,避免内存泄漏 _animationControllers[index]?.dispose(); final controller = AnimationController( duration: const Duration(milliseconds: 300), vsync: vsync, ); final tween = Tween<double>( begin: _baseWidths[index] ?? 100.0, end: targetWidth, ); // 绑定动画曲线,更新时通知状态变化 _widthAnimations[index] = tween.animate(CurvedAnimation( parent: controller, curve: Curves.easeInOut, ))..addListener(notifyListeners); _animationControllers[index] = controller; // 启动动画,结束后更新基准宽度并清理资源 controller.forward().then((_) { _baseWidths[index] = targetWidth; controller.dispose(); _animationControllers.remove(index); _widthAnimations.remove(index); }); } }
2. 修改ColumnBuilder实现局部响应式更新
在你的Widget中,修改列构建方法,通过Riverpod的select监听单列宽度变化,仅重建目标列的TableSpan,而非整个表格:
TableSpan _columnBuildSpan(int index) { // 仅监听当前列的动画宽度变化,触发局部重建 final columnWidth = ref.watch( tableSizeControllerProvider.select( (controller) => controller.getAnimatedCellWidth(index) ) ); const TableSpanDecoration decoration = TableSpanDecoration(); return TableSpan( backgroundDecoration: decoration, extent: FixedTableSpanExtent(columnWidth), cursor: SystemMouseCursors.click, // 点击触发动画:示例切换100/200宽度 onTap: () { final targetWidth = columnWidth == 200 ? 100 : 200; ref.read(tableSizeControllerProvider).animateColumnWidth( index, targetWidth, this, // 传入TickerProvider,需Widget混入对应Mixin ); }, ); }
3. 给Widget添加TickerProvider支持
动画控制器需要vsync绑定,给你的StatefulWidget混入SingleTickerProviderStateMixin(单动画场景)或TickerProviderStateMixin(多动画场景):
class YourTableWidget extends StatefulWidget { final TableDetails tableDetails; const YourTableWidget({super.key, required this.tableDetails}); @override State<YourTableWidget> createState() => _YourTableWidgetState(); } class _YourTableWidgetState extends State<YourTableWidget> with SingleTickerProviderStateMixin { // 你的现有build方法和其他逻辑 @override Widget build(BuildContext context) { // ... 原代码保持不变,仅修改columnBuilder部分 } }
核心优化点
- 局部更新:通过
select精准监听单列宽度变化,仅重建目标列的TableSpan,避免全表重建 - 资源回收:动画结束后自动销毁控制器,杜绝内存泄漏
- 平滑过渡:使用
CurvedAnimation实现自然的缓动效果,提升交互体验
内容的提问来源于stack exchange,提问作者Gabz95
相关产品推荐
相关产品推荐

