feat: optimize record list

This commit is contained in:
Nedifinita
2025-09-25 21:45:52 +08:00
parent ba373f749a
commit 56689fc993
3 changed files with 40 additions and 33 deletions

View File

@@ -3,6 +3,7 @@ import 'dart:isolate';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:scrollview_observer/scrollview_observer.dart';
import '../models/merged_record.dart'; import '../models/merged_record.dart';
import '../services/database_service.dart'; import '../services/database_service.dart';
import '../models/train_record.dart'; import '../models/train_record.dart';
@@ -31,6 +32,9 @@ class HistoryScreenState extends State<HistoryScreen> {
final Set<String> _selectedRecords = {}; final Set<String> _selectedRecords = {};
final Map<String, bool> _expandedStates = {}; final Map<String, bool> _expandedStates = {};
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
final ListObserverController _observerController =
ListObserverController(controller: null)..cacheJumpIndexOffset = false;
late final ChatScrollObserver _chatObserver;
bool _isAtTop = true; bool _isAtTop = true;
MergeSettings _mergeSettings = MergeSettings(); MergeSettings _mergeSettings = MergeSettings();
double _itemHeightCache = 0.0; double _itemHeightCache = 0.0;
@@ -56,6 +60,10 @@ class HistoryScreenState extends State<HistoryScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_chatObserver = ChatScrollObserver(_observerController)
..toRebuildScrollViewCallback = () {
setState(() {});
};
_scrollController.addListener(() { _scrollController.addListener(() {
if (_scrollController.position.atEdge) { if (_scrollController.position.atEdge) {
if (_scrollController.position.pixels == 0) { if (_scrollController.position.pixels == 0) {
@@ -73,6 +81,7 @@ class HistoryScreenState extends State<HistoryScreen> {
@override @override
void dispose() { void dispose() {
_scrollController.dispose(); _scrollController.dispose();
_observerController.controller?.dispose();
super.dispose(); super.dispose();
} }
@@ -123,44 +132,26 @@ class HistoryScreenState extends State<HistoryScreen> {
if (!isNewRecord) return; if (!isNewRecord) return;
final allRecords = await DatabaseService.instance.getAllRecords();
final items = MergeService.getMixedList(allRecords, _mergeSettings);
if (mounted) { if (mounted) {
final previousScrollOffset = _scrollController.hasClients ? _scrollController.offset : 0.0; if (!_isAtTop) {
final previousItemCount = _displayItems.length; _chatObserver.standby();
}
final allRecords = await DatabaseService.instance.getAllRecords();
final items = MergeService.getMixedList(allRecords, _mergeSettings);
setState(() { setState(() {
_displayItems.clear(); _displayItems.clear();
_displayItems.addAll(items); _displayItems.addAll(items);
}); });
if (_scrollController.hasClients) { if (_isAtTop && _scrollController.hasClients) {
if (_isAtTop) { _scrollController.jumpTo(0.0);
_scrollController.jumpTo(0.0);
} else {
final newItemCount = items.length;
final itemDifference = newItemCount - previousItemCount;
if (itemDifference > 0 && previousScrollOffset > 0) {
final itemHeight = _getEstimatedItemHeight();
final adjustedOffset = previousScrollOffset + (itemDifference * itemHeight);
_scrollController.jumpTo(adjustedOffset.clamp(0.0, _scrollController.position.maxScrollExtent));
}
}
} }
} }
} catch (e) {} } catch (e) {}
} }
double _getEstimatedItemHeight() {
if (_itemHeightCache > 0) {
return _itemHeightCache;
}
return 85.0;
}
bool _hasDataChanged(List<Object> newItems) { bool _hasDataChanged(List<Object> newItems) {
if (_displayItems.length != newItems.length) return true; if (_displayItems.length != newItems.length) return true;
@@ -194,8 +185,12 @@ class HistoryScreenState extends State<HistoryScreen> {
Text('暂无记录', style: TextStyle(color: Colors.white, fontSize: 18)) Text('暂无记录', style: TextStyle(color: Colors.white, fontSize: 18))
])); ]));
} }
return ListView.builder( return ListViewObserver(
controller: _observerController,
child: ListView.builder(
controller: _scrollController, controller: _scrollController,
physics: ChatObserverClampingScrollPhysics(observer: _chatObserver),
shrinkWrap: _chatObserver.isShrinkWrap,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
itemCount: _displayItems.length, itemCount: _displayItems.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -206,7 +201,9 @@ class HistoryScreenState extends State<HistoryScreen> {
return _buildRecordCard(item); return _buildRecordCard(item);
} }
return const SizedBox.shrink(); return const SizedBox.shrink();
}); },
),
);
} }
Widget _buildMergedRecordCard(MergedTrainRecord mergedRecord) { Widget _buildMergedRecordCard(MergedTrainRecord mergedRecord) {
@@ -510,9 +507,9 @@ class HistoryScreenState extends State<HistoryScreen> {
final isSelected = _selectedRecords.contains(record.uniqueId); final isSelected = _selectedRecords.contains(record.uniqueId);
final isExpanded = final isExpanded =
!isSubCard && (_expandedStates[record.uniqueId] ?? false); !isSubCard && (_expandedStates[record.uniqueId] ?? false);
final GlobalKey itemKey = GlobalKey(); final GlobalKey itemKey = GlobalKey();
final Widget card = Card( final Widget card = Card(
key: itemKey, key: itemKey,
color: isSelected && _isEditMode color: isSelected && _isEditMode
@@ -570,10 +567,11 @@ class HistoryScreenState extends State<HistoryScreen> {
_buildLocoInfo(record), _buildLocoInfo(record),
if (isExpanded) _buildExpandedContent(record) if (isExpanded) _buildExpandedContent(record)
])))); ]))));
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (_itemHeightCache <= 0 && itemKey.currentContext != null) { if (_itemHeightCache <= 0 && itemKey.currentContext != null) {
final RenderBox renderBox = itemKey.currentContext!.findRenderObject() as RenderBox; final RenderBox renderBox =
itemKey.currentContext!.findRenderObject() as RenderBox;
final double realHeight = renderBox.size.height; final double realHeight = renderBox.size.height;
if (realHeight > 0) { if (realHeight > 0) {
setState(() { setState(() {
@@ -582,7 +580,7 @@ class HistoryScreenState extends State<HistoryScreen> {
} }
} }
}); });
return card; return card;
} }

View File

@@ -920,6 +920,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.28.0" version: "0.28.0"
scrollview_observer:
dependency: "direct main"
description:
name: scrollview_observer
sha256: c2f713509f18f88f637b2084b47a90c91fb1ef066d5d82d2cf3194d8509dc6ab
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.26.2"
share_plus: share_plus:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -53,6 +53,7 @@ dependencies:
package_info_plus: ^8.1.2 package_info_plus: ^8.1.2
msix: ^3.16.12 msix: ^3.16.12
flutter_background_service: ^5.1.0 flutter_background_service: ^5.1.0
scrollview_observer: ^1.20.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: