fix: solve the problem that errors may occur during merging
This commit is contained in:
@@ -221,28 +221,43 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
if (_isAtTop) {
|
if (_isAtTop) {
|
||||||
setState(() {
|
setState(() {
|
||||||
bool isMerge = false;
|
List<TrainRecord> allRecords = [];
|
||||||
Object? mergeResult;
|
Set<String> selectedRecordIds = {};
|
||||||
if (_displayItems.isNotEmpty) {
|
|
||||||
final firstItem = _displayItems.first;
|
for (final item in _displayItems) {
|
||||||
List<TrainRecord> tempRecords = [newRecord];
|
if (item is MergedTrainRecord) {
|
||||||
if (firstItem is MergedTrainRecord) {
|
allRecords.addAll(item.records);
|
||||||
tempRecords.addAll(firstItem.records);
|
if (_selectedRecords.contains(item.records.first.uniqueId)) {
|
||||||
} else if (firstItem is TrainRecord) {
|
selectedRecordIds.addAll(item.records.map((r) => r.uniqueId));
|
||||||
tempRecords.add(firstItem);
|
}
|
||||||
}
|
} else if (item is TrainRecord) {
|
||||||
final mergeCheckResult =
|
allRecords.add(item);
|
||||||
MergeService.getMixedList(tempRecords, _mergeSettings);
|
if (_selectedRecords.contains(item.uniqueId)) {
|
||||||
if (mergeCheckResult.length == 1 &&
|
selectedRecordIds.add(item.uniqueId);
|
||||||
mergeCheckResult.first is MergedTrainRecord) {
|
}
|
||||||
isMerge = true;
|
|
||||||
mergeResult = mergeCheckResult.first;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isMerge) {
|
|
||||||
_displayItems[0] = mergeResult!;
|
allRecords.insert(0, newRecord);
|
||||||
} else {
|
|
||||||
_displayItems.insert(0, newRecord);
|
final mergedItems =
|
||||||
|
MergeService.getMixedList(allRecords, _mergeSettings);
|
||||||
|
|
||||||
|
_displayItems.clear();
|
||||||
|
_displayItems.addAll(mergedItems);
|
||||||
|
|
||||||
|
_selectedRecords.clear();
|
||||||
|
for (final item in _displayItems) {
|
||||||
|
if (item is MergedTrainRecord) {
|
||||||
|
if (item.records
|
||||||
|
.any((r) => selectedRecordIds.contains(r.uniqueId))) {
|
||||||
|
_selectedRecords.addAll(item.records.map((r) => r.uniqueId));
|
||||||
|
}
|
||||||
|
} else if (item is TrainRecord) {
|
||||||
|
if (selectedRecordIds.contains(item.uniqueId)) {
|
||||||
|
_selectedRecords.add(item.uniqueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (_scrollController.hasClients) {
|
if (_scrollController.hasClients) {
|
||||||
@@ -723,8 +738,6 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
Widget _buildRecordCard(TrainRecord record,
|
Widget _buildRecordCard(TrainRecord record,
|
||||||
{bool isSubCard = false, Key? key}) {
|
{bool isSubCard = false, Key? key}) {
|
||||||
final isSelected = _selectedRecords.contains(record.uniqueId);
|
final isSelected = _selectedRecords.contains(record.uniqueId);
|
||||||
final isExpanded =
|
|
||||||
!isSubCard && (_expandedStates[record.uniqueId] ?? false);
|
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
key: key,
|
key: key,
|
||||||
@@ -752,6 +765,11 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
}
|
}
|
||||||
widget.onSelectionChanged();
|
widget.onSelectionChanged();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_expandedStates[record.uniqueId] =
|
||||||
|
!(_expandedStates[record.uniqueId] ?? false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
@@ -771,7 +789,8 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
_buildRecordHeader(record),
|
_buildRecordHeader(record),
|
||||||
_buildPositionAndSpeed(record),
|
_buildPositionAndSpeed(record),
|
||||||
_buildLocoInfo(record),
|
_buildLocoInfo(record),
|
||||||
if (isExpanded) _buildExpandedContent(record),
|
if (_expandedStates[record.uniqueId] ?? false)
|
||||||
|
_buildExpandedContent(record),
|
||||||
]))));
|
]))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,6 +822,7 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
final hasLocoInfo =
|
final hasLocoInfo =
|
||||||
formattedLocoInfo.isNotEmpty && formattedLocoInfo != "<NUL>";
|
formattedLocoInfo.isNotEmpty && formattedLocoInfo != "<NUL>";
|
||||||
final shouldShowTrainRow = hasTrainNumber || hasDirection || hasLocoInfo;
|
final shouldShowTrainRow = hasTrainNumber || hasDirection || hasLocoInfo;
|
||||||
|
final hasPosition = _parsePosition(record.positionInfo) != null;
|
||||||
|
|
||||||
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||||
@@ -856,7 +876,8 @@ class HistoryScreenState extends State<HistoryScreen> {
|
|||||||
])),
|
])),
|
||||||
if (hasLocoInfo)
|
if (hasLocoInfo)
|
||||||
Text(formattedLocoInfo,
|
Text(formattedLocoInfo,
|
||||||
style: const TextStyle(fontSize: 14, color: Colors.white70))
|
style:
|
||||||
|
const TextStyle(fontSize: 14, color: Colors.white70)),
|
||||||
]),
|
]),
|
||||||
const SizedBox(height: 2)
|
const SizedBox(height: 2)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_selectedGroupKeys.remove(groupKey);
|
_selectedGroupKeys.remove(groupKey);
|
||||||
print('记录不存在,移除选中状态: $groupKey');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +226,6 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_selectedGroupKeys.remove(groupKey);
|
_selectedGroupKeys.remove(groupKey);
|
||||||
print('记录不存在,移除选中状态: $groupKey');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +273,6 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_selectedGroupKeys.remove(groupKey);
|
_selectedGroupKeys.remove(groupKey);
|
||||||
print('记录不存在,移除选中状态: $groupKey');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,25 +457,19 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_scrollController.addListener(() {
|
_scrollController.addListener(() {
|
||||||
print(
|
|
||||||
'滚动监听器触发 - 当前位置: ${_scrollController.position.pixels}, maxScrollExtent: ${_scrollController.position.maxScrollExtent}, isAtTop: $_isAtTop');
|
|
||||||
|
|
||||||
if (_scrollController.position.atEdge) {
|
if (_scrollController.position.atEdge) {
|
||||||
if (_scrollController.position.pixels ==
|
if (_scrollController.position.pixels ==
|
||||||
_scrollController.position.maxScrollExtent) {
|
_scrollController.position.maxScrollExtent) {
|
||||||
print('到达底部(反转后的"顶部")- 设置 _isAtTop = true');
|
|
||||||
if (!_isAtTop) {
|
if (!_isAtTop) {
|
||||||
setState(() => _isAtTop = true);
|
setState(() => _isAtTop = true);
|
||||||
}
|
}
|
||||||
} else if (_scrollController.position.pixels == 0) {
|
} else if (_scrollController.position.pixels == 0) {
|
||||||
print('到达顶部(反转后的"底部")- 设置 _isAtTop = false');
|
|
||||||
if (_isAtTop) {
|
if (_isAtTop) {
|
||||||
setState(() => _isAtTop = false);
|
setState(() => _isAtTop = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_isAtTop) {
|
if (_isAtTop) {
|
||||||
print('离开底部(反转后的"顶部")- 设置 _isAtTop = false');
|
|
||||||
setState(() => _isAtTop = false);
|
setState(() => _isAtTop = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,16 +492,12 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
if (mounted && _scrollController.hasClients && _displayItems.isNotEmpty) {
|
if (mounted && _scrollController.hasClients && _displayItems.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final maxScrollExtent = _scrollController.position.maxScrollExtent;
|
final maxScrollExtent = _scrollController.position.maxScrollExtent;
|
||||||
print('初始滚动执行:maxScrollExtent=$maxScrollExtent');
|
|
||||||
_scrollController.jumpTo(maxScrollExtent);
|
_scrollController.jumpTo(maxScrollExtent);
|
||||||
print('初始滚动完成:位置=${_scrollController.position.pixels}');
|
|
||||||
|
|
||||||
if (!_isAtTop) {
|
if (!_isAtTop) {
|
||||||
setState(() => _isAtTop = true);
|
setState(() => _isAtTop = true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
print('初始滚动错误:$e');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -675,13 +662,8 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
try {
|
try {
|
||||||
final maxScrollExtent =
|
final maxScrollExtent =
|
||||||
_scrollController.position.maxScrollExtent;
|
_scrollController.position.maxScrollExtent;
|
||||||
print('loadRecords - 滚动到底部, maxScrollExtent: $maxScrollExtent');
|
|
||||||
_scrollController.jumpTo(maxScrollExtent);
|
_scrollController.jumpTo(maxScrollExtent);
|
||||||
print(
|
} catch (e) {}
|
||||||
'loadRecords - 滚动完成,新位置: ${_scrollController.position.pixels}');
|
|
||||||
} catch (e) {
|
|
||||||
print('loadRecords - 滚动错误: $e');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_isLoading) {
|
if (_isLoading) {
|
||||||
@@ -697,11 +679,9 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addNewRecord(TrainRecord newRecord) async {
|
Future<void> addNewRecord(TrainRecord newRecord) async {
|
||||||
print('addNewRecord - 开始添加新记录, 当前_isAtTop=$_isAtTop');
|
|
||||||
try {
|
try {
|
||||||
final position = _parsePositionFromRecord(newRecord);
|
final position = _parsePositionFromRecord(newRecord);
|
||||||
if (position == null) {
|
if (position == null) {
|
||||||
print('addNewRecord - 记录没有位置信息,忽略');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,41 +701,44 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
if (!isNewRecord) return;
|
if (!isNewRecord) return;
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
List<TrainRecord> allRecords = [];
|
||||||
bool isMerge = false;
|
Set<String> selectedRecordIds = {};
|
||||||
Object? mergeResult;
|
|
||||||
String? oldSingleRecordKey;
|
|
||||||
|
|
||||||
if (_displayItems.isNotEmpty) {
|
for (final item in _displayItems) {
|
||||||
final firstItem = _displayItems.first;
|
if (item is MergedTrainRecord) {
|
||||||
List<TrainRecord> tempRecords = [newRecord];
|
allRecords.addAll(item.records);
|
||||||
if (firstItem is MergedTrainRecord) {
|
if (_selectedGroupKeys.contains(item.groupKey)) {
|
||||||
tempRecords.addAll(firstItem.records);
|
selectedRecordIds.addAll(item.records.map((r) => r.uniqueId));
|
||||||
} else if (firstItem is TrainRecord) {
|
|
||||||
tempRecords.add(firstItem);
|
|
||||||
|
|
||||||
oldSingleRecordKey = "single:${firstItem.uniqueId}";
|
|
||||||
}
|
}
|
||||||
final mergeCheckResult =
|
} else if (item is TrainRecord) {
|
||||||
MergeService.getMixedList(tempRecords, _mergeSettings);
|
allRecords.add(item);
|
||||||
if (mergeCheckResult.length == 1 &&
|
if (_selectedGroupKeys.contains("single:${item.uniqueId}")) {
|
||||||
mergeCheckResult.first is MergedTrainRecord) {
|
selectedRecordIds.add(item.uniqueId);
|
||||||
isMerge = true;
|
|
||||||
mergeResult = mergeCheckResult.first;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isMerge) {
|
allRecords.insert(0, newRecord);
|
||||||
final mergedRecord = mergeResult as MergedTrainRecord;
|
|
||||||
_displayItems[0] = mergedRecord;
|
|
||||||
|
|
||||||
if (oldSingleRecordKey != null &&
|
final mergedItems =
|
||||||
_selectedGroupKeys.contains(oldSingleRecordKey)) {
|
MergeService.getMixedList(allRecords, _mergeSettings);
|
||||||
_selectedGroupKeys.remove(oldSingleRecordKey);
|
|
||||||
_selectedGroupKeys.add(mergedRecord.groupKey);
|
setState(() {
|
||||||
|
_displayItems.clear();
|
||||||
|
_displayItems.addAll(mergedItems);
|
||||||
|
|
||||||
|
_selectedGroupKeys.clear();
|
||||||
|
for (final item in _displayItems) {
|
||||||
|
if (item is MergedTrainRecord) {
|
||||||
|
if (item.records
|
||||||
|
.any((r) => selectedRecordIds.contains(r.uniqueId))) {
|
||||||
|
_selectedGroupKeys.add(item.groupKey);
|
||||||
|
}
|
||||||
|
} else if (item is TrainRecord) {
|
||||||
|
if (selectedRecordIds.contains(item.uniqueId)) {
|
||||||
|
_selectedGroupKeys.add("single:${item.uniqueId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_displayItems.insert(0, newRecord);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -765,23 +748,16 @@ class RealtimeScreenState extends State<RealtimeScreen> {
|
|||||||
_adjustMapViewToSelectedGroups();
|
_adjustMapViewToSelectedGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
print(
|
|
||||||
'addNewRecord - 检查滚动条件: _isAtTop=$_isAtTop, hasClients=${_scrollController.hasClients}, 当前位置: ${_scrollController.position.pixels}');
|
|
||||||
if (_isAtTop && _scrollController.hasClients) {
|
if (_isAtTop && _scrollController.hasClients) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted && _scrollController.hasClients) {
|
if (mounted && _scrollController.hasClients) {
|
||||||
final newMaxScrollExtent =
|
final newMaxScrollExtent =
|
||||||
_scrollController.position.maxScrollExtent;
|
_scrollController.position.maxScrollExtent;
|
||||||
print(
|
|
||||||
'addNewRecord - 执行滚动到底部, maxScrollExtent: $newMaxScrollExtent');
|
|
||||||
_scrollController.jumpTo(newMaxScrollExtent);
|
_scrollController.jumpTo(newMaxScrollExtent);
|
||||||
print(
|
|
||||||
'addNewRecord - 滚动完成,新位置: ${_scrollController.position.pixels}');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {}
|
||||||
print('addNewRecord - 不执行滚动,条件不满足');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.6.0-flutter+60
|
version: 0.6.1-flutter+61
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
|||||||
Reference in New Issue
Block a user