105 lines
3.0 KiB
Dart
105 lines
3.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
|
|
import '../network/api_error_message.dart';
|
|
import 'app_state_card.dart';
|
|
|
|
/// Shared loading, failure and empty-state contract for feature pages.
|
|
class AsyncStateView<T> extends StatelessWidget {
|
|
const AsyncStateView({
|
|
super.key,
|
|
required this.state,
|
|
required this.data,
|
|
this.isEmpty,
|
|
this.empty,
|
|
this.loading,
|
|
this.onRetry,
|
|
this.errorTitle = '加载失败',
|
|
this.errorSubtitle = '请检查网络后重试',
|
|
this.emptyIcon = Icons.inbox_outlined,
|
|
this.emptyTitle = '暂无内容',
|
|
this.emptySubtitle = '暂时没有可展示的数据',
|
|
});
|
|
|
|
final AsyncValue<T> state;
|
|
final Widget Function(T value) data;
|
|
final bool Function(T value)? isEmpty;
|
|
final Widget Function()? empty;
|
|
final Widget? loading;
|
|
final VoidCallback? onRetry;
|
|
final String errorTitle;
|
|
final String errorSubtitle;
|
|
final IconData emptyIcon;
|
|
final String emptyTitle;
|
|
final String emptySubtitle;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return state.when(
|
|
loading: () =>
|
|
loading ?? const Center(child: CircularProgressIndicator()),
|
|
error: (error, _) => Center(
|
|
child: AppStateCard(
|
|
icon: Icons.error_outline,
|
|
title: errorTitle,
|
|
subtitle: ErrorMessageMapper.message(error, fallback: errorSubtitle),
|
|
buttonLabel: onRetry == null ? null : '重试',
|
|
onButtonPressed: onRetry,
|
|
buttonStyle: AppStateButtonStyle.filled,
|
|
),
|
|
),
|
|
data: (value) => isEmpty?.call(value) == true
|
|
? empty?.call() ??
|
|
Center(
|
|
child: AppStateCard(
|
|
icon: emptyIcon,
|
|
title: emptyTitle,
|
|
subtitle: emptySubtitle,
|
|
),
|
|
)
|
|
: data(value),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Sliver counterpart for CustomScrollView pages without forcing one global
|
|
/// card layout on successful feature content.
|
|
class SliverAsyncState<T> extends StatelessWidget {
|
|
const SliverAsyncState({
|
|
super.key,
|
|
required this.state,
|
|
required this.data,
|
|
this.loading,
|
|
this.onRetry,
|
|
this.errorSubtitle = '请检查网络后重试',
|
|
});
|
|
|
|
final AsyncValue<T> state;
|
|
final Widget Function(T value) data;
|
|
final Widget? loading;
|
|
final VoidCallback? onRetry;
|
|
final String errorSubtitle;
|
|
|
|
@override
|
|
Widget build(BuildContext context) => state.when(
|
|
data: data,
|
|
loading: () => SliverFillRemaining(
|
|
hasScrollBody: false,
|
|
child: loading ?? const Center(child: CircularProgressIndicator()),
|
|
),
|
|
error: (error, _) => SliverFillRemaining(
|
|
hasScrollBody: false,
|
|
child: Center(
|
|
child: AppStateCard(
|
|
icon: Icons.error_outline,
|
|
title: '加载失败',
|
|
subtitle: ErrorMessageMapper.message(error, fallback: errorSubtitle),
|
|
buttonLabel: onRetry == null ? null : '重试',
|
|
onButtonPressed: onRetry,
|
|
buttonStyle: AppStateButtonStyle.filled,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|