如何在Flutter中实现行列交叉的表格?求技术实现方案
Flutter 行列交叉表格实现方案
基础静态行列交叉表格
如果是数据量较小的静态场景,直接使用Flutter内置的Table组件即可实现行列交叉效果,核心是将第一行和第一列作为交叉标题。
示例代码:
class CrossTable extends StatelessWidget { // 模拟交叉数据:行标题、列标题、单元格内容 final List<String> rowTitles = ['行1', '行2', '行3', '行4']; final List<String> columnTitles = ['列1', '列2', '列3', '列4']; final List<List<String>> data = [ ['1-1', '1-2', '1-3', '1-4'], ['2-1', '2-2', '2-3', '2-4'], ['3-1', '3-2', '3-3', '3-4'], ['4-1', '4-2', '4-3', '4-4'], ]; @override Widget build(BuildContext context) { return SingleChildScrollView( scrollDirection: Axis.vertical, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Table( border: TableBorder.all(color: Colors.grey), children: [ // 首行:列标题(含左上角空白单元格) TableRow( decoration: BoxDecoration(color: Colors.grey[200]), children: [ Container(width: 80, height: 40, alignment: Alignment.center), ...columnTitles.map((title) => Container( width: 120, height: 40, alignment: Alignment.center, child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), )), ], ), // 数据行:行标题+内容单元格 ...rowTitles.asMap().entries.map((entry) { final rowIndex = entry.key; final rowTitle = entry.value; return TableRow( children: [ Container( width: 80, height: 40, alignment: Alignment.center, decoration: BoxDecoration(color: Colors.grey[200]), child: Text(rowTitle, style: const TextStyle(fontWeight: FontWeight.bold)), ), ...data[rowIndex].map((cell) => Container( width: 120, height: 40, alignment: Alignment.center, child: Text(cell), )), ], ); }), ], ), ), ); } }
带固定表头的行列交叉表格(滚动时表头锁定)
如果数据量较大,需要滚动时保持行标题和列标题固定,可通过Stack结合SingleChildScrollView实现,同时用滚动控制器同步横向/纵向滚动,保证表头与内容对齐。
示例代码:
class FixedCrossTable extends StatefulWidget { @override _FixedCrossTableState createState() => _FixedCrossTableState(); } class _FixedCrossTableState extends State<FixedCrossTable> { // 模拟大量数据 final List<String> rowTitles = List.generate(20, (index) => '行${index+1}'); final List<String> columnTitles = List.generate(15, (index) => '列${index+1}'); final List<List<String>> data = List.generate(20, (row) => List.generate(15, (col) => '${row+1}-${col+1}')); // 同步滚动控制器 final ScrollController _verticalController = ScrollController(); final ScrollController _horizontalController = ScrollController(); @override Widget build(BuildContext context) { return Stack( children: [ // 主内容区:可滚动的数据表格 SingleChildScrollView( controller: _verticalController, scrollDirection: Axis.vertical, child: SingleChildScrollView( controller: _horizontalController, scrollDirection: Axis.horizontal, child: Table( border: TableBorder.all(color: Colors.grey), children: [ // 占位首行(被顶部固定表头覆盖) TableRow( children: [ const SizedBox(width: 80, height: 40), ...columnTitles.map((_) => const SizedBox(width: 120, height: 40)), ], ), // 数据行(左侧被固定行标题覆盖) ...rowTitles.asMap().entries.map((entry) { final rowIndex = entry.key; return TableRow( children: [ const SizedBox(width: 80, height: 40), ...data[rowIndex].map((cell) => Container( width: 120, height: 40, alignment: Alignment.center, child: Text(cell), )), ], ); }), ], ), ), ), // 顶部固定列标题 SingleChildScrollView( controller: _horizontalController, scrollDirection: Axis.horizontal, child: Container( color: Colors.white, child: Table( border: TableBorder.all(color: Colors.grey), children: [ TableRow( decoration: BoxDecoration(color: Colors.grey[200]), children: [ const SizedBox(width: 80, height: 40), // 左上角空白 ...columnTitles.map((title) => Container( width: 120, height: 40, alignment: Alignment.center, child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), )), ], ), ], ), ), ), // 左侧固定行标题 SingleChildScrollView( controller: _verticalController, scrollDirection: Axis.vertical, child: Container( color: Colors.white, child: Table( border: TableBorder.all(color: Colors.grey), children: [ // 左上角空白单元格 TableRow( decoration: BoxDecoration(color: Colors.grey[200]), children: [const SizedBox(width: 80, height: 40)], ), // 行标题列表 ...rowTitles.map((title) => TableRow( decoration: BoxDecoration(color: Colors.grey[200]), children: [ Container( width: 80, height: 40, alignment: Alignment.center, child: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)), ), ], )), ], ), ), ), ], ); } }
自定义调整建议
- 可根据需求修改单元格的
width、height、背景色、字体样式,强化表头与内容的区分。 - 如果需要动态加载数据,只需将示例中的静态数据替换为接口返回的数据源即可。
内容的提问来源于stack exchange,提问作者Semih Kaya
相关产品推荐
相关产品推荐

