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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:05:55