Files
AI-Health/health_app/lib/widgets/app_future_view.dart
MingNian 0d4fd88ce7 feat: 三端抽屉重构 + 配色系统更新 + 后台管理页精调 + 键盘抬起组件
## 抽屉重构
- admin_drawer / doctor_drawer / health_drawer 三端抽屉全部重做
- drawer_shell 增强

## 配色系统
- app_colors / app_module_visuals / app_theme 更新
- app.dart 启动流程调整

## 后台管理页
- admin_doctors_page 大幅重构(+251)
- admin_home / doctor_dashboard / doctor_reports / doctor_home / doctor_followups / doctor_settings 精调

## 患者端
- home_page / chat_messages_view / medication_list / notification_center / remaining_pages / exercise_plan / device / diet / consultation 微调

## 新增
- keyboard_lift.dart: 键盘抬起处理组件
- AGENTS.md: agent 指引文档

## 其他
- api_client IP 适配
- app_future_view 增强
- data_providers 调整
- secondary_page_visuals_test 更新
2026-07-19 19:11:30 +08:00

85 lines
2.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/app_colors.dart';
import 'app_error_state.dart';
/// 基于 FutureBuilder 的四态封装:统一处理"加载中 / 失败"
/// "空数据 / 有数据"交由 onData 自行判断(不同页面空态文案不同)。
///
/// onData 拿到非空数据后,自行判断 isEmpty 决定显示空态还是列表。
class AppFutureView<T> extends StatelessWidget {
final Future<T>? future;
final Widget Function(BuildContext context, T data) onData;
final VoidCallback? onRetry;
final Widget? loading;
final String? errorTitle;
const AppFutureView({
super.key,
required this.future,
required this.onData,
this.onRetry,
this.loading,
this.errorTitle,
});
@override
Widget build(BuildContext context) {
return FutureBuilder<T>(
future: future,
builder: (ctx, snap) {
// 失败优先于其它判断,避免把错误显示成"暂无数据"
if (snap.hasError) {
return AppErrorState(title: errorTitle ?? '加载失败', onRetry: onRetry);
}
// 首次加载、尚无数据 → 转圈
if (snap.connectionState == ConnectionState.waiting && !snap.hasData) {
return loading ??
const Center(
child: CircularProgressIndicator(color: AppColors.primary),
);
}
if (!snap.hasData) {
return loading ??
const Center(
child: CircularProgressIndicator(color: AppColors.primary),
);
}
return onData(ctx, snap.data as T);
},
);
}
}
/// Riverpod 异步数据视图。Provider 会保留最近一次成功数据,刷新时不清空页面。
class AppAsyncValueView<T> extends StatelessWidget {
final AsyncValue<T> value;
final Widget Function(BuildContext context, T data) onData;
final VoidCallback? onRetry;
final Widget? loading;
final String? errorTitle;
const AppAsyncValueView({
super.key,
required this.value,
required this.onData,
this.onRetry,
this.loading,
this.errorTitle,
});
@override
Widget build(BuildContext context) => value.when(
skipLoadingOnRefresh: true,
skipLoadingOnReload: true,
data: (data) => onData(context, data),
error: (_, _) =>
AppErrorState(title: errorTitle ?? '加载失败', onRetry: onRetry),
loading: () =>
loading ??
const Center(
child: CircularProgressIndicator(color: AppColors.primary),
),
);
}