feat(app): 发现页动态化 — DiscoverBloc + API 驱动 + 下拉刷新
- 新增 DiscoverBloc (LoadData/Refresh) + DiscoverModels 4 个数据类 - DiscoverPage 改为 BlocBuilder 驱动: loading/loaded/error/empty 四态 - 替换全部硬编码占位数据为 API 实时数据 - 添加 RefreshIndicator 下拉刷新 - 离线异常时保留已有数据,友好错误提示
This commit is contained in:
78
app/lib/features/discover/bloc/discover_bloc.dart
Normal file
78
app/lib/features/discover/bloc/discover_bloc.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
// 发现页 BLoC — 管理发现页数据加载状态
|
||||
//
|
||||
// 职责:调用 /diary/discover API,解析响应,管理加载/成功/失败状态。
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../data/remote/api_client.dart';
|
||||
import '../models/discover_models.dart';
|
||||
|
||||
part 'discover_event.dart';
|
||||
part 'discover_state.dart';
|
||||
|
||||
class DiscoverBloc extends Bloc<DiscoverEvent, DiscoverState> {
|
||||
final ApiClient _api;
|
||||
|
||||
DiscoverBloc({required ApiClient api})
|
||||
: _api = api,
|
||||
super(const DiscoverInitial()) {
|
||||
on<DiscoverLoadData>(_onLoadData);
|
||||
on<DiscoverRefresh>(_onRefresh);
|
||||
}
|
||||
|
||||
/// 首次加载 — 显示 loading 状态
|
||||
Future<void> _onLoadData(
|
||||
DiscoverLoadData event,
|
||||
Emitter<DiscoverState> emit,
|
||||
) async {
|
||||
emit(const DiscoverLoading());
|
||||
await _fetchData(emit);
|
||||
}
|
||||
|
||||
/// 刷新 — 不显示 loading,静默更新
|
||||
Future<void> _onRefresh(
|
||||
DiscoverRefresh event,
|
||||
Emitter<DiscoverState> emit,
|
||||
) async {
|
||||
await _fetchData(emit);
|
||||
}
|
||||
|
||||
/// 通用数据获取逻辑
|
||||
Future<void> _fetchData(Emitter<DiscoverState> emit) async {
|
||||
try {
|
||||
final response = await _api.get('/diary/discover');
|
||||
final body = response.data as Map<String, dynamic>;
|
||||
|
||||
// 后端信封格式: { success, data: { ... }, message }
|
||||
final dataJson = body['data'] as Map<String, dynamic>? ?? {};
|
||||
final discoverData = DiscoverData.fromJson(dataJson);
|
||||
|
||||
emit(DiscoverLoaded(discoverData));
|
||||
} on OfflineException {
|
||||
// 离线时,如果有已加载的数据,保留它
|
||||
if (state is DiscoverLoaded) return;
|
||||
emit(const DiscoverError('网络不可用,请检查网络连接'));
|
||||
} catch (e) {
|
||||
if (state is DiscoverLoaded) return;
|
||||
emit(DiscoverError('加载失败:${_friendlyError(e)}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// 将异常转换为用户友好的错误消息
|
||||
String _friendlyError(Object error) {
|
||||
final msg = error.toString();
|
||||
if (msg.contains('SocketException') || msg.contains('Connection refused')) {
|
||||
return '无法连接服务器';
|
||||
}
|
||||
if (msg.contains('401')) {
|
||||
return '登录已过期,请重新登录';
|
||||
}
|
||||
if (msg.contains('403')) {
|
||||
return '没有访问权限';
|
||||
}
|
||||
if (msg.contains('500')) {
|
||||
return '服务器错误,请稍后重试';
|
||||
}
|
||||
return '请稍后重试';
|
||||
}
|
||||
}
|
||||
16
app/lib/features/discover/bloc/discover_event.dart
Normal file
16
app/lib/features/discover/bloc/discover_event.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
part of 'discover_bloc.dart';
|
||||
|
||||
/// 发现页事件
|
||||
sealed class DiscoverEvent {
|
||||
const DiscoverEvent();
|
||||
}
|
||||
|
||||
/// 加载发现页数据(首次进入或重新进入页面)
|
||||
final class DiscoverLoadData extends DiscoverEvent {
|
||||
const DiscoverLoadData();
|
||||
}
|
||||
|
||||
/// 下拉刷新(不显示全屏 loading,避免闪烁)
|
||||
final class DiscoverRefresh extends DiscoverEvent {
|
||||
const DiscoverRefresh();
|
||||
}
|
||||
30
app/lib/features/discover/bloc/discover_state.dart
Normal file
30
app/lib/features/discover/bloc/discover_state.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
part of 'discover_bloc.dart';
|
||||
|
||||
/// 发现页状态
|
||||
sealed class DiscoverState {
|
||||
const DiscoverState();
|
||||
}
|
||||
|
||||
/// 初始状态
|
||||
final class DiscoverInitial extends DiscoverState {
|
||||
const DiscoverInitial();
|
||||
}
|
||||
|
||||
/// 加载中
|
||||
final class DiscoverLoading extends DiscoverState {
|
||||
const DiscoverLoading();
|
||||
}
|
||||
|
||||
/// 加载成功
|
||||
final class DiscoverLoaded extends DiscoverState {
|
||||
final DiscoverData data;
|
||||
|
||||
const DiscoverLoaded(this.data);
|
||||
}
|
||||
|
||||
/// 加载失败
|
||||
final class DiscoverError extends DiscoverState {
|
||||
final String message;
|
||||
|
||||
const DiscoverError(this.message);
|
||||
}
|
||||
Reference in New Issue
Block a user