如何实现Flutter DataTable表头固定、内容可滚动的效果?
Flutter原生实现固定表头、内容可滚动的DataTable(无需第三方包)
要实现类似NG-Zorro Table的固定表头效果,无需依赖第三方包,核心是将表头与表格内容拆分,让表头固定在顶部,内容区域单独滚动。以下是基于你现有代码的改造方案:
import 'package:flutter/material.dart'; class FixedHeaderDataTable extends StatelessWidget { const FixedHeaderDataTable({super.key}); // 统一管理列配置,确保表头与内容布局对齐 final List<Map<String, dynamic>> columnConfigs = [ { 'title': 'header 1', 'padding': const EdgeInsets.only(left: 20), }, { 'title': 'header 2', 'padding': EdgeInsets.zero, }, { 'title': 'header 3', 'padding': EdgeInsets.zero, }, { 'title': 'header 4', 'padding': EdgeInsets.zero, }, ]; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Card( margin: EdgeInsets.zero, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // 固定表头区域 Container( color: Colors.grey[50], child: Row( children: [ // 对应DataTable的复选框列 const Padding( padding: EdgeInsets.symmetric(horizontal: 6), child: SizedBox( width: 24, child: Checkbox(value: false, onChanged: null), ), ), // 遍历列配置生成表头项 ...columnConfigs.map((config) { return Expanded( flex: 1, child: Padding( padding: config['padding'] as EdgeInsets, child: Text( config['title'] as String, style: const TextStyle( fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500, ), ), ), ); }), ], ), ), // 可滚动的表格内容区域 Expanded( child: SingleChildScrollView( padding: EdgeInsets.zero, child: SizedBox( width: double.infinity, child: CheckboxTheme( data: CheckboxThemeData( side: MaterialStateBorderSide.resolveWith( (_) => const BorderSide(width: 1, color: Colors.blueGrey), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), fillColor: MaterialStateProperty.all(Colors.blueGrey[25]), checkColor: MaterialStateProperty.all(Colors.blueGrey[700]), ), child: DataTable( columnSpacing: 12, // 隐藏原生表头,避免重复 headingRowHeight: 0, dataRowHeight: 60, checkboxHorizontalMargin: 5, showCheckboxColumn: true, horizontalMargin: 6, // 保留列配置保证内容列与表头对齐 columns: columnConfigs.map((config) { return DataColumn( label: Padding( padding: config['padding'] as EdgeInsets, child: const SizedBox.shrink(), ), ); }).toList(), rows: [ for (int x = 0; x < 20; x++) DataRow( color: MaterialStateProperty.resolveWith((states) { if (states.contains(MaterialState.selected)) { return Colors.transparent; } return Colors.transparent; }), cells: [ DataCell( Padding( padding: const EdgeInsets.only(left: 20), child: Text( "data 1", style: TextStyle( fontSize: 14, color: Colors.grey[900], fontWeight: FontWeight.w500, ), ), ), ), DataCell( Text( "data 2", style: TextStyle( fontSize: 14, color: Colors.grey[900], fontWeight: FontWeight.w500, ), ), ), DataCell( Text( "data 3", style: TextStyle( fontSize: 14, color: Colors.grey[900], fontWeight: FontWeight.w500, ), ), ), DataCell( Text( "data 4", style: TextStyle( fontSize: 14, color: Colors.grey[900], fontWeight: FontWeight.w500, ), ), ), ], ) ], ), ), ), ), ), ], ), ), ); } }
关键改动说明
- 统一列配置:将列标题、内边距等属性抽成统一列表,确保表头和内容列的布局完全对齐,避免出现错位。
- 自定义固定表头:用
Container+Row模拟原生DataTable的表头样式,包含复选框列和所有数据列,固定在Card顶部。 - 隐藏原生表头:设置
DataTable的headingRowHeight: 0,彻底隐藏自带表头,避免重复渲染。 - 独立内容滚动:仅将表格的行数据区域包裹在
SingleChildScrollView中,实现内容纵向滚动时表头保持固定。
如果需要支持横向滚动,可在SingleChildScrollView中添加scrollDirection: Axis.horizontal,同时将表头也放入同一个横向滚动容器内,即可实现横向滚动时表头与内容同步移动。
内容的提问来源于stack exchange,提问作者Alfahad
相关产品推荐
相关产品推荐

