修复用户端订单列表生命周期与刷新

This commit is contained in:
czl231
2026-09-05 20:49:50 +08:00
parent cec02c8887
commit 1dd21e6df1
8 changed files with 378 additions and 43 deletions

View File

@@ -8,45 +8,66 @@ class RecordListPage extends StatefulWidget {
const RecordListPage({
required this.title,
required this.eyebrow,
required this.viewModel,
required this.sourceKey,
required this.loader,
this.description,
this.floatingActionButton,
this.onRecordTap,
this.embedded = false,
this.refreshToken = 0,
super.key,
});
final String title;
final String eyebrow;
final String? description;
final RecordListViewModel viewModel;
final String sourceKey;
final RecordLoader loader;
final Widget? floatingActionButton;
final ValueChanged<ClientRecord>? onRecordTap;
final bool embedded;
final int refreshToken;
@override
State<RecordListPage> createState() => _RecordListPageState();
}
class _RecordListPageState extends State<RecordListPage> {
class _RecordListPageState extends State<RecordListPage>
with AutomaticKeepAliveClientMixin<RecordListPage> {
late RecordListViewModel _viewModel;
@override
void initState() {
super.initState();
widget.viewModel.load();
_viewModel = RecordListViewModel(widget.loader)..load();
}
@override
void didUpdateWidget(covariant RecordListPage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.sourceKey != oldWidget.sourceKey) {
_viewModel.dispose();
_viewModel = RecordListViewModel(widget.loader)..load();
return;
}
if (widget.refreshToken != oldWidget.refreshToken) {
_viewModel.load();
}
}
@override
void dispose() {
widget.viewModel.dispose();
_viewModel.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
final content = ListenableBuilder(
listenable: widget.viewModel,
listenable: _viewModel,
builder: (context, _) {
final state = widget.viewModel;
final state = _viewModel;
if (state.loading && state.records.isEmpty) {
return const Center(child: CircularProgressIndicator());
}
@@ -123,4 +144,7 @@ class _RecordListPageState extends State<RecordListPage> {
body: content,
);
}
@override
bool get wantKeepAlive => true;
}

View File

@@ -14,23 +14,46 @@ class RecordListViewModel extends ChangeNotifier {
List<ClientRecord> _records = const [];
Object? _error;
bool _loading = false;
bool _disposed = false;
int _requestGeneration = 0;
List<ClientRecord> get records => List.unmodifiable(_records);
Object? get error => _error;
bool get loading => _loading;
Future<void> load() async {
final generation = ++_requestGeneration;
_loading = true;
_error = null;
notifyListeners();
_notifyIfActive();
try {
_records = await _loader();
final records = await _loader();
if (!_isCurrent(generation)) return;
_records = records;
} catch (error) {
if (!_isCurrent(generation)) return;
// 会话失效由统一路由接管,不在列表中重复展示网络错误。
if (error is! SessionExpiredException) _error = error;
} finally {
_loading = false;
notifyListeners();
if (_isCurrent(generation)) {
_loading = false;
_notifyIfActive();
}
}
}
/// 仅允许最后一次请求且组件仍存活时写入状态。
bool _isCurrent(int generation) => !_disposed && generation == _requestGeneration;
/// 避免异步请求在页面销毁后继续通知监听器。
void _notifyIfActive() {
if (!_disposed) notifyListeners();
}
@override
void dispose() {
_disposed = true;
_requestGeneration += 1;
super.dispose();
}
}