Flutter DataTable多行选择实现及重复行异常问题解决
问题分析与解决方案
问题根源
- 全局选中状态导致批量异常:原代码用单个
bool checked变量控制所有行的选中状态,点击任意一行时,所有行的选中状态会同步切换,出现自动选中多行的异常。 - 表头与数据不匹配:表头中第四项是“SUPPLIER”,但对应单元格显示的是
productId数据,导致内容错位。 - 无用变量冗余:State中定义的
selectedIndex和itemListData未实际发挥作用,造成代码冗余。
修复后的完整代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import '../defaults/defaults.dart'; import 'items/additem.dart'; class SearchItem extends StatefulWidget { const SearchItem({Key? key}) : super(key: key); @override State<SearchItem> createState() => _SearchItemState(); } class _SearchItemState extends State<SearchItem> { // 存储选中的文档ID,实现独立的多行选择 final Set<String> selectedItemIds = {}; List<DataCell> _createCellsForElement(DocumentSnapshot document) { Map<String, dynamic> data = document.data() as Map<String, dynamic>; return <DataCell>[ DataCell(Text(data['itemid'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['isbn'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['itemName'].toString(), style: Defaults.drawerTextBlacknormal)), // 修正表头与数据的对应关系,优先显示supplier字段,不存在则保留productId DataCell(Text(data['supplier']?.toString() ?? data['productId'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['category'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['costPrice'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['quantity'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['sellingPrice'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['amount'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(Text(data['promoEndDt'].toString(), style: Defaults.drawerTextBlacknormal)), DataCell(IconButton(onPressed: () {}, icon: const Icon(Icons.delete))), ]; } // 表头列表,修正顺序与数据对应 final columns = [ 'ID', 'UPC/EAN/ISBN', 'ITEM NAME', 'SUPPLIER', 'CATEGORY', 'COST PRICE', 'QUANTITY', 'SELLING PRICE', 'AMOUNT', 'EXPIRY DATE', 'DELETE' ]; List<DataColumn> getcolumns(List<String> columns) => columns .map((String column) => DataColumn( label: Text(column, style: Defaults.drawerTextBlacknormal))).toList(); @override Widget build(BuildContext context) { return Align( alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Text('Item List', style: Defaults.drawerTextStyleHeadBlack), Container( margin: const EdgeInsets.fromLTRB(20, 30, 20, 0), child: StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection('ItemsCollection').snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return const Text('Something went Wrong!'); } else if (snapshot.hasData) { final itemListData = snapshot.data!.docs.map((doc) { return DataRow( cells: _createCellsForElement(doc), // 判断当前文档是否被选中 selected: selectedItemIds.contains(doc.id), onSelectChanged: (bool? isSelected) { setState(() { if (isSelected == true) { selectedItemIds.add(doc.id); } else { selectedItemIds.remove(doc.id); } }); } ); }).toList(); return SingleChildScrollView( scrollDirection: Axis.horizontal, clipBehavior: Clip.hardEdge, child: InteractiveViewer( alignPanAxis: true, child: DataTable( columnSpacing: 20, sortAscending: true, columns: getcolumns(columns), rows: itemListData, ), ), ); } else { return const Center(child: CircularProgressIndicator()); } }, ), ), ], ), ); } }
关键修改说明
- 多行选择实现:用
Set<String> selectedItemIds存储选中的文档ID,每个行的选中状态独立,点击行时仅更新当前行对应的ID在集合中的存在状态。 - 修正数据与表头匹配:调整第四列的单元格数据,优先显示
supplier字段(如果Firestore文档中有该字段),否则保留原productId,确保与表头“SUPPLIER”对应。 - 简化行列表构建:用
map替代循环构建行列表,代码更简洁,避免冗余的索引变量。 - 移除无用变量:删除State中未使用的
selectedIndex和itemListData,减少代码冗余。
内容的提问来源于stack exchange,提问作者haruna auwal
相关产品推荐
相关产品推荐

