feat: optimize record list
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user